From 62498f7eef427c457e8bae22713766645ea8363a Mon Sep 17 00:00:00 2001 From: ale8k Date: Mon, 10 Nov 2025 21:11:25 +0000 Subject: [PATCH] feat: wip model creation failing due to incorrect controller model type (why?) --- cloud/clouds.go | 2 + cmd/jujud-controller/agent/bootstrap.go | 2 +- controllerss.yaml | 162 +++++++++ domain/model/service/service.go | 2 +- domain/schema/controller/sql/0005-cloud.sql | 3 +- go.mod | 11 + go.sum | 24 +- inspect0473 | 264 +++++++++++++++ internal/provider/all/docker.go | 11 + internal/provider/docker/broker.go | 358 ++++++++++++++++++++ internal/provider/docker/client.go | 246 ++++++++++++++ internal/provider/docker/environ.go | 7 + internal/provider/docker/provider.go | 144 ++++++++ modeloperatordeployment.yaml | 56 +++ 14 files changed, 1277 insertions(+), 15 deletions(-) create mode 100644 controllerss.yaml create mode 100644 inspect0473 create mode 100644 internal/provider/all/docker.go create mode 100644 internal/provider/docker/broker.go create mode 100644 internal/provider/docker/client.go create mode 100644 internal/provider/docker/environ.go create mode 100644 internal/provider/docker/provider.go create mode 100644 modeloperatordeployment.yaml diff --git a/cloud/clouds.go b/cloud/clouds.go index 85f824ff919f..17bc489a709f 100644 --- a/cloud/clouds.go +++ b/cloud/clouds.go @@ -304,6 +304,7 @@ type region struct { const ( // CloudTypeKubernetes is the kubernetes cloud type. CloudTypeKubernetes = "kubernetes" + CloudTypeDocker = "docker" ) // DefaultCloudRegion is the name of the default region that Juju creates for clouds that do not define a region. @@ -311,6 +312,7 @@ const DefaultCloudRegion = "default" var caasCloudTypes = map[string]bool{ CloudTypeKubernetes: true, + CloudTypeDocker: true, } // CloudIsCAAS checks if cloud is a CAAS cloud. diff --git a/cmd/jujud-controller/agent/bootstrap.go b/cmd/jujud-controller/agent/bootstrap.go index a357e0a67239..17707bdc92d7 100644 --- a/cmd/jujud-controller/agent/bootstrap.go +++ b/cmd/jujud-controller/agent/bootstrap.go @@ -176,7 +176,7 @@ func (c *BootstrapCommand) Run(ctx *cmd.Context) error { } } - isCAAS := args.ControllerCloud.Type == cloud.CloudTypeKubernetes + isCAAS := args.ControllerCloud.Type == cloud.CloudTypeKubernetes || args.ControllerCloud.Type == cloud.CloudTypeDocker if isCAAS { if err := c.ensureConfigFilesForCaas(); err != nil { diff --git a/controllerss.yaml b/controllerss.yaml new file mode 100644 index 000000000000..ba59bab328cf --- /dev/null +++ b/controllerss.yaml @@ -0,0 +1,162 @@ +Name: controller +Namespace: controller-40 +CreationTimestamp: Mon, 10 Nov 2025 09:53:30 +0000 +Selector: app.kubernetes.io/name=controller +Labels: app.kubernetes.io/managed-by=juju + app.kubernetes.io/name=controller + model.juju.is/disable-webhook=true +Annotations: controller.juju.is/id: 1165bdc0-b9d0-477e-8eac-ea9f3f77e850 +Replicas: 1 desired | 1 total +Update Strategy: RollingUpdate + Partition: 0 +Pods Status: 1 Running / 0 Waiting / 0 Succeeded / 0 Failed +Pod Template: + Labels: app.kubernetes.io/name=controller + model.juju.is/disable-webhook=true + Annotations: controller.juju.is/id: 1165bdc0-b9d0-477e-8eac-ea9f3f77e850 + Service Account: controller + Init Containers: + charm-init: + Image: ghcr.io/juju/jujud-operator:4.0-rc1 + Port: + Host Port: + Command: + /opt/containeragent + Args: + init + --containeragent-pebble-dir + /containeragent/pebble + --charm-modified-version + 0 + --data-dir + /var/lib/juju + --bin-dir + /charm/bin + --profile-dir + /containeragent/etc/profile.d + --pebble-identities-file + /charm/etc/pebble/identities.yaml + --pebble-charm-identity + 170 + --controller + Environment Variables from: + controller-application-config Secret Optional: false + Environment: + JUJU_CONTAINER_NAMES: api-server + JUJU_K8S_POD_NAME: (v1:metadata.name) + JUJU_K8S_POD_UUID: (v1:metadata.uid) + Mounts: + /charm/bin from charm-data (rw,path="charm/bin") + /charm/containers from charm-data (rw,path="charm/containers") + /charm/etc/pebble/ from charm-data (rw,path="charm/etc/pebble/") + /containeragent/etc/profile.d from charm-data (rw,path="containeragent/etc/profile.d") + /containeragent/pebble from charm-data (rw,path="containeragent/pebble") + /var/lib/juju from charm-data (rw,path="var/lib/juju") + /var/lib/juju/template-agent.conf from controller-agent-conf (rw,path="controller-unit-agent.conf") + Containers: + charm: + Image: ghcr.io/juju/charm-base:ubuntu-24.04 + Port: + Host Port: + Command: + /charm/bin/pebble + Args: + run + --http + :38812 + --verbose + Limits: + memory: 1536Mi + Requests: + memory: 64Mi + Environment: + JUJU_CONTAINER_NAMES: api-server + Mounts: + /charm/bin from charm-data (ro,path="charm/bin") + /charm/containers from charm-data (rw,path="charm/containers") + /etc/profile.d/juju-introspection.sh from charm-data (ro,path="containeragent/etc/profile.d/juju-introspection.sh") + /usr/bin/juju-exec from charm-data (ro,path="charm/bin/containeragent") + /usr/bin/juju-introspect from charm-data (ro,path="charm/bin/containeragent") + /var/lib/juju from storage (rw) + /var/lib/juju/template-agent.conf from controller-agent-conf (rw,path="controller-unit-agent.conf") + /var/lib/pebble/default from charm-data (rw,path="containeragent/pebble") + /var/log/juju from charm-data (rw,path="containeragent/var/log/juju") + api-server: + Image: ghcr.io/juju/jujud-operator:4.0-rc1 + Port: + Host Port: + Command: + /bin/sh + Args: + -c + export JUJU_DATA_DIR=/var/lib/juju + export JUJU_TOOLS_DIR=$JUJU_DATA_DIR/tools + + mkdir -p $JUJU_TOOLS_DIR + cp /opt/jujud $JUJU_TOOLS_DIR/jujud + + test -e $JUJU_DATA_DIR/agents/controller-0/agent.conf || $JUJU_TOOLS_DIR/jujud bootstrap-state --data-dir $JUJU_DATA_DIR --show-log --timeout 20m0s + + mkdir -p /var/lib/pebble/default/layers + cat > /var/lib/pebble/default/layers/001-jujud.yaml < + apiserver-scratch: + Type: EmptyDir (a temporary directory that shares a pod's lifetime) + Medium: + SizeLimit: + controller-agent-conf: + Type: ConfigMap (a volume populated by a ConfigMap) + Name: controller-configmap + Optional: false + controller-bootstrap-params: + Type: ConfigMap (a volume populated by a ConfigMap) + Name: controller-configmap + Optional: false + Node-Selectors: + Tolerations: +Volume Claims: + Name: storage + StorageClass: microk8s-hostpath + Labels: app.kubernetes.io/managed-by=juju + app.kubernetes.io/name=controller + Annotations: controller.juju.is/id=1165bdc0-b9d0-477e-8eac-ea9f3f77e850 + Capacity: 20Gi + Access Modes: [ReadWriteOnce] +Events: diff --git a/domain/model/service/service.go b/domain/model/service/service.go index c7fa13754a49..d97faca7cfc6 100644 --- a/domain/model/service/service.go +++ b/domain/model/service/service.go @@ -184,7 +184,7 @@ type Service struct { } var ( - caasCloudTypes = []string{cloud.CloudTypeKubernetes} + caasCloudTypes = []string{cloud.CloudTypeKubernetes, cloud.CloudTypeDocker} ) // NewService returns a new Service for interacting with a models state. diff --git a/domain/schema/controller/sql/0005-cloud.sql b/domain/schema/controller/sql/0005-cloud.sql index abe45bdf09e0..781fe7d4dac4 100644 --- a/domain/schema/controller/sql/0005-cloud.sql +++ b/domain/schema/controller/sql/0005-cloud.sql @@ -23,7 +23,8 @@ INSERT INTO cloud_type VALUES (6, 'gce'), (7, 'oci'), (8, 'openstack'), -(9, 'vsphere'); +(9, 'vsphere'), +(10, 'docker'); CREATE TABLE auth_type ( id INT PRIMARY KEY, diff --git a/go.mod b/go.mod index 3a0e6999cdb2..04199bfb8852 100644 --- a/go.mod +++ b/go.mod @@ -34,6 +34,8 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/distribution/reference v0.5.0 github.com/docker/distribution v2.8.3+incompatible + github.com/docker/docker v24.0.7+incompatible + github.com/docker/go-connections v0.4.0 github.com/dustin/go-humanize v1.0.1 github.com/fatih/color v1.16.0 github.com/fsnotify/fsnotify v1.8.0 @@ -299,6 +301,15 @@ require ( sigs.k8s.io/yaml v1.4.0 // indirect ) +require ( + github.com/Microsoft/go-winio v0.5.2 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect + github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect + gotest.tools/v3 v3.5.2 // indirect +) + replace gopkg.in/yaml.v2 => github.com/juju/yaml/v2 v2.0.0 // go.uber.org/mock v0.5.0 is a broken release. It adds named arguments to mocked diff --git a/go.sum b/go.sum index 54b7760b008a..570828636869 100644 --- a/go.sum +++ b/go.sum @@ -60,8 +60,6 @@ github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 h1:IEjq88XO4PuBDcv github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5/go.mod h1:exZ0C/1emQJAw5tHOaUDyY1ycttqBAPcxuzf7QbY6ec= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/hcsshim v0.9.4 h1:mnUj0ivWy6UzbB1uLFqKR6F+ZyiDc7j4iGgHTpO+5+I= -github.com/Microsoft/hcsshim v0.9.4/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= github.com/Rican7/retry v0.3.1 h1:scY4IbO8swckzoA/11HgBwaZRJEyY9vaNJshcdhp1Mc= github.com/Rican7/retry v0.3.1/go.mod h1:CxSDrhAyXmTMeEuRAnArMu1FHu48vtfjLREWqVl7Vw0= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= @@ -148,8 +146,6 @@ github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUg github.com/cjlapao/common-go v0.0.39 h1:bAAUrj2B9v0kMzbAOhzjSmiyDy+rd56r2sy7oEiQLlA= github.com/cjlapao/common-go v0.0.39/go.mod h1:M3dzazLjTjEtZJbbxoA5ZDiGCiHmpwqW9l4UWaddwOA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= -github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= github.com/containerd/containerd v1.6.8 h1:h4dOFDwzHmqFEP754PgfgTeVXFnLiRc6kiqC7tplDJs= github.com/containerd/containerd v1.6.8/go.mod h1:By6p5KqPK0/7/CgO/A6t/Gz+CUYUu2zf1hUaaymVXB0= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= @@ -174,8 +170,8 @@ github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= -github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -252,8 +248,6 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= @@ -599,12 +593,14 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mittwald/vaultgo v0.1.4 h1:f+r0H+hgzXL9b9hkOhkFAQMAR3a/RNgqzlqywN+StXo= github.com/mittwald/vaultgo v0.1.4/go.mod h1:MuFKjvIXDjRU8cVxAKS/12JcxxzRCWzbdDcPC8sGdQQ= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mount v0.3.3 h1:fX1SVkXFJ47XWDoeFW4Sq7PdQJnV2QIDZAqjNqgEjUs= -github.com/moby/sys/mount v0.3.3/go.mod h1:PBaEorSNTLG5t/+4EgukEQVlAvVEc6ZjTySwKdqp5K0= github.com/moby/sys/mountinfo v0.7.1 h1:/tTvQaSJRr2FshkhXiIpux6fQ2Zvc4j7tAhMTStAG2g= github.com/moby/sys/mountinfo v0.7.1/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -686,6 +682,7 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= @@ -701,6 +698,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -740,8 +738,6 @@ github.com/zitadel/oidc/v3 v3.35.0 h1:ClEhENG/fly9bYV2K5GaDih9bAMfnpzj7mWjv6puTQ github.com/zitadel/oidc/v3 v3.35.0/go.mod h1:RLkol0VGNN6EHfWoa+H0GsEOqrLI/Dwl/rnqBnGh7hU= github.com/zitadel/schema v1.3.0 h1:kQ9W9tvIwZICCKWcMvCEweXET1OcOyGEuFbHs4o5kg0= github.com/zitadel/schema v1.3.0/go.mod h1:NptN6mkBDFvERUCvZHlvWmmME+gmZ44xzwRXwhzsbtc= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= @@ -852,12 +848,14 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -999,6 +997,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A= diff --git a/inspect0473 b/inspect0473 new file mode 100644 index 000000000000..5db865d3401f --- /dev/null +++ b/inspect0473 @@ -0,0 +1,264 @@ +[ + { + "Id": "04734318d38226b3b20c63e36511add52d6104e4a646d7fc89a570b6f295b107", + "Created": "2025-11-10T15:16:10.745034259Z", + "Path": "sh", + "Args": [ + "-c", + "/bin/sh", + "-c", + "echo \"Starting Juju controller agent...\"\nsleep 10\nexport JUJU_DATA_DIR=/var/lib/juju\nexport JUJU_TOOLS_DIR=$JUJU_DATA_DIR/tools\n\nmkdir -p $JUJU_TOOLS_DIR\ncp /opt/jujud $JUJU_TOOLS_DIR/jujud\n\ntest -e $JUJU_DATA_DIR/agents/controller-0/agent.conf || $JUJU_TOOLS_DIR/jujud bootstrap-state --data-dir $JUJU_DATA_DIR --show-log --timeout 20m0s\n\nmkdir -p /var/lib/pebble/default/layers\ncat > /var/lib/pebble/default/layers/001-jujud.yaml < /var/lib/pebble/default/layers/001-jujud.yaml <&2 +sleep 2 +export JUJU_DATA_DIR=/var/lib/juju +arch=$(uname -m); case "$arch" in aarch64) arch=arm64;; x86_64) arch=amd64;; *) arch=$arch;; esac +VER="` + jujuVer + `-ubuntu-${arch}" +export JUJU_TOOLS_ROOT=$JUJU_DATA_DIR/tools +export JUJU_TOOLS_DIR=$JUJU_TOOLS_ROOT/$VER +mkdir -p $JUJU_TOOLS_DIR $JUJU_DATA_DIR/agents/controller-0 +ln -snf $JUJU_TOOLS_DIR $JUJU_TOOLS_ROOT/controller-0 + +if [ ! -f $JUJU_DATA_DIR/bootstrap-params ]; then + echo "$BOOTSTRAP_PARAMS_B64" | base64 -d > $JUJU_DATA_DIR/bootstrap-params || { echo "failed writing bootstrap-params" >&2; exit 1; } + chmod 600 $JUJU_DATA_DIR/bootstrap-params || true +fi +if [ ! -f $JUJU_DATA_DIR/agents/controller-0/agent.conf ]; then + echo "$AGENT_CONF_B64" | base64 -d > $JUJU_DATA_DIR/agents/controller-0/agent.conf || { echo "failed writing agent.conf" >&2; exit 1; } + chmod 600 $JUJU_DATA_DIR/agents/controller-0/agent.conf || true +fi + +install -m0755 /opt/jujud $JUJU_TOOLS_DIR/jujud + +# Ensure required tooling exists +for bin in tar sha256sum stat; do + if ! command -v $bin >/dev/null 2>&1; then + echo "$bin not found in image; cannot build tools tarball" >&2 + exit 1 + fi +done + +TOOLS_TAR=$JUJU_TOOLS_DIR/tools.tar.gz +if [ ! -f "$TOOLS_TAR" ]; then + ( cd $JUJU_TOOLS_DIR && tar -czf tools.tar.gz jujud ) || { echo "failed to create tools.tar.gz" >&2; exit 1; } +fi +sha=$(sha256sum "$TOOLS_TAR" | awk '{print $1}') +size=$(stat -c%s "$TOOLS_TAR") +printf '{"version":"%s","url":"","sha256":"%s","size":%s}' "$VER" "$sha" "$size" > $JUJU_TOOLS_DIR/downloaded-tools.txt +sha256sum "$TOOLS_TAR" > $JUJU_TOOLS_DIR/juju${VER}.sha256 + +if ! grep -q 'old-password' $JUJU_DATA_DIR/agents/controller-0/agent.conf 2>/dev/null; then + $JUJU_TOOLS_DIR/jujud bootstrap-state --data-dir $JUJU_DATA_DIR --show-log --timeout 20m0s + rc=$? + if [ $rc -ne 0 ]; then + echo "bootstrap-state failed (rc=$rc)" >&2 + sleep 5 + exit $rc + fi +fi + +mkdir -p /var/lib/pebble/default/layers +cat > /var/lib/pebble/default/layers/001-jujud.yaml < 0 { + bindings[portKey] = []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: fmt.Sprintf("%d", p.HostPort)}} + } + } + + mounts := []mounttypes.Mount{} + for _, m := range spec.Mounts { + mounts = append(mounts, mounttypes.Mount{ + Type: mounttypes.TypeVolume, + Source: m.VolumeName, + Target: m.MountPath, + ReadOnly: m.ReadOnly, + }) + } + + containerCfg := &container.Config{ + Image: spec.Image, + Env: env, + Labels: spec.Labels, + ExposedPorts: exposed, + WorkingDir: "/var/lib/juju", + } + if len(spec.Cmd) > 0 { + containerCfg.Cmd = spec.Cmd + } + if spec.User != "" { + containerCfg.User = spec.User + } + hostCfg := &container.HostConfig{ + PortBindings: bindings, + Mounts: mounts, + RestartPolicy: container.RestartPolicy{ + Name: "always", // replaced container.RestartPolicyAlways constant + }, + } + netCfg := &networktypes.NetworkingConfig{} + if spec.NetworkName != "" { + netCfg.EndpointsConfig = map[string]*networktypes.EndpointSettings{ + spec.NetworkName: {}, + } + } + + resp, err := d.cli.ContainerCreate(ctx, containerCfg, hostCfg, netCfg, nil, spec.Name) + if err != nil { + return "", err + } + if err := d.cli.ContainerStart(ctx, resp.ID, dockerapi.ContainerStartOptions{}); err != nil { + return "", err + } + return resp.ID, nil +} + +func (d *dockerSDKClient) RemoveContainer(ctx context.Context, id string) error { + // Attempt stop then remove. + err := d.cli.ContainerStop(ctx, id, container.StopOptions{}) + if err != nil { + return err + } + return d.cli.ContainerRemove(ctx, id, dockerapi.ContainerRemoveOptions{Force: true}) +} + +func (d *dockerSDKClient) RemoveNetwork(ctx context.Context, name string) error { + return d.cli.NetworkRemove(ctx, name) +} + +func (d *dockerSDKClient) RemoveVolume(ctx context.Context, name string) error { + return d.cli.VolumeRemove(ctx, name, true) +} + +func (d *dockerSDKClient) WriteFile(ctx context.Context, containerID, path string, content []byte, mode uint32) error { + // Create tar archive in-memory containing the single file at the desired absolute path. + var buf bytes.Buffer + tw := tar.NewWriter(&buf) + // Docker expects unix style path; ensure no leading duplicate slashes. + hdr := &tar.Header{ + Name: strings.TrimLeft(path, "/"), // tar header uses relative path inside archive + Mode: int64(mode), + Size: int64(len(content)), + } + if err := tw.WriteHeader(hdr); err != nil { + return err + } + if _, err := tw.Write(content); err != nil { + return err + } + if err := tw.Close(); err != nil { + return err + } + // Copy archive to container root. + // Using "/" so that header Name is treated as absolute path components. + return d.cli.CopyToContainer(ctx, containerID, "/", &buf, dockerapi.CopyToContainerOptions{AllowOverwriteDirWithFile: true}) +} + +func (d *dockerSDKClient) Close(ctx context.Context) error { return d.cli.Close() } + +// TODO: Add label constants and filtering helpers. diff --git a/internal/provider/docker/environ.go b/internal/provider/docker/environ.go new file mode 100644 index 000000000000..7ed6466a7a7c --- /dev/null +++ b/internal/provider/docker/environ.go @@ -0,0 +1,7 @@ +// Copyright 2025 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +// Package docker implements a minimal CAAS provider backed by Docker. +// This provider exposes a caas.Broker and does not implement an IaaS-style +// environs.Environ. See provider.go and broker.go. +package docker diff --git a/internal/provider/docker/provider.go b/internal/provider/docker/provider.go new file mode 100644 index 000000000000..49fad78e5423 --- /dev/null +++ b/internal/provider/docker/provider.go @@ -0,0 +1,144 @@ +// Copyright 2025 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package docker + +import ( + context "context" + "net/url" + + "github.com/juju/errors" + "github.com/juju/jsonschema" + "github.com/juju/schema" + + "github.com/juju/juju/caas" + "github.com/juju/juju/cloud" + "github.com/juju/juju/environs" + environscloudspec "github.com/juju/juju/environs/cloudspec" + "github.com/juju/juju/environs/config" + internallogger "github.com/juju/juju/internal/logger" +) + +var logger = internallogger.GetLogger("juju.provider.docker") + +// environProvider implements a minimal CAAS-oriented provider backed by Docker. +type environProvider struct{} + +func init() { + // Register as CAAS container provider (not a traditional IaaS environ). + caas.RegisterContainerProvider("docker", &environProvider{}) + // Note: not registering an IaaS environs provider; docker is CAAS-only. +} + +// Version implements environs.EnvironProvider (unused for CAAS broker but required). +func (p *environProvider) Version() int { return 1 } + +// Open returns a caas.Broker (DockerBroker) placeholder implementing minimal CAAS operations. +func (p *environProvider) Open(ctx context.Context, args environs.OpenParams, _ environs.CredentialInvalidator) (caas.Broker, error) { + if err := p.ValidateCloud(ctx, args.Cloud); err != nil { + return nil, errors.Annotate(err, "validating cloud spec") + } + if _, err := p.Validate(ctx, args.Config, nil); err != nil { + return nil, errors.Annotate(err, "validating model config") + } + cl, err := NewClientFromSpec(ctx, args.Cloud) + if err != nil { + return nil, errors.Annotate(err, "creating docker client") + } + // Verify connectivity to the docker daemon. + if err := cl.Ping(ctx); err != nil { + return nil, errors.Annotate(err, "ping docker daemon") + } + broker := newDockerBroker(p, args.ControllerUUID, args.Cloud, args.Config) + broker.client = cl + return broker, nil +} + +// CloudSchema returns the schema for adding new clouds of this type. +func (p *environProvider) CloudSchema() *jsonschema.Schema { + return &jsonschema.Schema{ + Type: []jsonschema.Type{jsonschema.ObjectType}, + Required: []string{cloud.EndpointKey, cloud.AuthTypesKey, cloud.RegionsKey}, + Order: []string{cloud.EndpointKey, cloud.AuthTypesKey, cloud.RegionsKey}, + Properties: map[string]*jsonschema.Schema{ + cloud.EndpointKey: { + Singular: "the Docker daemon address or URL (unix:///var/run/docker.sock or tcp://host:port)", + Type: []jsonschema.Type{jsonschema.StringType}, + Format: jsonschema.FormatURI, + }, + cloud.AuthTypesKey: { + Type: []jsonschema.Type{jsonschema.ArrayType}, + Enum: []interface{}{[]string{string(cloud.EmptyAuthType)}}, + }, + cloud.RegionsKey: { + Type: []jsonschema.Type{jsonschema.ObjectType}, + Singular: "region", + Plural: "regions", + AdditionalProperties: &jsonschema.Schema{Type: []jsonschema.Type{jsonschema.ObjectType}, MaxProperties: jsonschema.Int(0)}, + }, + }, + } +} + +// CredentialSchemas declares supported auth. +func (*environProvider) CredentialSchemas() map[cloud.AuthType]cloud.CredentialSchema { + return map[cloud.AuthType]cloud.CredentialSchema{cloud.EmptyAuthType: {}} +} + +// DetectCredentials returns empty credentials. +func (*environProvider) DetectCredentials(string) (*cloud.CloudCredential, error) { + return cloud.NewEmptyCloudCredential(), nil +} + +// FinalizeCredential passthrough. +func (*environProvider) FinalizeCredential(_ environs.FinalizeCredentialContext, args environs.FinalizeCredentialParams) (*cloud.Credential, error) { + return &args.Credential, nil +} + +// DetectRegions returns a single default region. +func (*environProvider) DetectRegions() ([]cloud.Region, error) { + return []cloud.Region{{Name: "default"}}, nil +} + +// Ping tests the connection to the Docker endpoint. +func (*environProvider) Ping(ctx context.Context, endpoint string) error { + u, err := url.Parse(endpoint) + if err != nil { + return errors.New("invalid endpoint format; use unix:///path or tcp://host:port") + } + switch u.Scheme { + case "unix", "tcp", "http", "https": + return nil + default: + return errors.Errorf("unsupported scheme %q", u.Scheme) + } +} + +// ValidateCloud validates the cloud spec. +func (*environProvider) ValidateCloud(ctx context.Context, spec environscloudspec.CloudSpec) error { + if err := spec.Validate(); err != nil { + return errors.Trace(err) + } + if spec.Credential == nil { + return errors.NotValidf("missing credential") + } + return nil +} + +// Validate implements environs.EnvironProvider. +func (*environProvider) Validate(ctx context.Context, cfg, old *config.Config) (*config.Config, error) { + // Reuse base config validation; no extra attrs initially. + if err := config.Validate(ctx, cfg, old); err != nil { + return nil, err + } + return cfg, nil +} + +// --- environs.ModelConfigProvider (no-op) --- +// These allow the bootstrap "model defaults" path to query provider defaults +// without reporting "not supported" for the docker CAAS provider. +func (*environProvider) ConfigDefaults() schema.Defaults { return schema.Defaults{} } +func (*environProvider) ConfigSchema() schema.Fields { return schema.Fields{} } +func (*environProvider) ModelConfigDefaults(context.Context) (map[string]any, error) { + return map[string]any{}, nil +} diff --git a/modeloperatordeployment.yaml b/modeloperatordeployment.yaml new file mode 100644 index 000000000000..a6ad24c754a9 --- /dev/null +++ b/modeloperatordeployment.yaml @@ -0,0 +1,56 @@ +Name: modeloperator +Namespace: controller-40 +CreationTimestamp: Mon, 10 Nov 2025 09:54:28 +0000 +Labels: app.kubernetes.io/managed-by=juju + model.juju.is/disable-webhook=true + operator.juju.is/name=modeloperator + operator.juju.is/target=model +Annotations: deployment.kubernetes.io/revision: 1 +Selector: operator.juju.is/name=modeloperator,operator.juju.is/target=model +Replicas: 1 desired | 1 updated | 1 total | 1 available | 0 unavailable +StrategyType: RollingUpdate +MinReadySeconds: 0 +RollingUpdateStrategy: 25% max unavailable, 25% max surge +Pod Template: + Labels: model.juju.is/disable-webhook=true + operator.juju.is/name=modeloperator + operator.juju.is/target=model + Service Account: modeloperator + Containers: + juju-operator: + Image: ghcr.io/juju/jujud-operator:4.0-rc1 + Port: 17071/TCP (api) + Host Port: 0/TCP (api) + Command: + /bin/sh + Args: + -c + export JUJU_DATA_DIR=/var/lib/juju + export JUJU_TOOLS_DIR=$JUJU_DATA_DIR/tools + + mkdir -p $JUJU_TOOLS_DIR + cp /opt/jujud $JUJU_TOOLS_DIR/jujud + + exec $JUJU_TOOLS_DIR/jujud model --model-uuid=53864cf3-b4ee-4f1f-8b60-f405c1ebf396 + + Environment: + HTTP_PORT: 17071 + SERVICE_NAME: modeloperator + SERVICE_NAMESPACE: controller-40 + Mounts: + /var/lib/juju/agents/model-53864cf3-b4ee-4f1f-8b60-f405c1ebf396/template-agent.conf from modeloperator (rw,path="template-agent.conf") + Volumes: + modeloperator: + Type: ConfigMap (a volume populated by a ConfigMap) + Name: modeloperator + Optional: false + Node-Selectors: + Tolerations: +Conditions: + Type Status Reason + ---- ------ ------ + Available True MinimumReplicasAvailable + Progressing True NewReplicaSetAvailable +OldReplicaSets: +NewReplicaSet: modeloperator-5894577dd6 (1/1 replicas created) +Events: