From 0ea1caf4ac9882c08925043e23aec79767b734db Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Thu, 18 Apr 2019 14:43:47 -0400 Subject: [PATCH 01/14] document dev environment setup --- README.md | 1 + docs/dev/setup.md | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 docs/dev/setup.md diff --git a/README.md b/README.md index 8a8eaf573..55a5da3e7 100644 --- a/README.md +++ b/README.md @@ -8,5 +8,6 @@ For more information about this actuator and related repositories, see ## Development Environment +* [Setting up for tests](docs/dev/setup.md) * Using [Minikube](docs/dev/minikube.md) * Using [OpenShift 4](docs/dev/openshift.md) diff --git a/docs/dev/setup.md b/docs/dev/setup.md new file mode 100644 index 000000000..afd125a5e --- /dev/null +++ b/docs/dev/setup.md @@ -0,0 +1,19 @@ +# Setting up a development environment + +The cluster-api requires two external tools for running the tests +during development. + +## Install kustomize + +```bash +eval $(go env) +export GOPATH +./tools/install_kustomize.sh +``` + +## Install kubebuilder + +```bash +./tools/install_kubebuilder.sh +sudo mv kubebuilder /usr/local +``` From f862305ef4cf1cdf46607d836f2213666ee7144b Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Tue, 23 Apr 2019 15:59:11 -0400 Subject: [PATCH 02/14] rename metalkube to metal3 Replace the vendored version of the baremetal-operator code with the one from the new location, which includes corrected imports. Make all of the local code updates needed. --- CONTRIBUTING.md | 2 +- Dockerfile | 6 +- Gopkg.lock | 13 +- Gopkg.toml | 2 +- Makefile | 2 +- PROJECT | 2 +- README.md | 2 +- cmd/manager/main.go | 16 +- docs/dev/minikube.md | 4 +- pkg/apis/addtoscheme_baremetal_v1alpha1.go | 2 +- pkg/apis/baremetal/v1alpha1/doc.go | 2 +- pkg/apis/baremetal/v1alpha1/register.go | 2 +- .../baremetal/actuators/machine/actuator.go | 4 +- .../actuators/machine/actuator_test.go | 4 +- pkg/controller/add_machine.go | 2 +- .../baremetal-operator/.gitignore | 0 .../baremetal-operator/.travis.yml | 0 .../baremetal-operator/CONTRIBUTING.md | 2 +- .../baremetal-operator/DCO | 0 .../baremetal-operator/Gopkg.lock | 4 +- .../baremetal-operator/Gopkg.toml | 0 .../baremetal-operator/LICENSE | 0 .../baremetal-operator/Makefile | 14 +- .../baremetal-operator/README.md | 1 + .../baremetal-operator/build/Dockerfile | 10 +- .../cmd/make-bm-worker/main.go | 35 +- .../cmd/make-virt-host/main.go | 2 +- .../baremetal-operator/cmd/manager/main.go | 4 +- .../deploy/crds/demo-hosts.yaml | 63 +++- .../crds/example-host-bad-credentials.yaml | 2 +- .../deploy/crds/example-host.yaml | 2 +- .../metal3_v1alpha1_baremetalhost_crd.yaml} | 16 +- .../deploy/crds/worker-0.yaml | 2 +- .../baremetal-operator/deploy/operator.yaml | 16 +- .../baremetal-operator/deploy/role.yaml | 4 +- .../deploy/role_binding.yaml | 8 +- .../deploy/service_account.yaml | 2 +- .../docs/BaremetalHost_ProvisioningState.dot | 4 + .../docs/BaremetalHost_ProvisioningState.png | Bin 0 -> 210499 bytes .../baremetal-operator/docs/api.md | 21 +- .../docs/baremetalhost-states.md | 67 ++++ .../baremetal-operator/docs/configuration.md | 14 + .../baremetal-operator/docs/dev-setup.md | 31 +- .../docs/publishing-images.md | 8 +- .../baremetal-operator/docs/testing.md | 0 .../pkg/apis/addtoscheme_metal3_v1alpha1.go} | 2 +- .../baremetal-operator/pkg/apis/apis.go | 0 .../metal3}/v1alpha1/baremetalhost_types.go | 28 +- .../pkg/apis/metal3}/v1alpha1/doc.go | 4 +- .../pkg/apis/metal3}/v1alpha1/register.go | 6 +- .../metal3}/v1alpha1/zz_generated.deepcopy.go | 0 .../metal3}/v1alpha1/zz_generated.defaults.go | 0 .../baremetal-operator/pkg/bmc/access.go | 13 - .../baremetal-operator/pkg/bmc/credentials.go | 18 + .../baremetal-operator/pkg/bmc/errors.go | 28 ++ .../baremetal-operator/pkg/bmc/idrac.go | 0 .../baremetal-operator/pkg/bmc/ipmi.go | 0 .../pkg/controller/add_baremetalhost.go | 2 +- .../baremetalhost/baremetalhost_controller.go | 337 +++++++++++------- .../pkg/controller/baremetalhost/errors.go | 49 +++ .../pkg/controller/controller.go | 0 .../pkg/hardware/profile.go | 88 +++++ .../pkg/provisioner/demo/demo.go | 91 ++--- .../pkg/provisioner/fixture/fixture.go | 28 +- .../pkg/provisioner/ironic/ironic.go | 299 +++++++++++----- .../pkg/provisioner/provisioner.go | 6 +- .../pkg/utils/stringlist.go | 0 .../test/e2e/role_binding.yaml | 0 .../tools/clean_demo_hosts.sh | 3 + .../baremetal-operator/tools/clean_host.sh | 0 .../tools/run_local_ironic.sh | 59 +++ .../tools/show_host_status.sh | 0 .../baremetal-operator/version/version.go | 0 .../docs/BaremetalHost_ProvisioningState.png | Bin 167653 -> 0 bytes .../docs/baremetalhost-states.md | 5 - .../baremetal-operator/pkg/bmc/credentials.go | 37 -- .../tools/clean_demo_hosts.sh | 3 - 77 files changed, 1040 insertions(+), 463 deletions(-) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/.gitignore (100%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/.travis.yml (100%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/CONTRIBUTING.md (80%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/DCO (100%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/Gopkg.lock (99%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/Gopkg.toml (100%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/LICENSE (100%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/Makefile (79%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/README.md (83%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/build/Dockerfile (53%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/cmd/make-bm-worker/main.go (63%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/cmd/make-virt-host/main.go (99%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/cmd/manager/main.go (95%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/deploy/crds/demo-hosts.yaml (74%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/deploy/crds/example-host-bad-credentials.yaml (93%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/deploy/crds/example-host.yaml (90%) rename vendor/github.com/{metalkube/baremetal-operator/deploy/crds/metalkube_v1alpha1_baremetalhost_crd.yaml => metal3-io/baremetal-operator/deploy/crds/metal3_v1alpha1_baremetalhost_crd.yaml} (69%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/deploy/crds/worker-0.yaml (93%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/deploy/operator.yaml (65%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/deploy/role.yaml (91%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/deploy/role_binding.yaml (59%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/deploy/service_account.yaml (55%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/docs/BaremetalHost_ProvisioningState.dot (90%) create mode 100644 vendor/github.com/metal3-io/baremetal-operator/docs/BaremetalHost_ProvisioningState.png rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/docs/api.md (86%) create mode 100644 vendor/github.com/metal3-io/baremetal-operator/docs/baremetalhost-states.md create mode 100644 vendor/github.com/metal3-io/baremetal-operator/docs/configuration.md rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/docs/dev-setup.md (82%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/docs/publishing-images.md (94%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/docs/testing.md (100%) rename vendor/github.com/{metalkube/baremetal-operator/pkg/apis/addtoscheme_metalkube_v1alpha1.go => metal3-io/baremetal-operator/pkg/apis/addtoscheme_metal3_v1alpha1.go} (75%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/pkg/apis/apis.go (100%) rename vendor/github.com/{metalkube/baremetal-operator/pkg/apis/metalkube => metal3-io/baremetal-operator/pkg/apis/metal3}/v1alpha1/baremetalhost_types.go (94%) rename vendor/github.com/{metalkube/baremetal-operator/pkg/apis/metalkube => metal3-io/baremetal-operator/pkg/apis/metal3}/v1alpha1/doc.go (69%) rename vendor/github.com/{metalkube/baremetal-operator/pkg/apis/metalkube => metal3-io/baremetal-operator/pkg/apis/metal3}/v1alpha1/register.go (77%) rename vendor/github.com/{metalkube/baremetal-operator/pkg/apis/metalkube => metal3-io/baremetal-operator/pkg/apis/metal3}/v1alpha1/zz_generated.deepcopy.go (100%) rename vendor/github.com/{metalkube/baremetal-operator/pkg/apis/metalkube => metal3-io/baremetal-operator/pkg/apis/metal3}/v1alpha1/zz_generated.defaults.go (100%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/pkg/bmc/access.go (91%) create mode 100644 vendor/github.com/metal3-io/baremetal-operator/pkg/bmc/credentials.go create mode 100644 vendor/github.com/metal3-io/baremetal-operator/pkg/bmc/errors.go rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/pkg/bmc/idrac.go (100%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/pkg/bmc/ipmi.go (100%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/pkg/controller/add_baremetalhost.go (77%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/pkg/controller/baremetalhost/baremetalhost_controller.go (67%) create mode 100644 vendor/github.com/metal3-io/baremetal-operator/pkg/controller/baremetalhost/errors.go rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/pkg/controller/controller.go (100%) create mode 100644 vendor/github.com/metal3-io/baremetal-operator/pkg/hardware/profile.go rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/pkg/provisioner/demo/demo.go (77%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/pkg/provisioner/fixture/fixture.go (90%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/pkg/provisioner/ironic/ironic.go (80%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/pkg/provisioner/provisioner.go (90%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/pkg/utils/stringlist.go (100%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/test/e2e/role_binding.yaml (100%) create mode 100755 vendor/github.com/metal3-io/baremetal-operator/tools/clean_demo_hosts.sh rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/tools/clean_host.sh (100%) create mode 100755 vendor/github.com/metal3-io/baremetal-operator/tools/run_local_ironic.sh rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/tools/show_host_status.sh (100%) rename vendor/github.com/{metalkube => metal3-io}/baremetal-operator/version/version.go (100%) delete mode 100644 vendor/github.com/metalkube/baremetal-operator/docs/BaremetalHost_ProvisioningState.png delete mode 100644 vendor/github.com/metalkube/baremetal-operator/docs/baremetalhost-states.md delete mode 100644 vendor/github.com/metalkube/baremetal-operator/pkg/bmc/credentials.go delete mode 100755 vendor/github.com/metalkube/baremetal-operator/tools/clean_demo_hosts.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b2b212dbe..ddff0e75b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # How to Contribute -MetalKube projects are [Apache 2.0 licensed](LICENSE) and accept contributions via +Metal3 projects are [Apache 2.0 licensed](LICENSE) and accept contributions via GitHub pull requests. ## Certificate of Origin diff --git a/Dockerfile b/Dockerfile index 593e2e121..fe266941e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ FROM registry.svc.ci.openshift.org/openshift/release:golang-1.10 AS builder -WORKDIR /go/src/github.com/metalkube/cluster-api-provider-baremetal +WORKDIR /go/src/github.com/metal3-io/cluster-api-provider-baremetal COPY . . RUN go build -o machine-controller-manager ./cmd/manager RUN go build -o manager ./vendor/sigs.k8s.io/cluster-api/cmd/manager @@ -11,5 +11,5 @@ FROM registry.svc.ci.openshift.org/openshift/origin-v4.0:base # yum install -y $INSTALL_PKGS && \ # rpm -V $INSTALL_PKGS && \ # yum clean all -COPY --from=builder /go/src/github.com/metalkube/cluster-api-provider-baremetal/manager / -COPY --from=builder /go/src/github.com/metalkube/cluster-api-provider-baremetal/machine-controller-manager / +COPY --from=builder /go/src/github.com/metal3-io/cluster-api-provider-baremetal/manager / +COPY --from=builder /go/src/github.com/metal3-io/cluster-api-provider-baremetal/machine-controller-manager / diff --git a/Gopkg.lock b/Gopkg.lock index 22836ab30..fa72aedad 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -239,14 +239,14 @@ [[projects]] branch = "master" - digest = "1:70f8d672ed0f89f3b1bd121fa264d6ad3cf20ab5e14a0a20c74a63f7f280d884" - name = "github.com/metalkube/baremetal-operator" + digest = "1:e8f7dec9267de068cce426a0167fa5d1ae96dfe627959dfb2eed6c92fc39ad94" + name = "github.com/metal3-io/baremetal-operator" packages = [ "pkg/apis", - "pkg/apis/metalkube/v1alpha1", + "pkg/apis/metal3/v1alpha1", ] pruneopts = "T" - revision = "96ca68ea37e4e8c64ae53d872d14412fa95b9ae0" + revision = "9140d7b451b41c2d80c6222991bbc00b0a75313c" [[projects]] digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563" @@ -972,8 +972,8 @@ analyzer-version = 1 input-imports = [ "github.com/emicklei/go-restful", - "github.com/metalkube/baremetal-operator/pkg/apis", - "github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1", + "github.com/metal3-io/baremetal-operator/pkg/apis", + "github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1", "github.com/onsi/ginkgo", "github.com/onsi/gomega", "golang.org/x/net/context", @@ -984,6 +984,7 @@ "k8s.io/apimachinery/pkg/runtime", "k8s.io/apimachinery/pkg/runtime/schema", "k8s.io/apimachinery/pkg/types", + "k8s.io/client-go/discovery", "k8s.io/client-go/kubernetes/scheme", "k8s.io/client-go/plugin/pkg/client/auth/gcp", "k8s.io/client-go/rest", diff --git a/Gopkg.toml b/Gopkg.toml index 53cbe8784..2fa39a3ac 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -21,7 +21,7 @@ required = [ [[constraint]] - name="github.com/metalkube/baremetal-operator" + name="github.com/metal3-io/baremetal-operator" branch="master" [[constraint]] diff --git a/Makefile b/Makefile index 45d29f3b4..c49dba465 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ unit: manifests # Build manager binary manager: generate fmt vet - go build -o bin/manager github.com/metalkube/cluster-api-provider-baremetal/cmd/manager + go build -o bin/manager github.com/metal3-io/cluster-api-provider-baremetal/cmd/manager # Run against the configured Kubernetes cluster in ~/.kube/config run: generate fmt vet diff --git a/PROJECT b/PROJECT index 229d76c20..abee4e2b7 100644 --- a/PROJECT +++ b/PROJECT @@ -1,3 +1,3 @@ version: "1" domain: cluster.k8s.io -repo: github.com/metalkube/cluster-api-provider-baremetal +repo: github.com/metal3-io/cluster-api-provider-baremetal diff --git a/README.md b/README.md index 55a5da3e7..a5309309b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This repository contains a Machine actuator implementation for the Kubernetes [Cluster API](https://github.com/kubernetes-sigs/cluster-api/). For more information about this actuator and related repositories, see -[metalkube.org](http://metalkube.org/). +[metal3.io](http://metal3.io/). ## Development Environment diff --git a/cmd/manager/main.go b/cmd/manager/main.go index b7877a6e9..a6a92ea23 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -22,9 +22,9 @@ import ( "os" "time" - bmoapis "github.com/metalkube/baremetal-operator/pkg/apis" - "github.com/metalkube/cluster-api-provider-baremetal/pkg/apis" - "github.com/metalkube/cluster-api-provider-baremetal/pkg/cloud/baremetal/actuators/machine" + bmoapis "github.com/metal3-io/baremetal-operator/pkg/apis" + "github.com/metal3-io/cluster-api-provider-baremetal/pkg/apis" + "github.com/metal3-io/cluster-api-provider-baremetal/pkg/cloud/baremetal/actuators/machine" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/discovery" "k8s.io/client-go/rest" @@ -100,19 +100,19 @@ func waitForAPIs(cfg *rest.Config) error { return err } - metalkubeGV := schema.GroupVersion{ - Group: "metalkube.org", + metal3GV := schema.GroupVersion{ + Group: "metal3.io", Version: "v1alpha1", } for { - err = discovery.ServerSupportsVersion(c, metalkubeGV) + err = discovery.ServerSupportsVersion(c, metal3GV) if err != nil { - log.Info(fmt.Sprintf("Waiting for API group %v to be available: %v", metalkubeGV, err)) + log.Info(fmt.Sprintf("Waiting for API group %v to be available: %v", metal3GV, err)) time.Sleep(time.Second * 10) continue } - log.Info(fmt.Sprintf("Found API group %v", metalkubeGV)) + log.Info(fmt.Sprintf("Found API group %v", metal3GV)) break } diff --git a/docs/dev/minikube.md b/docs/dev/minikube.md index e54503ff7..aff5702d3 100644 --- a/docs/dev/minikube.md +++ b/docs/dev/minikube.md @@ -18,7 +18,7 @@ The actuator also uses the `BareMetalHost` custom resource that’s defined by the `baremetal-operator`. ```bash -kubectl apply -f vendor/github.com/metalkube/baremetal-operator/deploy/crds/metalkube_v1alpha1_baremetalhost_crd.yaml +kubectl apply -f vendor/github.com/metal3-io/baremetal-operator/deploy/crds/metal3_v1alpha1_baremetalhost_crd.yaml ``` ## Create a BareMetalHost @@ -30,7 +30,7 @@ a dummy `BareMetalHost` object. There’s no requirement to actually run the `baremetal-operator` to test the reconciliation logic of the actuator. Refer to the [baremetal-operator developer -documentation](https://github.com/metalkube/baremetal-operator/blob/master/docs/dev-setup.md) +documentation](https://github.com/metal3-io/baremetal-operator/blob/master/docs/dev-setup.md) for instructions and tools for creating BareMetalHost objects. ## Run the Actuator diff --git a/pkg/apis/addtoscheme_baremetal_v1alpha1.go b/pkg/apis/addtoscheme_baremetal_v1alpha1.go index 361f6fbc7..5fbf89d01 100644 --- a/pkg/apis/addtoscheme_baremetal_v1alpha1.go +++ b/pkg/apis/addtoscheme_baremetal_v1alpha1.go @@ -17,7 +17,7 @@ limitations under the License. package apis import ( - "github.com/metalkube/cluster-api-provider-baremetal/pkg/apis/baremetal/v1alpha1" + "github.com/metal3-io/cluster-api-provider-baremetal/pkg/apis/baremetal/v1alpha1" ) func init() { diff --git a/pkg/apis/baremetal/v1alpha1/doc.go b/pkg/apis/baremetal/v1alpha1/doc.go index f10ba10df..e88a2570d 100644 --- a/pkg/apis/baremetal/v1alpha1/doc.go +++ b/pkg/apis/baremetal/v1alpha1/doc.go @@ -17,7 +17,7 @@ limitations under the License. // Package v1alpha1 contains API Schema definitions for the baremetal v1alpha1 API group // +k8s:openapi-gen=true // +k8s:deepcopy-gen=package,register -// +k8s:conversion-gen=github.com/metalkube/cluster-api-provider-baremetal/pkg/apis/baremetal +// +k8s:conversion-gen=github.com/metal3-io/cluster-api-provider-baremetal/pkg/apis/baremetal // +k8s:defaulter-gen=TypeMeta // +groupName=baremetal.cluster.k8s.io package v1alpha1 diff --git a/pkg/apis/baremetal/v1alpha1/register.go b/pkg/apis/baremetal/v1alpha1/register.go index ce08fd83f..0d73274ea 100644 --- a/pkg/apis/baremetal/v1alpha1/register.go +++ b/pkg/apis/baremetal/v1alpha1/register.go @@ -19,7 +19,7 @@ limitations under the License. // Package v1alpha1 contains API Schema definitions for the baremetal v1alpha1 API group // +k8s:openapi-gen=true // +k8s:deepcopy-gen=package,register -// +k8s:conversion-gen=github.com/metalkube/cluster-api-provider-baremetal/pkg/apis/baremetal +// +k8s:conversion-gen=github.com/metal3-io/cluster-api-provider-baremetal/pkg/apis/baremetal // +k8s:defaulter-gen=TypeMeta // +groupName=baremetal.cluster.k8s.io package v1alpha1 diff --git a/pkg/cloud/baremetal/actuators/machine/actuator.go b/pkg/cloud/baremetal/actuators/machine/actuator.go index ea035723f..c60bd7e4a 100644 --- a/pkg/cloud/baremetal/actuators/machine/actuator.go +++ b/pkg/cloud/baremetal/actuators/machine/actuator.go @@ -23,7 +23,7 @@ import ( "math/rand" "time" - bmh "github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1" + bmh "github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/client-go/tools/cache" @@ -36,7 +36,7 @@ const ( ProviderName = "solas" // HostAnnotation is the key for an annotation that should go on a Machine to // reference what BareMetalHost it corresponds to. - HostAnnotation = "metalkube.org/BareMetalHost" + HostAnnotation = "metal3.io/BareMetalHost" // FIXME(dhellmann): These image values should probably come from // configuration settings and something that can tell the IP // address of the web server hosting the image in the ironic pod. diff --git a/pkg/cloud/baremetal/actuators/machine/actuator_test.go b/pkg/cloud/baremetal/actuators/machine/actuator_test.go index 57567c31c..c7e060b2e 100644 --- a/pkg/cloud/baremetal/actuators/machine/actuator_test.go +++ b/pkg/cloud/baremetal/actuators/machine/actuator_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" - bmoapis "github.com/metalkube/baremetal-operator/pkg/apis" - bmh "github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1" + bmoapis "github.com/metal3-io/baremetal-operator/pkg/apis" + bmh "github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" diff --git a/pkg/controller/add_machine.go b/pkg/controller/add_machine.go index 5a8fc2f48..2ba3fd045 100644 --- a/pkg/controller/add_machine.go +++ b/pkg/controller/add_machine.go @@ -17,7 +17,7 @@ limitations under the License. package controller import ( - "github.com/metalkube/cluster-api-provider-baremetal/pkg/cloud/baremetal/actuators/machine" + "github.com/metal3-io/cluster-api-provider-baremetal/pkg/cloud/baremetal/actuators/machine" capimachine "sigs.k8s.io/cluster-api/pkg/controller/machine" "sigs.k8s.io/controller-runtime/pkg/manager" ) diff --git a/vendor/github.com/metalkube/baremetal-operator/.gitignore b/vendor/github.com/metal3-io/baremetal-operator/.gitignore similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/.gitignore rename to vendor/github.com/metal3-io/baremetal-operator/.gitignore diff --git a/vendor/github.com/metalkube/baremetal-operator/.travis.yml b/vendor/github.com/metal3-io/baremetal-operator/.travis.yml similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/.travis.yml rename to vendor/github.com/metal3-io/baremetal-operator/.travis.yml diff --git a/vendor/github.com/metalkube/baremetal-operator/CONTRIBUTING.md b/vendor/github.com/metal3-io/baremetal-operator/CONTRIBUTING.md similarity index 80% rename from vendor/github.com/metalkube/baremetal-operator/CONTRIBUTING.md rename to vendor/github.com/metal3-io/baremetal-operator/CONTRIBUTING.md index b2b212dbe..ddff0e75b 100644 --- a/vendor/github.com/metalkube/baremetal-operator/CONTRIBUTING.md +++ b/vendor/github.com/metal3-io/baremetal-operator/CONTRIBUTING.md @@ -1,6 +1,6 @@ # How to Contribute -MetalKube projects are [Apache 2.0 licensed](LICENSE) and accept contributions via +Metal3 projects are [Apache 2.0 licensed](LICENSE) and accept contributions via GitHub pull requests. ## Certificate of Origin diff --git a/vendor/github.com/metalkube/baremetal-operator/DCO b/vendor/github.com/metal3-io/baremetal-operator/DCO similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/DCO rename to vendor/github.com/metal3-io/baremetal-operator/DCO diff --git a/vendor/github.com/metalkube/baremetal-operator/Gopkg.lock b/vendor/github.com/metal3-io/baremetal-operator/Gopkg.lock similarity index 99% rename from vendor/github.com/metalkube/baremetal-operator/Gopkg.lock rename to vendor/github.com/metal3-io/baremetal-operator/Gopkg.lock index 0b2e4d708..3abe57636 100644 --- a/vendor/github.com/metalkube/baremetal-operator/Gopkg.lock +++ b/vendor/github.com/metal3-io/baremetal-operator/Gopkg.lock @@ -203,7 +203,7 @@ [[projects]] branch = "master" - digest = "1:d76e352fd12815ad6093fee6712149803c0ac7a13d3abce04b627372d0838039" + digest = "1:d363d138539cbd0db190e08657de2f29e28077f211abcfeaad871cf537216cbe" name = "github.com/gophercloud/gophercloud" packages = [ ".", @@ -213,7 +213,7 @@ "pagination", ] pruneopts = "NT" - revision = "954aa14363ced787c28efcfcd15ae6945eb862fb" + revision = "fe629955684802399c46e2a8e9aeecc4459d2923" [[projects]] branch = "master" diff --git a/vendor/github.com/metalkube/baremetal-operator/Gopkg.toml b/vendor/github.com/metal3-io/baremetal-operator/Gopkg.toml similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/Gopkg.toml rename to vendor/github.com/metal3-io/baremetal-operator/Gopkg.toml diff --git a/vendor/github.com/metalkube/baremetal-operator/LICENSE b/vendor/github.com/metal3-io/baremetal-operator/LICENSE similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/LICENSE rename to vendor/github.com/metal3-io/baremetal-operator/LICENSE diff --git a/vendor/github.com/metalkube/baremetal-operator/Makefile b/vendor/github.com/metal3-io/baremetal-operator/Makefile similarity index 79% rename from vendor/github.com/metalkube/baremetal-operator/Makefile rename to vendor/github.com/metal3-io/baremetal-operator/Makefile index 0350d29b6..66cf5a774 100644 --- a/vendor/github.com/metalkube/baremetal-operator/Makefile +++ b/vendor/github.com/metal3-io/baremetal-operator/Makefile @@ -1,9 +1,15 @@ TEST_NAMESPACE = operator-test -RUN_NAMESPACE = metalkube +RUN_NAMESPACE = metal3 GO_TEST_FLAGS = $(VERBOSE) DEBUG = --debug SETUP = --no-setup +# Set some variables the operator expects to have in order to work +export OPERATOR_NAME=baremetal-operator +export DEPLOY_KERNEL_URL=http://172.22.0.1/images/ironic-python-agent.kernel +export DEPLOY_RAMDISK_URL=http://172.22.0.1/images/ironic-python-agent.initramfs +export IRONIC_ENDPOINT=http://localhost:6385/v1/ + .PHONY: help help: @echo "Targets:" @@ -58,15 +64,13 @@ dep: .PHONY: run run: - OPERATOR_NAME=baremetal-operator \ - operator-sdk up local \ + operator-sdk up local \ --namespace=$(RUN_NAMESPACE) \ --operator-flags="-dev" .PHONY: demo demo: - OPERATOR_NAME=baremetal-operator \ - operator-sdk up local \ + operator-sdk up local \ --namespace=$(RUN_NAMESPACE) \ --operator-flags="-dev -demo-mode" diff --git a/vendor/github.com/metalkube/baremetal-operator/README.md b/vendor/github.com/metal3-io/baremetal-operator/README.md similarity index 83% rename from vendor/github.com/metalkube/baremetal-operator/README.md rename to vendor/github.com/metal3-io/baremetal-operator/README.md index 028089a93..6969aa7c7 100644 --- a/vendor/github.com/metalkube/baremetal-operator/README.md +++ b/vendor/github.com/metal3-io/baremetal-operator/README.md @@ -3,5 +3,6 @@ Bare Metal Operator * [API documentation](docs/api.md) * [Setup Development Environment](docs/dev-setup.md) +* [Configuration](docs/configuration.md) * [Testing](docs/testing.md) * [Publishing Images](docs/publishing-images.md) diff --git a/vendor/github.com/metalkube/baremetal-operator/build/Dockerfile b/vendor/github.com/metal3-io/baremetal-operator/build/Dockerfile similarity index 53% rename from vendor/github.com/metalkube/baremetal-operator/build/Dockerfile rename to vendor/github.com/metal3-io/baremetal-operator/build/Dockerfile index 076bfa4a3..9f978e323 100644 --- a/vendor/github.com/metalkube/baremetal-operator/build/Dockerfile +++ b/vendor/github.com/metal3-io/baremetal-operator/build/Dockerfile @@ -1,11 +1,11 @@ FROM registry.svc.ci.openshift.org/openshift/release:golang-1.10 AS builder -WORKDIR /go/src/github.com/metalkube/baremetal-operator +WORKDIR /go/src/github.com/metal3-io/baremetal-operator COPY . . RUN go build -o build/_output/bin/baremetal-operator cmd/manager/main.go -FROM quay.io/metalkube/base-image +FROM quay.io/metal3-io/base-image -COPY --from=builder /go/src/github.com/metalkube/baremetal-operator/build/_output/bin/baremetal-operator / +COPY --from=builder /go/src/github.com/metal3-io/baremetal-operator/build/_output/bin/baremetal-operator / RUN if ! rpm -q genisoimage; \ then yum install -y genisoimage && \ @@ -13,5 +13,5 @@ RUN if ! rpm -q genisoimage; \ rm -rf /var/cache/yum/*; \ fi -LABEL io.k8s.display-name="MetalKube BareMetal Operator" \ - io.k8s.description="This is the image for the MetalKube BareMetal Operator." +LABEL io.k8s.display-name="Metal3 BareMetal Operator" \ + io.k8s.description="This is the image for the Metal3 BareMetal Operator." diff --git a/vendor/github.com/metalkube/baremetal-operator/cmd/make-bm-worker/main.go b/vendor/github.com/metal3-io/baremetal-operator/cmd/make-bm-worker/main.go similarity index 63% rename from vendor/github.com/metalkube/baremetal-operator/cmd/make-bm-worker/main.go rename to vendor/github.com/metal3-io/baremetal-operator/cmd/make-bm-worker/main.go index afbac4a0c..a00008aae 100644 --- a/vendor/github.com/metalkube/baremetal-operator/cmd/make-bm-worker/main.go +++ b/vendor/github.com/metal3-io/baremetal-operator/cmd/make-bm-worker/main.go @@ -20,7 +20,7 @@ data: password: {{ .EncodedPassword }} --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: {{ .Name }} @@ -29,14 +29,22 @@ spec: bmc: address: {{ .BMCAddress }} credentialsName: {{ .Name }}-bmc-secret +{{- if .WithMachine }} + machineRef: + name: {{ .Machine }} + namespace: {{ .MachineNamespace }} +{{- end }} ` // TemplateArgs holds the arguments to pass to the template. type TemplateArgs struct { - Name string - BMCAddress string - EncodedUsername string - EncodedPassword string + Name string + BMCAddress string + EncodedUsername string + EncodedPassword string + WithMachine bool + Machine string + MachineNamespace string } func encodeToSecret(input string) string { @@ -48,6 +56,10 @@ func main() { var password = flag.String("password", "", "password for BMC") var bmcAddress = flag.String("address", "", "address URL for BMC") var verbose = flag.Bool("v", false, "turn on verbose output") + var machine = flag.String( + "machine", "", "specify name of a related, existing, machine to link") + var machineNamespace = flag.String( + "machine-namespace", "", "specify namespace of a related, existing, machine to link") flag.Parse() @@ -70,10 +82,15 @@ func main() { } args := TemplateArgs{ - Name: strings.Replace(hostName, "_", "-", -1), - BMCAddress: *bmcAddress, - EncodedUsername: encodeToSecret(*username), - EncodedPassword: encodeToSecret(*password), + Name: strings.Replace(hostName, "_", "-", -1), + BMCAddress: *bmcAddress, + EncodedUsername: encodeToSecret(*username), + EncodedPassword: encodeToSecret(*password), + Machine: strings.TrimSpace(*machine), + MachineNamespace: strings.TrimSpace(*machineNamespace), + } + if args.Machine != "" { + args.WithMachine = true } if *verbose { fmt.Fprintf(os.Stderr, "%v", args) diff --git a/vendor/github.com/metalkube/baremetal-operator/cmd/make-virt-host/main.go b/vendor/github.com/metal3-io/baremetal-operator/cmd/make-virt-host/main.go similarity index 99% rename from vendor/github.com/metalkube/baremetal-operator/cmd/make-virt-host/main.go rename to vendor/github.com/metal3-io/baremetal-operator/cmd/make-virt-host/main.go index d6da7d094..a257417fa 100644 --- a/vendor/github.com/metalkube/baremetal-operator/cmd/make-virt-host/main.go +++ b/vendor/github.com/metal3-io/baremetal-operator/cmd/make-virt-host/main.go @@ -62,7 +62,7 @@ data: password: cGFzc3dvcmQ= --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: {{ .Domain }} diff --git a/vendor/github.com/metalkube/baremetal-operator/cmd/manager/main.go b/vendor/github.com/metal3-io/baremetal-operator/cmd/manager/main.go similarity index 95% rename from vendor/github.com/metalkube/baremetal-operator/cmd/manager/main.go rename to vendor/github.com/metal3-io/baremetal-operator/cmd/manager/main.go index ca9da75a1..cfa09a8cc 100644 --- a/vendor/github.com/metalkube/baremetal-operator/cmd/manager/main.go +++ b/vendor/github.com/metal3-io/baremetal-operator/cmd/manager/main.go @@ -7,8 +7,8 @@ import ( "os" "runtime" - "github.com/metalkube/baremetal-operator/pkg/apis" - "github.com/metalkube/baremetal-operator/pkg/controller" + "github.com/metal3-io/baremetal-operator/pkg/apis" + "github.com/metal3-io/baremetal-operator/pkg/controller" "github.com/operator-framework/operator-sdk/pkg/k8sutil" "github.com/operator-framework/operator-sdk/pkg/leader" sdkVersion "github.com/operator-framework/operator-sdk/version" diff --git a/vendor/github.com/metalkube/baremetal-operator/deploy/crds/demo-hosts.yaml b/vendor/github.com/metal3-io/baremetal-operator/deploy/crds/demo-hosts.yaml similarity index 74% rename from vendor/github.com/metalkube/baremetal-operator/deploy/crds/demo-hosts.yaml rename to vendor/github.com/metal3-io/baremetal-operator/deploy/crds/demo-hosts.yaml index 74b3c0913..7f381512e 100644 --- a/vendor/github.com/metalkube/baremetal-operator/deploy/crds/demo-hosts.yaml +++ b/vendor/github.com/metal3-io/baremetal-operator/deploy/crds/demo-hosts.yaml @@ -10,11 +10,44 @@ data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: demo-discovered +--- +apiVersion: machine.openshift.io/v1beta1 +kind: Machine +metadata: + labels: + sigs.k8s.io/cluster-api-cluster: ostest + sigs.k8s.io/cluster-api-machine-role: worker + sigs.k8s.io/cluster-api-machine-type: worker + name: demo-ostest-worker + namespace: openshift-machine-api +--- +apiVersion: v1 +kind: Secret +metadata: + name: demo-externally-provisioned-secret +type: Opaque +data: + username: YWRtaW4= + password: MWYyZDFlMmU2N2Rm +--- +apiVersion: metal3.io/v1alpha1 +kind: BareMetalHost +metadata: + name: demo-externally-provisioned +spec: + online: false + bmc: + address: ipmi://192.168.122.1:6233 + credentialsName: demo-externally-provisioned-secret + machineRef: + name: demo-ostest-worker + namespace: openshift-machine-api + --- apiVersion: v1 kind: Secret @@ -25,12 +58,12 @@ data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: demo-registration-error labels: - metalkubedemo: "" + metal3demo: "" spec: online: true bmc: @@ -47,12 +80,12 @@ data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: demo-registering labels: - metalkubedemo: "" + metal3demo: "" spec: online: true bmc: @@ -69,12 +102,12 @@ data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: demo-ready labels: - metalkubedemo: "" + metal3demo: "" spec: online: true bmc: @@ -91,12 +124,12 @@ data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: demo-inspecting labels: - metalkubedemo: "" + metal3demo: "" spec: online: true bmc: @@ -113,12 +146,12 @@ data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: demo-provisioning labels: - metalkubedemo: "" + metal3demo: "" spec: online: true bmc: @@ -142,12 +175,12 @@ data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: demo-provisioned labels: - metalkubedemo: "" + metal3demo: "" spec: online: true bmc: @@ -171,12 +204,12 @@ data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: demo-validation-error labels: - metalkubedemo: "" + metal3demo: "" spec: online: true bmc: diff --git a/vendor/github.com/metalkube/baremetal-operator/deploy/crds/example-host-bad-credentials.yaml b/vendor/github.com/metal3-io/baremetal-operator/deploy/crds/example-host-bad-credentials.yaml similarity index 93% rename from vendor/github.com/metalkube/baremetal-operator/deploy/crds/example-host-bad-credentials.yaml rename to vendor/github.com/metal3-io/baremetal-operator/deploy/crds/example-host-bad-credentials.yaml index 4069e7948..c495a517c 100644 --- a/vendor/github.com/metalkube/baremetal-operator/deploy/crds/example-host-bad-credentials.yaml +++ b/vendor/github.com/metal3-io/baremetal-operator/deploy/crds/example-host-bad-credentials.yaml @@ -19,7 +19,7 @@ data: password: "" --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: example-baremetalhost diff --git a/vendor/github.com/metalkube/baremetal-operator/deploy/crds/example-host.yaml b/vendor/github.com/metal3-io/baremetal-operator/deploy/crds/example-host.yaml similarity index 90% rename from vendor/github.com/metalkube/baremetal-operator/deploy/crds/example-host.yaml rename to vendor/github.com/metal3-io/baremetal-operator/deploy/crds/example-host.yaml index 366aef367..5551c86db 100644 --- a/vendor/github.com/metalkube/baremetal-operator/deploy/crds/example-host.yaml +++ b/vendor/github.com/metal3-io/baremetal-operator/deploy/crds/example-host.yaml @@ -9,7 +9,7 @@ data: password: MWYyZDFlMmU2N2Rm --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: example-baremetalhost diff --git a/vendor/github.com/metalkube/baremetal-operator/deploy/crds/metalkube_v1alpha1_baremetalhost_crd.yaml b/vendor/github.com/metal3-io/baremetal-operator/deploy/crds/metal3_v1alpha1_baremetalhost_crd.yaml similarity index 69% rename from vendor/github.com/metalkube/baremetal-operator/deploy/crds/metalkube_v1alpha1_baremetalhost_crd.yaml rename to vendor/github.com/metal3-io/baremetal-operator/deploy/crds/metal3_v1alpha1_baremetalhost_crd.yaml index cb4b55b6d..5b02d4281 100644 --- a/vendor/github.com/metalkube/baremetal-operator/deploy/crds/metalkube_v1alpha1_baremetalhost_crd.yaml +++ b/vendor/github.com/metal3-io/baremetal-operator/deploy/crds/metal3_v1alpha1_baremetalhost_crd.yaml @@ -1,9 +1,9 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: - name: baremetalhosts.metalkube.org + name: baremetalhosts.metal3.io spec: - group: metalkube.org + group: metal3.io names: kind: BareMetalHost listKind: BareMetalHostList @@ -23,13 +23,21 @@ spec: name: Provisioning Status type: string - JSONPath: .spec.machineRef.name - description: Machine - name: Machine using this host + description: Machine using this host + name: Machine type: string - JSONPath: .spec.bmc.address description: Address of management controler name: BMC type: string + - JSONPath: .status.hardwareProfile + description: The type of hardware detected + name: Hardware Profile + type: string + - JSONPath: .spec.online + description: Whether the host is online or not + name: Online + type: string - JSONPath: .status.errorMessage description: Most recent error name: Error diff --git a/vendor/github.com/metalkube/baremetal-operator/deploy/crds/worker-0.yaml b/vendor/github.com/metal3-io/baremetal-operator/deploy/crds/worker-0.yaml similarity index 93% rename from vendor/github.com/metalkube/baremetal-operator/deploy/crds/worker-0.yaml rename to vendor/github.com/metal3-io/baremetal-operator/deploy/crds/worker-0.yaml index 16013b850..d1b28cf22 100644 --- a/vendor/github.com/metalkube/baremetal-operator/deploy/crds/worker-0.yaml +++ b/vendor/github.com/metal3-io/baremetal-operator/deploy/crds/worker-0.yaml @@ -9,7 +9,7 @@ data: password: cGFzc3dvcmQ= --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: worker-0 diff --git a/vendor/github.com/metalkube/baremetal-operator/deploy/operator.yaml b/vendor/github.com/metal3-io/baremetal-operator/deploy/operator.yaml similarity index 65% rename from vendor/github.com/metalkube/baremetal-operator/deploy/operator.yaml rename to vendor/github.com/metal3-io/baremetal-operator/deploy/operator.yaml index 491441268..19d1eff7b 100644 --- a/vendor/github.com/metalkube/baremetal-operator/deploy/operator.yaml +++ b/vendor/github.com/metal3-io/baremetal-operator/deploy/operator.yaml @@ -1,21 +1,21 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: metalkube-baremetal-operator + name: metal3-baremetal-operator spec: replicas: 1 selector: matchLabels: - name: metalkube-baremetal-operator + name: metal3-baremetal-operator template: metadata: labels: - name: metalkube-baremetal-operator + name: metal3-baremetal-operator spec: - serviceAccountName: metalkube-baremetal-operator + serviceAccountName: metal3-baremetal-operator containers: - name: baremetal-operator - image: quay.io/metalkube/baremetal-operator + image: quay.io/metal3-io/baremetal-operator ports: - containerPort: 60000 name: metrics @@ -33,6 +33,12 @@ spec: fieldPath: metadata.name - name: OPERATOR_NAME value: "baremetal-operator" + - name: DEPLOY_KERNEL_URL + value: "http://172.22.0.1/images/ironic-python-agent.kernel" + - name: DEPLOY_RAMDISK_URL + value: "http://172.22.0.1/images/ironic-python-agent.initramfs" + - name: IRONIC_ENDPOINT + value: "http://localhost:6385/v1/" # Temporary workaround to talk to an external Ironic process until Ironic is running in this pod. - name: ironic-proxy image: alpine/socat diff --git a/vendor/github.com/metalkube/baremetal-operator/deploy/role.yaml b/vendor/github.com/metal3-io/baremetal-operator/deploy/role.yaml similarity index 91% rename from vendor/github.com/metalkube/baremetal-operator/deploy/role.yaml rename to vendor/github.com/metal3-io/baremetal-operator/deploy/role.yaml index e1a1b5139..9d842135b 100644 --- a/vendor/github.com/metalkube/baremetal-operator/deploy/role.yaml +++ b/vendor/github.com/metal3-io/baremetal-operator/deploy/role.yaml @@ -2,7 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: creationTimestamp: null - name: metalkube-baremetal-operator + name: metal3-baremetal-operator rules: - apiGroups: - "" @@ -39,7 +39,7 @@ rules: - get - create - apiGroups: - - metalkube.org + - metal3.io resources: - '*' verbs: diff --git a/vendor/github.com/metalkube/baremetal-operator/deploy/role_binding.yaml b/vendor/github.com/metal3-io/baremetal-operator/deploy/role_binding.yaml similarity index 59% rename from vendor/github.com/metalkube/baremetal-operator/deploy/role_binding.yaml rename to vendor/github.com/metal3-io/baremetal-operator/deploy/role_binding.yaml index dd09aec54..82c2860ee 100644 --- a/vendor/github.com/metalkube/baremetal-operator/deploy/role_binding.yaml +++ b/vendor/github.com/metal3-io/baremetal-operator/deploy/role_binding.yaml @@ -1,14 +1,14 @@ kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: - name: metalkube-baremetal-operator - namespace: metalkube + name: metal3-baremetal-operator + namespace: metal3 subjects: - kind: ServiceAccount - name: metalkube-baremetal-operator + name: metal3-baremetal-operator - kind: User name: developer roleRef: kind: Role - name: metalkube-baremetal-operator + name: metal3-baremetal-operator apiGroup: rbac.authorization.k8s.io diff --git a/vendor/github.com/metalkube/baremetal-operator/deploy/service_account.yaml b/vendor/github.com/metal3-io/baremetal-operator/deploy/service_account.yaml similarity index 55% rename from vendor/github.com/metalkube/baremetal-operator/deploy/service_account.yaml rename to vendor/github.com/metal3-io/baremetal-operator/deploy/service_account.yaml index b215ec158..b5000fe51 100644 --- a/vendor/github.com/metalkube/baremetal-operator/deploy/service_account.yaml +++ b/vendor/github.com/metal3-io/baremetal-operator/deploy/service_account.yaml @@ -1,4 +1,4 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: metalkube-baremetal-operator + name: metal3-baremetal-operator diff --git a/vendor/github.com/metalkube/baremetal-operator/docs/BaremetalHost_ProvisioningState.dot b/vendor/github.com/metal3-io/baremetal-operator/docs/BaremetalHost_ProvisioningState.dot similarity index 90% rename from vendor/github.com/metalkube/baremetal-operator/docs/BaremetalHost_ProvisioningState.dot rename to vendor/github.com/metal3-io/baremetal-operator/docs/BaremetalHost_ProvisioningState.dot index b4a685754..656b2d036 100644 --- a/vendor/github.com/metalkube/baremetal-operator/docs/BaremetalHost_ProvisioningState.dot +++ b/vendor/github.com/metal3-io/baremetal-operator/docs/BaremetalHost_ProvisioningState.dot @@ -2,6 +2,7 @@ digraph BaremetalHost { Created [shape=house] Created -> Discovered [label="BMC.* == \"\""] Created -> Registering [label="BMC.* != \"\""] + Created -> ExternallyProvisioned [label="image.URL = '' && machineRef != nil"] Discovered [shape=doublecircle] Discovered -> Registering [label="BMC.* != \"\""] @@ -44,6 +45,9 @@ digraph BaremetalHost { Provisioned -> Deprovisioning [label="NeedsDeprovisioning()"] Provisioned -> Deprovisioning [label="!DeletionTimestamp.IsZero()"] + ExternallyProvisioned [shape=doublecircle] + ExternallyProvisioned -> Deleted [label="!DeletionTimestamp.IsZero()"] + Deprovisioning -> Provisioning [label="NeedsProvisioning()"] Deprovisioning -> Ready [label="!NeedsProvisioning()"] Deprovisioning -> Deleted [label="!DeletionTimestamp.IsZero()"] diff --git a/vendor/github.com/metal3-io/baremetal-operator/docs/BaremetalHost_ProvisioningState.png b/vendor/github.com/metal3-io/baremetal-operator/docs/BaremetalHost_ProvisioningState.png new file mode 100644 index 0000000000000000000000000000000000000000..3a25d68ca64736bdba0eaa1651479189faae534c GIT binary patch literal 210499 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 literal 0 HcmV?d00001 diff --git a/vendor/github.com/metalkube/baremetal-operator/docs/api.md b/vendor/github.com/metal3-io/baremetal-operator/docs/api.md similarity index 86% rename from vendor/github.com/metalkube/baremetal-operator/docs/api.md rename to vendor/github.com/metal3-io/baremetal-operator/docs/api.md index 7801d31ea..a1d01df23 100644 --- a/vendor/github.com/metalkube/baremetal-operator/docs/api.md +++ b/vendor/github.com/metal3-io/baremetal-operator/docs/api.md @@ -48,7 +48,7 @@ data: password: cGFzc3dvcmQ= --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: openshift-worker-1 @@ -98,20 +98,20 @@ the host. *hardware.cpus.speed* -- The speed in GHz of the CPU. ``` -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: creationTimestamp: 2019-02-08T20:10:32Z finalizers: - - baremetalhost.metalkube.org + - baremetalhost.metal3.io generation: 9 labels: - metalkube.org/hardware-profile: unknown - metalkube.org/operational-status: online + metal3.io/hardware-profile: unknown + metal3.io/operational-status: online name: example-baremetalhost namespace: bmo-project resourceVersion: "1750818" - selfLink: /apis/metalkube.org/v1alpha1/namespaces/bmo-project/baremetalhosts/example-baremetalhost + selfLink: /apis/metal3.io/v1alpha1/namespaces/bmo-project/baremetalhosts/example-baremetalhost uid: 96837048-2bdd-11e9-8df7-525400f68198 spec: bmc: @@ -135,12 +135,12 @@ status: The BareMetalHost operator manages several labels with host status and settings to make it easier to find specific hosts. -*metalkube.org/hardware-profile* -- The name of the hardware profile +*metal3.io/hardware-profile* -- The name of the hardware profile that matches the hardware discovered on the host. Details about the hardware are saved to the *hardware* section of the status. If the hardware does not match a known profile, the value "unknown" is used. -*metalkube.org/operational-status* -- The status of the server. +*metal3.io/operational-status* -- The status of the server. *online* -- The server is powered on and running. @@ -158,10 +158,7 @@ Several conditions must be met in order to initiate provisioning. 1. The host `spec.image.url` field must contain a URL for a valid image file that is visible from within the cluster and from the host receiving the image. -2. The host must not have an image provisioned, as reflected by the - `status.provisioning.image.URL` field being empty. To reuse an - existing host with a different image, deprovision the host first. -3. The host must have `online` set to `true` so that the operator will +2. The host must have `online` set to `true` so that the operator will keep the host powered on. To initiate deprovisioning, clear the image URL from the host spec. diff --git a/vendor/github.com/metal3-io/baremetal-operator/docs/baremetalhost-states.md b/vendor/github.com/metal3-io/baremetal-operator/docs/baremetalhost-states.md new file mode 100644 index 000000000..ab4968fc5 --- /dev/null +++ b/vendor/github.com/metal3-io/baremetal-operator/docs/baremetalhost-states.md @@ -0,0 +1,67 @@ +# BaremetalHost Provisioning States + +The following diagram shows the possible Provisioning State transitions for the BaremetalHost object: + +![BaremetalHost ProvisioningState transitions](BaremetalHost_ProvisioningState.png) + +## Created + +Newly created hosts move immediately to Discovered or Registering. No +host stays in the Created state while the operator is working +properly. + +## Discovered + +A Discovered host is missing either the BMC address or credentials +secret name, and does not have enough information to access the BMC +for registration. + +## Externally Provisioned + +An Externally Provisioned host was deployed using another tool and +then a host object was created with a link to an existing Machine +object and without an Image setting. Hosts in this state are +monitored, and only their power status is managed. + +## Registering + +The host will stay in the Registering state while the BMC access +details are being validated. + +## Inspecting + +After the host is registered, an agent image will be booted on it +using a ramdisk. The agent collects information about the available +hardware components, and this process is called "inspection." The host +will stay in the Inspecting state until this process is completed. + +## Ready + +A host in the Ready state is available to be provisioned. + +## Provisioning + +While an image is being copied to the host and it is being configured +to run the image the host will be in the Provisioning state. + +## Provisioned + +After an image is copied to the host and the host is running the +image, it will be in the Provisioned state. + +## Deprovisioning + +When the previously provisioned image is being removed from the host, +it will be in the Deprovisioning state. + +## Error + +If an error occurs during one of the processing states (Registering, +Inspecting, Provisioning, Deprovisioning) the host will enter the +Error state. + +## Deleted + +When the host is marked to be deleted, it will move from its current +state to Deleted, at which point the resource record is deleted from +kubernetes. diff --git a/vendor/github.com/metal3-io/baremetal-operator/docs/configuration.md b/vendor/github.com/metal3-io/baremetal-operator/docs/configuration.md new file mode 100644 index 000000000..3cfe4edf7 --- /dev/null +++ b/vendor/github.com/metal3-io/baremetal-operator/docs/configuration.md @@ -0,0 +1,14 @@ +Configuration Settings +====================== + +The operator supports several configuration options for controlling +its interaction with Ironic. + +`DEPLOY_RAMDISK_URL` -- The URL for the ramdisk of the image +containing the Ironic agent. + +`DEPLOY_KERNEL_URL` -- The URL for the kernel to go with the deploy +ramdisk. + +`IRONIC_ENDPOINT` -- The URL for the operator to use when talking to +Ironic. diff --git a/vendor/github.com/metalkube/baremetal-operator/docs/dev-setup.md b/vendor/github.com/metal3-io/baremetal-operator/docs/dev-setup.md similarity index 82% rename from vendor/github.com/metalkube/baremetal-operator/docs/dev-setup.md rename to vendor/github.com/metal3-io/baremetal-operator/docs/dev-setup.md index 92d4c1a06..2704f0889 100644 --- a/vendor/github.com/metalkube/baremetal-operator/docs/dev-setup.md +++ b/vendor/github.com/metal3-io/baremetal-operator/docs/dev-setup.md @@ -16,28 +16,28 @@ install the operator-sdk tools. 3. Create a namespace to host the operator ``` - kubectl create namespace metalkube + kubectl create namespace metal3 ``` 4. Install operator-sdk ``` eval $(go env) - mkdir -p $GOPATH/src/github.com/metalkube - cd $GOPATH/src/github.com/metalkube - git clone https://github.com/metalkube/baremetal-operator.git + mkdir -p $GOPATH/src/github.com/metal3-io + cd $GOPATH/src/github.com/metal3-io + git clone https://github.com/metal3-io/baremetal-operator.git cd baremetal-operator kubectl apply -f deploy/service_account.yaml kubectl apply -f deploy/role.yaml kubectl apply -f deploy/role_binding.yaml - kubectl apply -f deploy/crds/metalkube_v1alpha1_baremetalhost_crd.yaml + kubectl apply -f deploy/crds/metal3_v1alpha1_baremetalhost_crd.yaml ``` 5. Launch the operator locally ``` export OPERATOR_NAME=baremetal-operator - operator-sdk up local --namespace=metalkube + operator-sdk up local --namespace=metal3 ``` 6. Create the CR @@ -57,6 +57,17 @@ operator when launching it. operator-sdk up local --operator-flags "-test-mode" ``` +## Running a local instance of Ironic + +There is a script available that will run a set of containers locally using +`podman` to stand up Ironic for development and testing. + +See `tools/run_local_ironic.sh`. + +Note that this script may need customizations to some of the `podman run` +commands, to include environment variables that configure the containers for +your environment. + ## Using libvirt VMs with Ironic In order to use VMs as hosts, they need to be connected to [vbmc](https://docs.openstack.org/tripleo-docs/latest/install/environments/virtualbmc.html) and @@ -66,7 +77,7 @@ network interface that will PXE boot. For example: ```yaml -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: worker-0 @@ -96,7 +107,7 @@ data: password: cGFzc3dvcmQ= --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: openshift-worker-1 @@ -131,7 +142,7 @@ data: password: cGFzc3dvcmQ= --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: openshift-master-1 @@ -164,7 +175,7 @@ data: password: cGFzc3dvcmQ= --- -apiVersion: metalkube.org/v1alpha1 +apiVersion: metal3.io/v1alpha1 kind: BareMetalHost metadata: name: worker-99 diff --git a/vendor/github.com/metalkube/baremetal-operator/docs/publishing-images.md b/vendor/github.com/metal3-io/baremetal-operator/docs/publishing-images.md similarity index 94% rename from vendor/github.com/metalkube/baremetal-operator/docs/publishing-images.md rename to vendor/github.com/metal3-io/baremetal-operator/docs/publishing-images.md index 7579dd354..d56f62b17 100644 --- a/vendor/github.com/metalkube/baremetal-operator/docs/publishing-images.md +++ b/vendor/github.com/metal3-io/baremetal-operator/docs/publishing-images.md @@ -2,12 +2,12 @@ Publishing Images ================= Images for changes merged into master are automatically built through -the [MetalKube org on -quay.io](https://quay.io/repository/metalkube/baremetal-operator). It +the [metal3-io org on +quay.io](https://quay.io/repository/metal3-io/baremetal-operator). It is also easy to set up your own builds to test images from branches in your development fork. -1. Fork `metalkube/baremetal-operator` on GitHub. +1. Fork `metal3-io/baremetal-operator` on GitHub. 2. Set up your account on [quay.io](https://quay.io). 3. Link your repository from step 1 to quay.io by following the instructions to "Create New Repository" from @@ -49,7 +49,7 @@ your development fork. build because the UI seems to cache pretty aggressively. 5. Create a dev deployment file that uses your image instead of the - one from the metalkube organization. + one from the metal3-io organization. 1. Copy `deploy/operator.yaml` to `deploy/dev-operator.yaml`. 2. Edit `deploy/dev-operator.yaml` and change the `image` setting diff --git a/vendor/github.com/metalkube/baremetal-operator/docs/testing.md b/vendor/github.com/metal3-io/baremetal-operator/docs/testing.md similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/docs/testing.md rename to vendor/github.com/metal3-io/baremetal-operator/docs/testing.md diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/apis/addtoscheme_metalkube_v1alpha1.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/apis/addtoscheme_metal3_v1alpha1.go similarity index 75% rename from vendor/github.com/metalkube/baremetal-operator/pkg/apis/addtoscheme_metalkube_v1alpha1.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/apis/addtoscheme_metal3_v1alpha1.go index 2c1f474f2..085e3f214 100644 --- a/vendor/github.com/metalkube/baremetal-operator/pkg/apis/addtoscheme_metalkube_v1alpha1.go +++ b/vendor/github.com/metal3-io/baremetal-operator/pkg/apis/addtoscheme_metal3_v1alpha1.go @@ -1,7 +1,7 @@ package apis import ( - "github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1" + "github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1" ) func init() { diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/apis/apis.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/apis/apis.go similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/pkg/apis/apis.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/apis/apis.go diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1/baremetalhost_types.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1/baremetalhost_types.go similarity index 94% rename from vendor/github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1/baremetalhost_types.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1/baremetalhost_types.go index 9de57e3a7..4a78d2bbd 100644 --- a/vendor/github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1/baremetalhost_types.go +++ b/vendor/github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1/baremetalhost_types.go @@ -15,7 +15,7 @@ const ( // BareMetalHostFinalizer is the name of the finalizer added to // hosts to block delete operations until the physical host can be // deprovisioned. - BareMetalHostFinalizer string = "baremetalhost.metalkube.org" + BareMetalHostFinalizer string = "baremetalhost.metal3.io" ) // OperationalStatus represents the state of the host @@ -74,6 +74,10 @@ const ( // disk(s) StateProvisioned ProvisioningState = "provisioned" + // StateExternallyProvisioned means something else is managing the + // image on the host + StateExternallyProvisioned ProvisioningState = "externally provisioned" + // StateDeprovisioning means we are removing an image from the // host's disk(s) StateDeprovisioning ProvisioningState = "deprovisioning" @@ -114,6 +118,11 @@ type BareMetalHostSpec struct { // How do we connect to the BMC? BMC BMCDetails `json:"bmc"` + // What is the name of the hardware profile for this host? It + // should only be necessary to set this when inspection cannot + // automatically determine the profile. + HardwareProfile string `json:"hardwareProfile"` + // Which MAC address will PXE boot? This is optional for some // types, but required for libvirt VMs driven by vbmc. BootMACAddress string `json:"bootMACAddress"` @@ -424,8 +433,6 @@ func (host *BareMetalHost) NeedsProvisioning() bool { // We have an image set, but not provisioned. return true } - // FIXME(dhellmann): Compare the provisioned image against the one - // we are supposed to have to make sure they match. return false } @@ -439,6 +446,15 @@ func (host *BareMetalHost) WasProvisioned() bool { return false } +// WasExternallyProvisioned returns true when we think something else +// is managing the image running on the host. +func (host *BareMetalHost) WasExternallyProvisioned() bool { + if host.Spec.Image == nil && host.Spec.MachineRef != nil { + return true + } + return false +} + // NeedsDeprovisioning compares the settings with the provisioning // status and returns true when the host should be deprovisioned. func (host *BareMetalHost) NeedsDeprovisioning() bool { @@ -448,7 +464,7 @@ func (host *BareMetalHost) NeedsDeprovisioning() bool { if host.Spec.Image == nil { return true } - if host.Spec.Image.URL == "" { + if host.Spec.Image.URL != host.Status.Provisioning.Image.URL { return true } return false @@ -484,13 +500,13 @@ func (host *BareMetalHost) NewEvent(reason, message string) corev1.Event { Reason: reason, Message: message, Source: corev1.EventSource{ - Component: "metalkube-baremetal-controller", + Component: "metal3-baremetal-controller", }, FirstTimestamp: t, LastTimestamp: t, Count: 1, Type: corev1.EventTypeNormal, - ReportingController: "metalkube.org/baremetal-controller", + ReportingController: "metal3.io/baremetal-controller", Related: host.Spec.MachineRef, } } diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1/doc.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1/doc.go similarity index 69% rename from vendor/github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1/doc.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1/doc.go index 859075d19..8e47c2938 100644 --- a/vendor/github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1/doc.go +++ b/vendor/github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1/doc.go @@ -1,4 +1,4 @@ -// Package v1alpha1 contains API Schema definitions for the metalkube v1alpha1 API group +// Package v1alpha1 contains API Schema definitions for the metal3 v1alpha1 API group // +k8s:deepcopy-gen=package,register -// +groupName=metalkube.org +// +groupName=metal3.io package v1alpha1 diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1/register.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1/register.go similarity index 77% rename from vendor/github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1/register.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1/register.go index ddc3ddc3c..f50d5a53a 100644 --- a/vendor/github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1/register.go +++ b/vendor/github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1/register.go @@ -1,8 +1,8 @@ // NOTE: Boilerplate only. Ignore this file. -// Package v1alpha1 contains API Schema definitions for the metalkube v1alpha1 API group +// Package v1alpha1 contains API Schema definitions for the metal3 v1alpha1 API group // +k8s:deepcopy-gen=package,register -// +groupName=metalkube.org +// +groupName=metal3.io package v1alpha1 import ( @@ -12,7 +12,7 @@ import ( var ( // SchemeGroupVersion is group version used to register these objects - SchemeGroupVersion = schema.GroupVersion{Group: "metalkube.org", Version: "v1alpha1"} + SchemeGroupVersion = schema.GroupVersion{Group: "metal3.io", Version: "v1alpha1"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1/zz_generated.deepcopy.go similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1/zz_generated.deepcopy.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1/zz_generated.deepcopy.go diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1/zz_generated.defaults.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1/zz_generated.defaults.go similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1/zz_generated.defaults.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1/zz_generated.defaults.go diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/bmc/access.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/bmc/access.go similarity index 91% rename from vendor/github.com/metalkube/baremetal-operator/pkg/bmc/access.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/bmc/access.go index 37506ca7a..f6b0e5561 100644 --- a/vendor/github.com/metalkube/baremetal-operator/pkg/bmc/access.go +++ b/vendor/github.com/metal3-io/baremetal-operator/pkg/bmc/access.go @@ -1,7 +1,6 @@ package bmc import ( - "fmt" "net" "net/url" "strings" @@ -34,18 +33,6 @@ type AccessDetails interface { DriverInfo(bmcCreds Credentials) map[string]interface{} } -// UnknownBMCTypeError is returned when the provided BMC address cannot be -// mapped to a driver. -type UnknownBMCTypeError struct { - address string - bmcType string -} - -func (e UnknownBMCTypeError) Error() string { - return fmt.Sprintf("Unknown BMC type '%s' for address %s", - e.bmcType, e.address) -} - func getTypeHostPort(address string) (bmcType, host, port, path string, err error) { // Start by assuming "type://host:port" parsedURL, err := url.Parse(address) diff --git a/vendor/github.com/metal3-io/baremetal-operator/pkg/bmc/credentials.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/bmc/credentials.go new file mode 100644 index 000000000..cbf28a50f --- /dev/null +++ b/vendor/github.com/metal3-io/baremetal-operator/pkg/bmc/credentials.go @@ -0,0 +1,18 @@ +package bmc + +// Credentials holds the information for authenticating with the BMC. +type Credentials struct { + Username string + Password string +} + +// Validate returns an error if the credentials are invalid +func (creds Credentials) Validate() error { + if creds.Username == "" { + return &CredentialsValidationError{message: "Missing BMC connection detail 'username' in credentials"} + } + if creds.Password == "" { + return &CredentialsValidationError{message: "Missing BMC connection details 'password' in credentials"} + } + return nil +} diff --git a/vendor/github.com/metal3-io/baremetal-operator/pkg/bmc/errors.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/bmc/errors.go new file mode 100644 index 000000000..e018fa66b --- /dev/null +++ b/vendor/github.com/metal3-io/baremetal-operator/pkg/bmc/errors.go @@ -0,0 +1,28 @@ +package bmc + +import ( + "fmt" +) + +// UnknownBMCTypeError is returned when the provided BMC address cannot be +// mapped to a driver. +type UnknownBMCTypeError struct { + address string + bmcType string +} + +func (e UnknownBMCTypeError) Error() string { + return fmt.Sprintf("Unknown BMC type '%s' for address %s", + e.bmcType, e.address) +} + +// CredentialsValidationError is returned when the provided BMC credentials +// are invalid (e.g. null) +type CredentialsValidationError struct { + message string +} + +func (e CredentialsValidationError) Error() string { + return fmt.Sprintf("Validation error with BMC credentials: %s", + e.message) +} diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/bmc/idrac.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/bmc/idrac.go similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/pkg/bmc/idrac.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/bmc/idrac.go diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/bmc/ipmi.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/bmc/ipmi.go similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/pkg/bmc/ipmi.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/bmc/ipmi.go diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/controller/add_baremetalhost.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/controller/add_baremetalhost.go similarity index 77% rename from vendor/github.com/metalkube/baremetal-operator/pkg/controller/add_baremetalhost.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/controller/add_baremetalhost.go index 0bc93c8f5..fe56b37ca 100644 --- a/vendor/github.com/metalkube/baremetal-operator/pkg/controller/add_baremetalhost.go +++ b/vendor/github.com/metal3-io/baremetal-operator/pkg/controller/add_baremetalhost.go @@ -1,7 +1,7 @@ package controller import ( - "github.com/metalkube/baremetal-operator/pkg/controller/baremetalhost" + "github.com/metal3-io/baremetal-operator/pkg/controller/baremetalhost" ) func init() { diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/controller/baremetalhost/baremetalhost_controller.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/controller/baremetalhost/baremetalhost_controller.go similarity index 67% rename from vendor/github.com/metalkube/baremetal-operator/pkg/controller/baremetalhost/baremetalhost_controller.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/controller/baremetalhost/baremetalhost_controller.go index 941914f5b..623bc1aaf 100644 --- a/vendor/github.com/metalkube/baremetal-operator/pkg/controller/baremetalhost/baremetalhost_controller.go +++ b/vendor/github.com/metal3-io/baremetal-operator/pkg/controller/baremetalhost/baremetalhost_controller.go @@ -4,17 +4,19 @@ import ( "context" "flag" "fmt" + "strings" "time" "github.com/pkg/errors" - metalkubev1alpha1 "github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1" - "github.com/metalkube/baremetal-operator/pkg/bmc" - "github.com/metalkube/baremetal-operator/pkg/provisioner" - "github.com/metalkube/baremetal-operator/pkg/provisioner/demo" - "github.com/metalkube/baremetal-operator/pkg/provisioner/fixture" - "github.com/metalkube/baremetal-operator/pkg/provisioner/ironic" - "github.com/metalkube/baremetal-operator/pkg/utils" + metal3v1alpha1 "github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1" + "github.com/metal3-io/baremetal-operator/pkg/bmc" + "github.com/metal3-io/baremetal-operator/pkg/hardware" + "github.com/metal3-io/baremetal-operator/pkg/provisioner" + "github.com/metal3-io/baremetal-operator/pkg/provisioner/demo" + "github.com/metal3-io/baremetal-operator/pkg/provisioner/fixture" + "github.com/metal3-io/baremetal-operator/pkg/provisioner/ironic" + "github.com/metal3-io/baremetal-operator/pkg/utils" "github.com/go-logr/logr" @@ -80,14 +82,14 @@ func newReconciler(mgr manager.Manager) reconcile.Reconciler { // add adds a new Controller to mgr with r as the reconcile.Reconciler func add(mgr manager.Manager, r reconcile.Reconciler) error { // Create a new controller - c, err := controller.New("metalkube-baremetalhost-controller", mgr, + c, err := controller.New("metal3-baremetalhost-controller", mgr, controller.Options{Reconciler: r}) if err != nil { return err } // Watch for changes to primary resource BareMetalHost - err = c.Watch(&source.Kind{Type: &metalkubev1alpha1.BareMetalHost{}}, + err = c.Watch(&source.Kind{Type: &metal3v1alpha1.BareMetalHost{}}, &handler.EnqueueRequestForObject{}) if err != nil { return err @@ -97,7 +99,7 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error { err = c.Watch(&source.Kind{Type: &corev1.Secret{}}, &handler.EnqueueRequestForOwner{ IsController: true, - OwnerType: &metalkubev1alpha1.BareMetalHost{}, + OwnerType: &metal3v1alpha1.BareMetalHost{}, }) return err } @@ -117,7 +119,7 @@ type ReconcileBareMetalHost struct { // hold them in a context type reconcileInfo struct { log logr.Logger - host *metalkubev1alpha1.BareMetalHost + host *metal3v1alpha1.BareMetalHost request reconcile.Request bmcCredsSecret *corev1.Secret events []corev1.Event @@ -157,7 +159,7 @@ func (r *ReconcileBareMetalHost) Reconcile(request reconcile.Request) (result re reqLogger.Info("Reconciling BareMetalHost") // Fetch the BareMetalHost - host := &metalkubev1alpha1.BareMetalHost{} + host := &metal3v1alpha1.BareMetalHost{} err = r.client.Get(context.TODO(), request.NamespacedName, host) if err != nil { if k8serrors.IsNotFound(err) { @@ -181,10 +183,10 @@ func (r *ReconcileBareMetalHost) Reconcile(request reconcile.Request) (result re reqLogger.Info( "adding finalizer", "existingFinalizers", host.Finalizers, - "newValue", metalkubev1alpha1.BareMetalHostFinalizer, + "newValue", metal3v1alpha1.BareMetalHostFinalizer, ) host.Finalizers = append(host.Finalizers, - metalkubev1alpha1.BareMetalHostFinalizer) + metal3v1alpha1.BareMetalHostFinalizer) err := r.client.Update(context.TODO(), host) if err != nil { return reconcile.Result{}, errors.Wrap(err, "failed to add finalizer") @@ -198,78 +200,92 @@ func (r *ReconcileBareMetalHost) Reconcile(request reconcile.Request) (result re return result, err } - // Check for a "discovered" host vs. one that we have all the info for. - if host.Spec.BMC.Address == "" { - reqLogger.Info(bmc.MissingAddressMsg) - dirty := host.SetOperationalStatus(metalkubev1alpha1.OperationalStatusDiscovered) - if dirty { - err = r.saveStatus(host) - if err != nil { + // Retrieve the BMC details from the host spec and validate host + // BMC details and build the credentials for talking to the + // management controller. + bmcCreds, bmcCredsSecret, err := r.buildAndValidateBMCCredentials(request, host) + if err != nil { + switch err.(type) { + // We treat an empty bmc address and empty bmc credentials fields as a + // trigger the host needs to be put into a discovered status. We also set + // an error message (but not an error state) on the host so we can understand + // what we may be waiting on. Editing the host to set these values will + // cause the host to be reconciled again so we do not Requeue. + case *EmptyBMCAddressError, *EmptyBMCSecretError: + dirty := host.SetOperationalStatus(metal3v1alpha1.OperationalStatusDiscovered) + if dirty { + // Set the host error message directly + // as we cannot use SetErrorCondition which + // overwrites our discovered state + host.Status.ErrorMessage = err.Error() + saveErr := r.saveStatus(host) + if saveErr != nil { + return reconcile.Result{Requeue: true}, saveErr + } // Only publish the event if we do not have an error // after saving so that we only publish one time. r.publishEvent(request, - host.NewEvent("Discovered", "Discovered host without BMC address")) + host.NewEvent("Discovered", fmt.Sprintf("Discovered host with unusable BMC details: %s", err.Error()))) } - // Without the address we can't do any more so we return here - // without checking for an error. - return reconcile.Result{Requeue: true}, err - } - reqLogger.Info("nothing to do for discovered host without BMC address") - return reconcile.Result{}, nil - } - if host.Spec.BMC.CredentialsName == "" { - reqLogger.Info(bmc.MissingCredentialsMsg) - dirty := host.SetOperationalStatus(metalkubev1alpha1.OperationalStatusDiscovered) - if dirty { - err = r.saveStatus(host) - if err != nil { - // Only publish the event if we do not have an error - // after saving so that we only publish one time. - r.publishEvent(request, - host.NewEvent("Discovered", "Discovered host without BMC credentials")) + return reconcile.Result{}, nil + // In the event a credential secret is defined, but we cannot find it + // we requeue the host as we will not know if they create the secret + // at some point in the future. + case *ResolveBMCSecretRefError: + saveErr := r.setErrorCondition(request, host, err.Error()) + if saveErr != nil { + return reconcile.Result{Requeue: true}, saveErr + } + // Only publish the event if we do not have an error + // after saving so that we only publish one time. + r.publishEvent(request, host.NewEvent("BMCCredentialError", err.Error())) + return reconcile.Result{Requeue: true, RequeueAfter: hostErrorRetryDelay}, nil + // If we have found the secret but it is missing the required fields + // or the BMC address is defined but malformed we set the + // host into an error state but we do not Requeue it + // as fixing the secret or the host BMC info will trigger + // the host to be reconciled again + case *bmc.CredentialsValidationError, *bmc.UnknownBMCTypeError: + saveErr := r.setErrorCondition(request, host, err.Error()) + if saveErr != nil { + return reconcile.Result{Requeue: true}, saveErr } - // Without any credentials we can't do any more so we return - // here without checking for an error. - return reconcile.Result{Requeue: true}, err + // Only publish the event if we do not have an error + // after saving so that we only publish one time. + r.publishEvent(request, host.NewEvent("BMCCredentialError", err.Error())) + return reconcile.Result{}, nil + default: + return reconcile.Result{}, errors.Wrap(err, "An unhandled failure occurred with the BMC secret") } - reqLogger.Info("nothing to do for discovered host without BMC credentials") - return reconcile.Result{}, nil - } - - // Load the credentials for talking to the management controller. - bmcCreds, bmcCredsSecret, err := r.getValidBMCCredentials(request, host) - if err != nil { - return reconcile.Result{}, errors.Wrap(err, "BMC credentials are invalid") - } - if bmcCreds == nil { - // We do not have valid credentials, but did not encounter a - // retriable error in determining that. Reconciliation is - // complete until something about the secrets change. - return reconcile.Result{}, nil } // Pick the action to perform - var actionName metalkubev1alpha1.ProvisioningState + var actionName metal3v1alpha1.ProvisioningState switch { + case host.WasExternallyProvisioned(): + actionName = metal3v1alpha1.StateExternallyProvisioned case host.CredentialsNeedValidation(*bmcCredsSecret): - actionName = metalkubev1alpha1.StateRegistering + actionName = metal3v1alpha1.StateRegistering case host.NeedsHardwareInspection(): - actionName = metalkubev1alpha1.StateInspecting - case host.HardwareProfile() == "": - actionName = metalkubev1alpha1.StateMatchProfile + actionName = metal3v1alpha1.StateInspecting + case host.NeedsHardwareProfile(): + actionName = metal3v1alpha1.StateMatchProfile case host.NeedsProvisioning(): - actionName = metalkubev1alpha1.StateProvisioning + actionName = metal3v1alpha1.StateProvisioning case host.NeedsDeprovisioning(): - actionName = metalkubev1alpha1.StateDeprovisioning + actionName = metal3v1alpha1.StateDeprovisioning case host.WasProvisioned(): - actionName = metalkubev1alpha1.StateProvisioned + actionName = metal3v1alpha1.StateProvisioned default: - actionName = metalkubev1alpha1.StateReady + actionName = metal3v1alpha1.StateReady } if actionName != host.Status.Provisioning.State { + reqLogger.Info("changing provisioning state", + "old", host.Status.Provisioning.State, + "new", actionName, + ) host.Status.Provisioning.State = actionName - reqLogger.Info(fmt.Sprintf("setting provisioning state to %q", actionName)) if err := r.saveStatus(host); err != nil { return reconcile.Result{}, errors.Wrap(err, fmt.Sprintf("failed to save host status after handling %q", actionName)) @@ -278,7 +294,7 @@ func (r *ReconcileBareMetalHost) Reconcile(request reconcile.Request) (result re } info := &reconcileInfo{ - log: reqLogger.WithValues("actionName", actionName), + log: reqLogger.WithValues("provisioningState", actionName), host: host, request: request, bmcCredsSecret: bmcCredsSecret, @@ -289,19 +305,21 @@ func (r *ReconcileBareMetalHost) Reconcile(request reconcile.Request) (result re } switch actionName { - case metalkubev1alpha1.StateRegistering: + case metal3v1alpha1.StateRegistering: result, err = r.actionRegistering(prov, info) - case metalkubev1alpha1.StateInspecting: + case metal3v1alpha1.StateInspecting: result, err = r.actionInspecting(prov, info) - case metalkubev1alpha1.StateMatchProfile: + case metal3v1alpha1.StateMatchProfile: result, err = r.actionMatchProfile(prov, info) - case metalkubev1alpha1.StateProvisioning: + case metal3v1alpha1.StateProvisioning: result, err = r.actionProvisioning(prov, info) - case metalkubev1alpha1.StateDeprovisioning: + case metal3v1alpha1.StateDeprovisioning: result, err = r.actionDeprovisioning(prov, info) - case metalkubev1alpha1.StateProvisioned: + case metal3v1alpha1.StateProvisioned: + result, err = r.actionManageHostPower(prov, info) + case metal3v1alpha1.StateReady: result, err = r.actionManageHostPower(prov, info) - case metalkubev1alpha1.StateReady: + case metal3v1alpha1.StateExternallyProvisioned: result, err = r.actionManageHostPower(prov, info) default: // Probably a provisioning error state? @@ -334,7 +352,7 @@ func (r *ReconcileBareMetalHost) Reconcile(request reconcile.Request) (result re // We have tried to do something that failed in a way we // assume is not retryable, so do not proceed to any other // steps. - info.log.Info("stopping on host error") + info.log.Info("stopping on host error", "message", host.Status.ErrorMessage) return reconcile.Result{}, nil } @@ -346,7 +364,7 @@ func (r *ReconcileBareMetalHost) Reconcile(request reconcile.Request) (result re } // Handle all delete cases -func (r *ReconcileBareMetalHost) deleteHost(request reconcile.Request, host *metalkubev1alpha1.BareMetalHost) (result reconcile.Result, err error) { +func (r *ReconcileBareMetalHost) deleteHost(request reconcile.Request, host *metal3v1alpha1.BareMetalHost) (result reconcile.Result, err error) { reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) @@ -357,18 +375,18 @@ func (r *ReconcileBareMetalHost) deleteHost(request reconcile.Request, host *met ) // no-op if finalizer has been removed. - if !utils.StringInList(host.Finalizers, metalkubev1alpha1.BareMetalHostFinalizer) { + if !utils.StringInList(host.Finalizers, metal3v1alpha1.BareMetalHostFinalizer) { reqLogger.Info("ready to be deleted") // There is nothing to save and no reason to requeue since we // are being deleted. return reconcile.Result{}, nil } - bmcCreds, _, err := r.getValidBMCCredentials(request, host) - // We ignore the error, because we are deleting this host anyway. - if bmcCreds == nil { - // There are no valid credentials, so create an empty - // credentials object to give to the provisioner. + // Retrieve the BMC secret from Kubernetes for this host and + // try and build credentials. If we fail, resort to an empty + // credentials object to give the provisioner + bmcCreds, _, err := r.buildAndValidateBMCCredentials(request, host) + if err != nil || bmcCreds == nil { bmcCreds = &bmc.Credentials{} } @@ -398,7 +416,7 @@ func (r *ReconcileBareMetalHost) deleteHost(request reconcile.Request, host *met // Remove finalizer to allow deletion host.Finalizers = utils.FilterStringFromList( - host.Finalizers, metalkubev1alpha1.BareMetalHostFinalizer) + host.Finalizers, metal3v1alpha1.BareMetalHostFinalizer) reqLogger.Info("cleanup is complete, removed finalizer", "remaining", host.Finalizers) if err := r.client.Update(context.Background(), host); err != nil { @@ -420,9 +438,11 @@ func (r *ReconcileBareMetalHost) actionRegistering(prov provisioner.Provisioner, } if provResult.ErrorMessage != "" { - info.host.Status.Provisioning.State = metalkubev1alpha1.StateRegistrationError - info.host.SetErrorMessage(provResult.ErrorMessage) - info.publishEvent("RegistrationError", provResult.ErrorMessage) + info.host.Status.Provisioning.State = metal3v1alpha1.StateRegistrationError + if info.host.SetErrorMessage(provResult.ErrorMessage) { + info.publishEvent("RegistrationError", provResult.ErrorMessage) + result.Requeue = true + } return result, nil } @@ -460,7 +480,7 @@ func (r *ReconcileBareMetalHost) actionInspecting(prov provisioner.Provisioner, } if provResult.ErrorMessage != "" { - info.host.Status.Provisioning.State = metalkubev1alpha1.StateRegistrationError + info.host.Status.Provisioning.State = metal3v1alpha1.StateRegistrationError info.host.SetErrorMessage(provResult.ErrorMessage) info.publishEvent("RegistrationError", provResult.ErrorMessage) return result, nil @@ -487,8 +507,39 @@ func (r *ReconcileBareMetalHost) actionInspecting(prov provisioner.Provisioner, func (r *ReconcileBareMetalHost) actionMatchProfile(prov provisioner.Provisioner, info *reconcileInfo) (result reconcile.Result, err error) { - // FIXME(dhellmann): Insert logic to match hardware profiles here. - hardwareProfile := "unknown" + var hardwareProfile string + + info.log.Info("determining hardware profile") + + // Start by looking for an override value from the user + if info.host.Spec.HardwareProfile != "" { + info.log.Info("using spec value for profile name", + "name", info.host.Spec.HardwareProfile) + hardwareProfile = info.host.Spec.HardwareProfile + _, err = hardware.GetProfile(hardwareProfile) + if err != nil { + info.log.Info("invalid hardware profile", "profile", hardwareProfile) + return result, err + } + } + + // Now do a bit of matching. + // + // FIXME(dhellmann): Insert more robust logic to match + // hardware profiles here. + if hardwareProfile == "" { + if strings.HasPrefix(info.host.Spec.BMC.Address, "libvirt") { + hardwareProfile = "libvirt" + info.log.Info("determining from BMC address", "name", hardwareProfile) + } + } + + // Now default to a value just in case there is no match + if hardwareProfile == "" { + hardwareProfile = hardware.DefaultProfileName + info.log.Info("using the default", "name", hardwareProfile) + } + if info.host.SetHardwareProfile(hardwareProfile) { info.log.Info("updating hardware profile", "profile", hardwareProfile) info.publishEvent("ProfileSet", fmt.Sprintf("Hardware profile set: %s", hardwareProfile)) @@ -508,6 +559,10 @@ func (r *ReconcileBareMetalHost) actionProvisioning(prov provisioner.Provisioner var provResult provisioner.Result getUserData := func() (string, error) { + if info.host.Spec.UserData == nil { + info.log.Info("no user data for host") + return "", nil + } info.log.Info("fetching user data before provisioning") userDataSecret := &corev1.Secret{} key := types.NamespacedName{ @@ -531,7 +586,7 @@ func (r *ReconcileBareMetalHost) actionProvisioning(prov provisioner.Provisioner if provResult.ErrorMessage != "" { info.log.Info("handling provisioning error in controller") - info.host.Status.Provisioning.State = metalkubev1alpha1.StateProvisioningError + info.host.Status.Provisioning.State = metal3v1alpha1.StateProvisioningError if info.host.SetErrorMessage(provResult.ErrorMessage) { info.publishEvent("ProvisioningError", provResult.ErrorMessage) result.Requeue = true @@ -572,7 +627,7 @@ func (r *ReconcileBareMetalHost) actionDeprovisioning(prov provisioner.Provision } if provResult.ErrorMessage != "" { - info.host.Status.Provisioning.State = metalkubev1alpha1.StateProvisioningError + info.host.Status.Provisioning.State = metal3v1alpha1.StateProvisioningError if info.host.SetErrorMessage(provResult.ErrorMessage) { info.publishEvent("ProvisioningError", provResult.ErrorMessage) result.Requeue = true @@ -589,7 +644,7 @@ func (r *ReconcileBareMetalHost) actionDeprovisioning(prov provisioner.Provision // After the provisioner is done, clear the image settings so we // transition to the next state. - info.host.Status.Provisioning.Image = metalkubev1alpha1.Image{} + info.host.Status.Provisioning.Image = metal3v1alpha1.Image{} // After deprovisioning we always requeue to ensure we enter the // "ready" state and start monitoring power status. @@ -608,7 +663,7 @@ func (r *ReconcileBareMetalHost) actionManageHostPower(prov provisioner.Provisio } if provResult.ErrorMessage != "" { - info.host.Status.Provisioning.State = metalkubev1alpha1.StatePowerManagementError + info.host.Status.Provisioning.State = metal3v1alpha1.StatePowerManagementError if info.host.SetErrorMessage(provResult.ErrorMessage) { info.publishEvent("PowerManagementError", provResult.ErrorMessage) result.Requeue = true @@ -646,7 +701,7 @@ func (r *ReconcileBareMetalHost) actionManageHostPower(prov provisioner.Provisio } if provResult.ErrorMessage != "" { - info.host.Status.Provisioning.State = metalkubev1alpha1.StatePowerManagementError + info.host.Status.Provisioning.State = metal3v1alpha1.StatePowerManagementError if info.host.SetErrorMessage(provResult.ErrorMessage) { info.publishEvent("PowerManagementError", provResult.ErrorMessage) result.Requeue = true @@ -671,13 +726,13 @@ func (r *ReconcileBareMetalHost) actionManageHostPower(prov provisioner.Provisio } -func (r *ReconcileBareMetalHost) saveStatus(host *metalkubev1alpha1.BareMetalHost) error { +func (r *ReconcileBareMetalHost) saveStatus(host *metal3v1alpha1.BareMetalHost) error { t := metav1.Now() host.Status.LastUpdated = &t return r.client.Status().Update(context.TODO(), host) } -func (r *ReconcileBareMetalHost) setErrorCondition(request reconcile.Request, host *metalkubev1alpha1.BareMetalHost, message string) error { +func (r *ReconcileBareMetalHost) setErrorCondition(request reconcile.Request, host *metal3v1alpha1.BareMetalHost, message string) error { reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) @@ -694,53 +749,75 @@ func (r *ReconcileBareMetalHost) setErrorCondition(request reconcile.Request, ho return nil } -// Make sure the credentials for the management controller look -// right. This does not actually try to use the credentials. -func (r *ReconcileBareMetalHost) getValidBMCCredentials(request reconcile.Request, host *metalkubev1alpha1.BareMetalHost) (bmcCreds *bmc.Credentials, bmcCredsSecret *corev1.Secret, err error) { - reqLogger := log.WithValues("Request.Namespace", - request.Namespace, "Request.Name", request.Name) +// Retrieve the secret containing the credentials for talking to the BMC. +func (r *ReconcileBareMetalHost) getBMCSecretAndSetOwner(request reconcile.Request, host *metal3v1alpha1.BareMetalHost) (bmcCredsSecret *corev1.Secret, err error) { - // Load the secret containing the credentials for talking to the - // BMC. This assumes we have a reference to the secret, otherwise - // Reconcile() should not have let us be called. + if host.Spec.BMC.CredentialsName == "" { + return nil, &EmptyBMCSecretError{message: "The BMC secret reference is empty"} + } secretKey := host.CredentialsKey() bmcCredsSecret = &corev1.Secret{} err = r.client.Get(context.TODO(), secretKey, bmcCredsSecret) if err != nil { - return nil, nil, errors.Wrap(err, - "failed to fetch BMC credentials from secret reference") + if k8serrors.IsNotFound(err) { + return nil, &ResolveBMCSecretRefError{message: fmt.Sprintf("The BMC secret %s does not exist", secretKey)} + } + return nil, err } - bmcCreds = &bmc.Credentials{ - Username: string(bmcCredsSecret.Data["username"]), - Password: string(bmcCredsSecret.Data["password"]), + + // Make sure the secret has the correct owner as soon as we can. + // This can return an SaveBMCSecretOwnerError + // which isn't handled causing us to immediately try again + // which seems fine as we expect this to be a transient failure + err = r.setBMCCredentialsSecretOwner(request, host, bmcCredsSecret) + if err != nil { + return bmcCredsSecret, err } - // Verify that the secret contains the expected info. - if validCreds, reason := bmcCreds.AreValid(); !validCreds { - reqLogger.Info("invalid BMC Credentials", "reason", reason) - err := r.setErrorCondition(request, host, reason) - if err != nil { - return nil, nil, errors.Wrap(err, "failed to set error condition") - } + return bmcCredsSecret, nil +} - // Only publish the event if we do not have an error - // after saving so that we only publish one time. - r.publishEvent(request, host.NewEvent("BMCCredentialError", reason)) +// Make sure the credentials for the management controller look +// right and manufacture bmc.Credentials. This does not actually try +// to use the credentials. +func (r *ReconcileBareMetalHost) buildAndValidateBMCCredentials(request reconcile.Request, host *metal3v1alpha1.BareMetalHost) (bmcCreds *bmc.Credentials, bmcCredsSecret *corev1.Secret, err error) { - // This is not an error we can retry from, so stop reconciling. - return nil, nil, nil + // Retrieve the BMC secret from Kubernetes for this host + bmcCredsSecret, err = r.getBMCSecretAndSetOwner(request, host) + if err != nil { + return nil, nil, err } - // Make sure the secret has the correct owner. - if err = r.setBMCCredentialsSecretOwner(request, host, bmcCredsSecret); err != nil { - return nil, nil, errors.Wrap(err, - "failed to update owner of credentials secret") + // Check for a "discovered" host vs. one that we have all the info for + // and find empty Address or CredentialsName fields + if host.Spec.BMC.Address == "" { + return nil, nil, &EmptyBMCAddressError{message: "Missing BMC connection detail 'Address'"} + } + + // pass the bmc address to bmc.NewAccessDetails which will do + // more in-depth checking on the url to ensure it is + // a valid bmc address, returning a bmc.UnknownBMCTypeError + // if it is not conformant + _, err = bmc.NewAccessDetails(host.Spec.BMC.Address) + if err != nil { + return nil, nil, err + } + + bmcCreds = &bmc.Credentials{ + Username: string(bmcCredsSecret.Data["username"]), + Password: string(bmcCredsSecret.Data["password"]), + } + + // Verify that the secret contains the expected info. + err = bmcCreds.Validate() + if err != nil { + return nil, bmcCredsSecret, err } return bmcCreds, bmcCredsSecret, nil } -func (r *ReconcileBareMetalHost) setBMCCredentialsSecretOwner(request reconcile.Request, host *metalkubev1alpha1.BareMetalHost, secret *corev1.Secret) (err error) { +func (r *ReconcileBareMetalHost) setBMCCredentialsSecretOwner(request reconcile.Request, host *metal3v1alpha1.BareMetalHost, secret *corev1.Secret) (err error) { reqLogger := log.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) if metav1.IsControlledBy(secret, host) { @@ -749,11 +826,11 @@ func (r *ReconcileBareMetalHost) setBMCCredentialsSecretOwner(request reconcile. reqLogger.Info("updating owner of secret") err = controllerutil.SetControllerReference(host, secret, r.scheme) if err != nil { - return errors.Wrap(err, "failed to set owner") + return &SaveBMCSecretOwnerError{message: fmt.Sprintf("cannot set owner: %q", err.Error())} } err = r.client.Update(context.TODO(), secret) if err != nil { - return errors.Wrap(err, "failed to save owner") + return &SaveBMCSecretOwnerError{message: fmt.Sprintf("cannot save owner: %q", err.Error())} } return nil } @@ -770,6 +847,6 @@ func (r *ReconcileBareMetalHost) publishEvent(request reconcile.Request, event c return } -func hostHasFinalizer(host *metalkubev1alpha1.BareMetalHost) bool { - return utils.StringInList(host.Finalizers, metalkubev1alpha1.BareMetalHostFinalizer) +func hostHasFinalizer(host *metal3v1alpha1.BareMetalHost) bool { + return utils.StringInList(host.Finalizers, metal3v1alpha1.BareMetalHostFinalizer) } diff --git a/vendor/github.com/metal3-io/baremetal-operator/pkg/controller/baremetalhost/errors.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/controller/baremetalhost/errors.go new file mode 100644 index 000000000..b09864e28 --- /dev/null +++ b/vendor/github.com/metal3-io/baremetal-operator/pkg/controller/baremetalhost/errors.go @@ -0,0 +1,49 @@ +package baremetalhost + +import ( + "fmt" +) + +// EmptyBMCAddressError is returned when the BMC address field +// for a host is empty +type EmptyBMCAddressError struct { + message string +} + +func (e EmptyBMCAddressError) Error() string { + return fmt.Sprintf("Empty BMC address %s", + e.message) +} + +// EmptyBMCSecretError is returned when the BMC secret +// for a host is empty +type EmptyBMCSecretError struct { + message string +} + +func (e EmptyBMCSecretError) Error() string { + return fmt.Sprintf("No BMC CredentialsName defined %s", + e.message) +} + +// ResolveBMCSecretRefError is returned when the BMC secret +// for a host is defined but cannot be found +type ResolveBMCSecretRefError struct { + message string +} + +func (e ResolveBMCSecretRefError) Error() string { + return fmt.Sprintf("BMC CredentialsName secret doesn't exist %s", + e.message) +} + +// SaveBMCSecretOwnerError is returned when we +// fail to set the owner of a secret +type SaveBMCSecretOwnerError struct { + message string +} + +func (e SaveBMCSecretOwnerError) Error() string { + return fmt.Sprintf("Failed to set owner of BMC secret %s", + e.message) +} diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/controller/controller.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/controller/controller.go similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/pkg/controller/controller.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/controller/controller.go diff --git a/vendor/github.com/metal3-io/baremetal-operator/pkg/hardware/profile.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/hardware/profile.go new file mode 100644 index 000000000..2bcc17ce1 --- /dev/null +++ b/vendor/github.com/metal3-io/baremetal-operator/pkg/hardware/profile.go @@ -0,0 +1,88 @@ +package hardware + +import ( + "fmt" +) + +const ( + // DefaultProfileName is the default hardware profile to use when + // no other profile matches. + DefaultProfileName string = "unknown" +) + +// Profile holds the settings for a class of hardware. +type Profile struct { + // Name holds the profile name + Name string + + // RootDeviceHints holds the suggestions for placing the storage + // for the root filesystem. + RootDeviceHints RootDeviceHints + + // RootGB is the size of the root volume in GB + RootGB int + + // LocalGB is the size of something(?) + LocalGB int + + // CPUArch is the architecture of the CPU. + CPUArch string +} + +// RootDeviceHints holds the hints for specifying the storage location +// for the root filesystem for the image. +// +// NOTE(dhellmann): Valid ironic hints are: "vendor, +// wwn_vendor_extension, wwn_with_extension, by_path, serial, wwn, +// size, rotational, name, hctl, model" +type RootDeviceHints struct { + // A device name like "/dev/vda" + DeviceName string + + // A SCSI bus address like 0:0:0:0 + HCTL string +} + +var profiles = make(map[string]Profile) + +func init() { + profiles[DefaultProfileName] = Profile{ + Name: DefaultProfileName, + RootDeviceHints: RootDeviceHints{ + DeviceName: "/dev/sda", + }, + RootGB: 10, + LocalGB: 50, + CPUArch: "x86_64", + } + + profiles["libvirt"] = Profile{ + Name: "libvirt", + RootDeviceHints: RootDeviceHints{ + DeviceName: "/dev/vda", + }, + RootGB: 10, + LocalGB: 50, + CPUArch: "x86_64", + } + + profiles["dell"] = Profile{ + Name: "dell", + RootDeviceHints: RootDeviceHints{ + HCTL: "0:0:0:0", + }, + RootGB: 10, + LocalGB: 50, + CPUArch: "x86_64", + } + +} + +// GetProfile returns the named profile +func GetProfile(name string) (Profile, error) { + profile, ok := profiles[name] + if !ok { + return Profile{}, fmt.Errorf("No hardware profile named %q", name) + } + return profile, nil +} diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/provisioner/demo/demo.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/provisioner/demo/demo.go similarity index 77% rename from vendor/github.com/metalkube/baremetal-operator/pkg/provisioner/demo/demo.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/provisioner/demo/demo.go index 74b68f650..8745e85cd 100644 --- a/vendor/github.com/metalkube/baremetal-operator/pkg/provisioner/demo/demo.go +++ b/vendor/github.com/metal3-io/baremetal-operator/pkg/provisioner/demo/demo.go @@ -1,15 +1,14 @@ package demo import ( - "fmt" "time" "github.com/go-logr/logr" logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" - metalkubev1alpha1 "github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1" - "github.com/metalkube/baremetal-operator/pkg/bmc" - "github.com/metalkube/baremetal-operator/pkg/provisioner" + metal3v1alpha1 "github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1" + "github.com/metal3-io/baremetal-operator/pkg/bmc" + "github.com/metal3-io/baremetal-operator/pkg/provisioner" ) var log = logf.Log.WithName("demo") @@ -17,20 +16,37 @@ var deprovisionRequeueDelay = time.Second * 10 var provisionRequeueDelay = time.Second * 10 const ( - registrationErrorHost string = "demo-registration-error" - registeringHost string = "demo-registering" - readyHost string = "demo-ready" - inspectingHost string = "demo-inspecting" - validationErrorHost string = "demo-validation-error" - provisioningHost string = "demo-provisioning" - provisionedHost string = "demo-provisioned" + // RegistrationErrorHost is a host that fails the registration + // process. + RegistrationErrorHost string = "demo-registration-error" + + // RegisteringHost is a host that is in the process of being + // registered. + RegisteringHost string = "demo-registering" + + // ReadyHost is a host that is ready to be used. + ReadyHost string = "demo-ready" + + // InspectingHost is a host that is having its hardware scanned. + InspectingHost string = "demo-inspecting" + + // ValidationErrorHost is a host that started provisioning but + // failed validation. + ValidationErrorHost string = "demo-validation-error" + + // ProvisioningHost is a host that is in the middle of + // provisioning. + ProvisioningHost string = "demo-provisioning" + + // ProvisionedHost is a host that has had an image provisioned. + ProvisionedHost string = "demo-provisioned" ) // Provisioner implements the provisioning.Provisioner interface // and uses Ironic to manage the host. type demoProvisioner struct { // the host to be managed by this provisioner - host *metalkubev1alpha1.BareMetalHost + host *metal3v1alpha1.BareMetalHost // the bmc credentials bmcCreds bmc.Credentials // a logger configured for this host @@ -40,7 +56,7 @@ type demoProvisioner struct { } // New returns a new Ironic Provisioner -func New(host *metalkubev1alpha1.BareMetalHost, bmcCreds bmc.Credentials, publisher provisioner.EventPublisher) (provisioner.Provisioner, error) { +func New(host *metal3v1alpha1.BareMetalHost, bmcCreds bmc.Credentials, publisher provisioner.EventPublisher) (provisioner.Provisioner, error) { p := &demoProvisioner{ host: host, bmcCreds: bmcCreds, @@ -59,15 +75,12 @@ func (p *demoProvisioner) ValidateManagementAccess() (result provisioner.Result, switch hostName { - case registrationErrorHost: - if p.host.SetErrorMessage("failed to register new host") { - p.log.Info("setting registration error") - p.publisher("RegistrationError", "Failed to register new host") - result.Dirty = true - } + case RegistrationErrorHost: // We have set an error, so Reconcile() will stop + result.ErrorMessage = "failed to register new host" + p.log.Info("setting registration error") - case registeringHost: + case RegisteringHost: // Always mark the host as dirty so it never moves past this // point. result.Dirty = true @@ -79,7 +92,6 @@ func (p *demoProvisioner) ValidateManagementAccess() (result provisioner.Result, p.log.Info("setting provisioning id", "provisioningID", p.host.Status.Provisioning.ID) result.Dirty = true - p.publisher("Registered", "Registered new host") } } @@ -95,8 +107,8 @@ func (p *demoProvisioner) InspectHardware() (result provisioner.Result, err erro hostName := p.host.ObjectMeta.Name - if hostName == inspectingHost { - p.host.Status.Provisioning.State = metalkubev1alpha1.StateInspecting + if hostName == InspectingHost { + p.host.Status.Provisioning.State = metal3v1alpha1.StateInspecting // set dirty so we don't allow the host to progress past this // state in Reconcile() result.Dirty = true @@ -111,10 +123,10 @@ func (p *demoProvisioner) InspectHardware() (result provisioner.Result, err erro if p.host.Status.HardwareDetails == nil { p.log.Info("continuing inspection by setting details") p.host.Status.HardwareDetails = - &metalkubev1alpha1.HardwareDetails{ + &metal3v1alpha1.HardwareDetails{ RAMGiB: 128, - NIC: []metalkubev1alpha1.NIC{ - metalkubev1alpha1.NIC{ + NIC: []metal3v1alpha1.NIC{ + metal3v1alpha1.NIC{ Name: "nic-1", Model: "virt-io", Network: "Pod Networking", @@ -122,7 +134,7 @@ func (p *demoProvisioner) InspectHardware() (result provisioner.Result, err erro IP: "192.168.100.1", SpeedGbps: 1, }, - metalkubev1alpha1.NIC{ + metal3v1alpha1.NIC{ Name: "nic-2", Model: "e1000", Network: "Pod Networking", @@ -131,29 +143,29 @@ func (p *demoProvisioner) InspectHardware() (result provisioner.Result, err erro SpeedGbps: 1, }, }, - Storage: []metalkubev1alpha1.Storage{ - metalkubev1alpha1.Storage{ + Storage: []metal3v1alpha1.Storage{ + metal3v1alpha1.Storage{ Name: "disk-1 (boot)", Type: "SSD", SizeGiB: 1024 * 93, Model: "Dell CFJ61", }, - metalkubev1alpha1.Storage{ + metal3v1alpha1.Storage{ Name: "disk-2", Type: "SSD", SizeGiB: 1024 * 93, Model: "Dell CFJ61", }, }, - CPUs: []metalkubev1alpha1.CPU{ - metalkubev1alpha1.CPU{ + CPUs: []metal3v1alpha1.CPU{ + metal3v1alpha1.CPU{ Type: "x86", SpeedGHz: 3, }, }, } p.publisher("InspectionComplete", "Hardware inspection completed") - p.host.SetOperationalStatus(metalkubev1alpha1.OperationalStatusOK) + p.host.SetOperationalStatus(metal3v1alpha1.OperationalStatusOK) result.Dirty = true return result, nil } @@ -182,22 +194,17 @@ func (p *demoProvisioner) Provision(getUserData provisioner.UserDataSource) (res switch hostName { - case validationErrorHost: - p.log.Info("validation error host") - p.publisher("HostValidationError", "validation failed") - p.host.SetErrorMessage("validation failed") - result.Dirty = true + case ValidationErrorHost: + p.log.Info("setting validation error") + result.ErrorMessage = "validation failed" - case provisioningHost: + case ProvisioningHost: p.log.Info("provisioning host") result.Dirty = true result.RequeueAfter = time.Second * 5 default: - p.publisher("ProvisioningComplete", - fmt.Sprintf("Image provisioning completed for %s", p.host.Spec.Image.URL)) p.log.Info("finished provisioning") - result.Dirty = true } return result, nil diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/provisioner/fixture/fixture.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/provisioner/fixture/fixture.go similarity index 90% rename from vendor/github.com/metalkube/baremetal-operator/pkg/provisioner/fixture/fixture.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/provisioner/fixture/fixture.go index 2c073b0d1..d70d229ad 100644 --- a/vendor/github.com/metalkube/baremetal-operator/pkg/provisioner/fixture/fixture.go +++ b/vendor/github.com/metal3-io/baremetal-operator/pkg/provisioner/fixture/fixture.go @@ -6,9 +6,9 @@ import ( "github.com/go-logr/logr" logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" - metalkubev1alpha1 "github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1" - "github.com/metalkube/baremetal-operator/pkg/bmc" - "github.com/metalkube/baremetal-operator/pkg/provisioner" + metal3v1alpha1 "github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1" + "github.com/metal3-io/baremetal-operator/pkg/bmc" + "github.com/metal3-io/baremetal-operator/pkg/provisioner" ) var log = logf.Log.WithName("fixture") @@ -19,7 +19,7 @@ var provisionRequeueDelay = time.Second * 10 // and uses Ironic to manage the host. type fixtureProvisioner struct { // the host to be managed by this provisioner - host *metalkubev1alpha1.BareMetalHost + host *metal3v1alpha1.BareMetalHost // the bmc credentials bmcCreds bmc.Credentials // a logger configured for this host @@ -29,7 +29,7 @@ type fixtureProvisioner struct { } // New returns a new Ironic Provisioner -func New(host *metalkubev1alpha1.BareMetalHost, bmcCreds bmc.Credentials, publisher provisioner.EventPublisher) (provisioner.Provisioner, error) { +func New(host *metal3v1alpha1.BareMetalHost, bmcCreds bmc.Credentials, publisher provisioner.EventPublisher) (provisioner.Provisioner, error) { p := &fixtureProvisioner{ host: host, bmcCreds: bmcCreds, @@ -75,10 +75,10 @@ func (p *fixtureProvisioner) InspectHardware() (result provisioner.Result, err e if p.host.Status.HardwareDetails == nil { p.log.Info("continuing inspection by setting details") p.host.Status.HardwareDetails = - &metalkubev1alpha1.HardwareDetails{ + &metal3v1alpha1.HardwareDetails{ RAMGiB: 128, - NIC: []metalkubev1alpha1.NIC{ - metalkubev1alpha1.NIC{ + NIC: []metal3v1alpha1.NIC{ + metal3v1alpha1.NIC{ Name: "nic-1", Model: "virt-io", Network: "Pod Networking", @@ -86,7 +86,7 @@ func (p *fixtureProvisioner) InspectHardware() (result provisioner.Result, err e IP: "192.168.100.1", SpeedGbps: 1, }, - metalkubev1alpha1.NIC{ + metal3v1alpha1.NIC{ Name: "nic-2", Model: "e1000", Network: "Pod Networking", @@ -95,22 +95,22 @@ func (p *fixtureProvisioner) InspectHardware() (result provisioner.Result, err e SpeedGbps: 1, }, }, - Storage: []metalkubev1alpha1.Storage{ - metalkubev1alpha1.Storage{ + Storage: []metal3v1alpha1.Storage{ + metal3v1alpha1.Storage{ Name: "disk-1 (boot)", Type: "SSD", SizeGiB: 1024 * 93, Model: "Dell CFJ61", }, - metalkubev1alpha1.Storage{ + metal3v1alpha1.Storage{ Name: "disk-2", Type: "SSD", SizeGiB: 1024 * 93, Model: "Dell CFJ61", }, }, - CPUs: []metalkubev1alpha1.CPU{ - metalkubev1alpha1.CPU{ + CPUs: []metal3v1alpha1.CPU{ + metal3v1alpha1.CPU{ Type: "x86", SpeedGHz: 3, }, diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/provisioner/ironic/ironic.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/provisioner/ironic/ironic.go similarity index 80% rename from vendor/github.com/metalkube/baremetal-operator/pkg/provisioner/ironic/ironic.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/provisioner/ironic/ironic.go index 73dca4ab4..be35f9022 100644 --- a/vendor/github.com/metalkube/baremetal-operator/pkg/provisioner/ironic/ironic.go +++ b/vendor/github.com/metal3-io/baremetal-operator/pkg/provisioner/ironic/ironic.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "net/http" "net/url" + "os" "strings" "time" @@ -20,31 +21,54 @@ import ( "github.com/go-logr/logr" logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" - metalkubev1alpha1 "github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1" - "github.com/metalkube/baremetal-operator/pkg/bmc" - "github.com/metalkube/baremetal-operator/pkg/provisioner" + metal3v1alpha1 "github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1" + "github.com/metal3-io/baremetal-operator/pkg/bmc" + "github.com/metal3-io/baremetal-operator/pkg/hardware" + "github.com/metal3-io/baremetal-operator/pkg/provisioner" ) var log = logf.Log.WithName("baremetalhost_ironic") var deprovisionRequeueDelay = time.Second * 10 var provisionRequeueDelay = time.Second * 10 var powerRequeueDelay = time.Second * 10 +var deployKernelURL string +var deployRamdiskURL string +var ironicEndpoint string const ( - ironicEndpoint = "http://localhost:6385/v1/" // See nodes.Node.PowerState for details powerOn = "power on" powerOff = "power off" powerNone = "None" ) +func init() { + // NOTE(dhellmann): Use Fprintf() to report errors instead of + // logging, because logging is not configured yet in init(). + deployKernelURL = os.Getenv("DEPLOY_KERNEL_URL") + if deployKernelURL == "" { + fmt.Fprintf(os.Stderr, "Cannot start: No DEPLOY_KERNEL_URL variable set\n") + os.Exit(1) + } + deployRamdiskURL = os.Getenv("DEPLOY_RAMDISK_URL") + if deployRamdiskURL == "" { + fmt.Fprintf(os.Stderr, "Cannot start: No DEPLOY_RAMDISK_URL variable set\n") + os.Exit(1) + } + ironicEndpoint = os.Getenv("IRONIC_ENDPOINT") + if ironicEndpoint == "" { + fmt.Fprintf(os.Stderr, "Cannot start: No IRONIC_ENDPOINT variable set\n") + os.Exit(1) + } +} + // Provisioner implements the provisioning.Provisioner interface // and uses Ironic to manage the host. type ironicProvisioner struct { // the host to be managed by this provisioner - host *metalkubev1alpha1.BareMetalHost + host *metal3v1alpha1.BareMetalHost // a shorter path to the provisioning status data structure - status *metalkubev1alpha1.ProvisionStatus + status *metal3v1alpha1.ProvisionStatus // access parameters for the BMC bmcAccess bmc.AccessDetails // credentials to log in to the BMC @@ -57,8 +81,14 @@ type ironicProvisioner struct { publisher provisioner.EventPublisher } -// New returns a new Ironic Provisioner -func New(host *metalkubev1alpha1.BareMetalHost, bmcCreds bmc.Credentials, publisher provisioner.EventPublisher) (provisioner.Provisioner, error) { +// A private function to construct an ironicProvisioner (rather than a +// Provisioner interface) in a consistent way for tests. +func newProvisioner(host *metal3v1alpha1.BareMetalHost, bmcCreds bmc.Credentials, publisher provisioner.EventPublisher) (*ironicProvisioner, error) { + log.Info("ironic settings", + "endpoint", ironicEndpoint, + "deployKernelURL", deployKernelURL, + "deployRamdiskURL", deployRamdiskURL, + ) client, err := noauth.NewBareMetalNoAuth(noauth.EndpointOpts{ IronicEndpoint: ironicEndpoint, }) @@ -84,6 +114,11 @@ func New(host *metalkubev1alpha1.BareMetalHost, bmcCreds bmc.Credentials, publis return p, nil } +// New returns a new Ironic Provisioner +func New(host *metal3v1alpha1.BareMetalHost, bmcCreds bmc.Credentials, publisher provisioner.EventPublisher) (provisioner.Provisioner, error) { + return newProvisioner(host, bmcCreds, publisher) +} + func (p *ironicProvisioner) validateNode(ironicNode *nodes.Node) (errorMessage string, err error) { var validationErrors []string @@ -169,8 +204,8 @@ func (p *ironicProvisioner) ValidateManagementAccess() (result provisioner.Resul // // FIXME(dhellmann): We need to get our IP on the // provisioning network from somewhere. - driverInfo["deploy_kernel"] = "http://172.22.0.1/images/ironic-python-agent.kernel" - driverInfo["deploy_ramdisk"] = "http://172.22.0.1/images/ironic-python-agent.initramfs" + driverInfo["deploy_kernel"] = deployKernelURL + driverInfo["deploy_ramdisk"] = deployRamdiskURL ironicNode, err = nodes.Create( p.client, @@ -334,10 +369,10 @@ func (p *ironicProvisioner) InspectHardware() (result provisioner.Result, err er if p.host.Status.HardwareDetails == nil { p.log.Info("continuing inspection by setting details") p.host.Status.HardwareDetails = - &metalkubev1alpha1.HardwareDetails{ + &metal3v1alpha1.HardwareDetails{ RAMGiB: 128, - NIC: []metalkubev1alpha1.NIC{ - metalkubev1alpha1.NIC{ + NIC: []metal3v1alpha1.NIC{ + metal3v1alpha1.NIC{ Name: "nic-1", Model: "virt-io", Network: "Pod Networking", @@ -345,7 +380,7 @@ func (p *ironicProvisioner) InspectHardware() (result provisioner.Result, err er IP: "192.168.100.1", SpeedGbps: 1, }, - metalkubev1alpha1.NIC{ + metal3v1alpha1.NIC{ Name: "nic-2", Model: "e1000", Network: "Pod Networking", @@ -354,22 +389,22 @@ func (p *ironicProvisioner) InspectHardware() (result provisioner.Result, err er SpeedGbps: 1, }, }, - Storage: []metalkubev1alpha1.Storage{ - metalkubev1alpha1.Storage{ + Storage: []metal3v1alpha1.Storage{ + metal3v1alpha1.Storage{ Name: "disk-1 (boot)", Type: "SSD", SizeGiB: 1024 * 93, Model: "Dell CFJ61", }, - metalkubev1alpha1.Storage{ + metal3v1alpha1.Storage{ Name: "disk-2", Type: "SSD", SizeGiB: 1024 * 93, Model: "Dell CFJ61", }, }, - CPUs: []metalkubev1alpha1.CPU{ - metalkubev1alpha1.CPU{ + CPUs: []metal3v1alpha1.CPU{ + metal3v1alpha1.CPU{ Type: "x86", SpeedGHz: 3, }, @@ -454,79 +489,170 @@ func (p *ironicProvisioner) getImageChecksum() (string, error) { return checksum, nil } -func (p *ironicProvisioner) startProvisioning(ironicNode *nodes.Node, checksum string, getUserData provisioner.UserDataSource) (result provisioner.Result, err error) { +func (p *ironicProvisioner) getUpdateOptsForNode(ironicNode *nodes.Node, checksum string) (updates nodes.UpdateOpts, err error) { - // Ensure the instance_info properties for the host are set to - // tell Ironic where to get the image to be provisioned. - ironicHasSameImage := (ironicNode.InstanceInfo["image_source"] == p.host.Spec.Image.URL && - ironicNode.InstanceInfo["image_checksum"] == checksum) + hwProf, err := hardware.GetProfile(p.host.HardwareProfile()) + if err != nil { + return updates, errors.Wrap(err, + fmt.Sprintf("Could not start provisioning with bad hardware profile %s", + p.host.HardwareProfile())) + } + + // image_source var op nodes.UpdateOp if _, ok := ironicNode.InstanceInfo["image_source"]; !ok { - // no source, need to add op = nodes.AddOp - p.log.Info("adding host settings in ironic") - } else if !ironicHasSameImage { - // have a different source or checksum, need to update + p.log.Info("adding image_source") + } else { op = nodes.ReplaceOp - p.log.Info("updating host settings in ironic") + p.log.Info("updating image_source") + } + updates = append( + updates, + nodes.UpdateOperation{ + Op: op, + Path: "/instance_info/image_source", + Value: p.host.Spec.Image.URL, + }, + ) + + // image_checksum + if _, ok := ironicNode.InstanceInfo["image_checksum"]; !ok { + op = nodes.AddOp + p.log.Info("adding image_checksum") } else { - p.log.Info("not making any change to host settings", - "ok", ok, "same", ironicHasSameImage) - } + op = nodes.ReplaceOp + p.log.Info("updating image_checksum") + } + updates = append( + updates, + nodes.UpdateOperation{ + Op: op, + Path: "/instance_info/image_checksum", + Value: checksum, + }, + ) - if op != "" { - _, err = nodes.Update( - p.client, - ironicNode.UUID, - nodes.UpdateOpts{ - nodes.UpdateOperation{ - Op: op, - Path: "/instance_info/image_source", - Value: p.host.Spec.Image.URL, - }, - nodes.UpdateOperation{ - Op: op, - Path: "/instance_info/image_checksum", - Value: checksum, - }, - // FIXME(dhellmann): We have to provide something for - // the disk size until - // https://storyboard.openstack.org/#!/story/2005165 - // is fixed in ironic. - nodes.UpdateOperation{ - Op: op, - Path: "/instance_info/root_gb", - Value: 10, - }, - // NOTE(dhellmann): We must fill in *some* value so - // that Ironic will monitor the host. We don't have a - // nova instance at all, so just give the node it's - // UUID again. - nodes.UpdateOperation{ - Op: op, - Path: "/instance_uuid", - Value: p.host.Status.Provisioning.ID, - }, - // FIXME(dhellmann): We need to specify the root - // device to receive the image. That should come from - // some combination of inspecting the host to see what - // is available and the hardware profile to give us - // instructions. - // nodes.UpdateOperation{ - // Op: nodes.AddOp, - // Path: "/properties/root_device", - // Value: map[string]interface{}, - // }, - }).Extract() - switch err.(type) { - case nil: - case gophercloud.ErrDefault409: - p.log.Info("could not update host settings in ironic, busy") - result.Dirty = true - return result, nil - default: - return result, errors.Wrap(err, "failed to update host settings in ironic") - } + // instance_uuid + // + // NOTE(dhellmann): We must fill in *some* value so that Ironic + // will monitor the host. We don't have a nova instance at all, so + // just give the node it's UUID again. + p.log.Info("setting instance_uuid") + updates = append( + updates, + nodes.UpdateOperation{ + Op: nodes.ReplaceOp, + Path: "/instance_uuid", + Value: p.host.Status.Provisioning.ID, + }, + ) + + // root_gb + // + // FIXME(dhellmann): We have to provide something for the disk + // size until https://storyboard.openstack.org/#!/story/2005165 is + // fixed in ironic. + if _, ok := ironicNode.InstanceInfo["root_gb"]; !ok { + op = nodes.AddOp + p.log.Info("adding root_gb") + } else if ironicNode.InstanceInfo["root_gb"] != 10 { + op = nodes.ReplaceOp + p.log.Info("updating root_gb") + } + updates = append( + updates, + nodes.UpdateOperation{ + Op: op, + Path: "/instance_info/root_gb", + Value: hwProf.RootGB, + }, + ) + + // root_device + // + // FIXME(dhellmann): We need to specify the root device to receive + // the image. That should come from some combination of inspecting + // the host to see what is available and the hardware profile to + // give us instructions. + if _, ok := ironicNode.Properties["root_device"]; !ok { + op = nodes.AddOp + p.log.Info("adding root_device") + } else { + op = nodes.ReplaceOp + p.log.Info("updating root_device") + } + hints := map[string]string{} + switch { + case hwProf.RootDeviceHints.DeviceName != "": + hints["name"] = hwProf.RootDeviceHints.DeviceName + case hwProf.RootDeviceHints.HCTL != "": + hints["hctl"] = hwProf.RootDeviceHints.HCTL + } + p.log.Info("using root device", "hints", hints) + updates = append( + updates, + nodes.UpdateOperation{ + Op: op, + Path: "/properties/root_device", + Value: hints, + }, + ) + + // cpu_arch + // + // FIXME(dhellmann): This should come from inspecting the + // host. + if _, ok := ironicNode.Properties["cpu_arch"]; !ok { + op = nodes.AddOp + p.log.Info("adding cpu_arch") + } else { + op = nodes.ReplaceOp + p.log.Info("updating cpu_arch") + } + updates = append( + updates, + nodes.UpdateOperation{ + Op: op, + Path: "/properties/cpu_arch", + Value: hwProf.CPUArch, + }, + ) + + // local_gb + if _, ok := ironicNode.Properties["local_gb"]; !ok { + op = nodes.AddOp + p.log.Info("adding local_gb") + } else { + op = nodes.ReplaceOp + p.log.Info("updating local_gb") + } + updates = append( + updates, + nodes.UpdateOperation{ + Op: op, + Path: "/properties/local_gb", + Value: hwProf.LocalGB, + }, + ) + + return updates, nil +} + +func (p *ironicProvisioner) startProvisioning(ironicNode *nodes.Node, checksum string, getUserData provisioner.UserDataSource) (result provisioner.Result, err error) { + + p.log.Info("starting provisioning") + + updates, err := p.getUpdateOptsForNode(ironicNode, checksum) + _, err = nodes.Update(p.client, ironicNode.UUID, updates).Extract() + switch err.(type) { + case nil: + case gophercloud.ErrDefault409: + p.log.Info("could not update host settings in ironic, busy") + result.Dirty = true + return result, nil + default: + return result, errors.Wrap(err, "failed to update host settings in ironic") } p.log.Info("validating host settings") @@ -597,7 +723,8 @@ func (p *ironicProvisioner) Provision(getUserData provisioner.UserDataSource) (r p.log.Info("checking image settings", "source", ironicNode.InstanceInfo["image_source"], "checksum", checksum, - "same", ironicHasSameImage) + "same", ironicHasSameImage, + "provisionState", ironicNode.ProvisionState) result.RequeueAfter = provisionRequeueDelay diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/provisioner/provisioner.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/provisioner/provisioner.go similarity index 90% rename from vendor/github.com/metalkube/baremetal-operator/pkg/provisioner/provisioner.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/provisioner/provisioner.go index 8fa6e76a1..d61743a95 100644 --- a/vendor/github.com/metalkube/baremetal-operator/pkg/provisioner/provisioner.go +++ b/vendor/github.com/metal3-io/baremetal-operator/pkg/provisioner/provisioner.go @@ -3,8 +3,8 @@ package provisioner import ( "time" - metalkubev1alpha1 "github.com/metalkube/baremetal-operator/pkg/apis/metalkube/v1alpha1" - "github.com/metalkube/baremetal-operator/pkg/bmc" + metal3v1alpha1 "github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1" + "github.com/metal3-io/baremetal-operator/pkg/bmc" ) /* @@ -16,7 +16,7 @@ Package provisioning defines the API for talking to the provisioning backend. type EventPublisher func(reason, message string) // Factory is the interface for creating new Provisioner objects. -type Factory func(host *metalkubev1alpha1.BareMetalHost, bmcCreds bmc.Credentials, publish EventPublisher) (Provisioner, error) +type Factory func(host *metal3v1alpha1.BareMetalHost, bmcCreds bmc.Credentials, publish EventPublisher) (Provisioner, error) // UserDataSource is the interface for a function to retrieve user // data for a host being provisioned. diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/utils/stringlist.go b/vendor/github.com/metal3-io/baremetal-operator/pkg/utils/stringlist.go similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/pkg/utils/stringlist.go rename to vendor/github.com/metal3-io/baremetal-operator/pkg/utils/stringlist.go diff --git a/vendor/github.com/metalkube/baremetal-operator/test/e2e/role_binding.yaml b/vendor/github.com/metal3-io/baremetal-operator/test/e2e/role_binding.yaml similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/test/e2e/role_binding.yaml rename to vendor/github.com/metal3-io/baremetal-operator/test/e2e/role_binding.yaml diff --git a/vendor/github.com/metal3-io/baremetal-operator/tools/clean_demo_hosts.sh b/vendor/github.com/metal3-io/baremetal-operator/tools/clean_demo_hosts.sh new file mode 100755 index 000000000..0e3334a36 --- /dev/null +++ b/vendor/github.com/metal3-io/baremetal-operator/tools/clean_demo_hosts.sh @@ -0,0 +1,3 @@ +#!/bin/bash -x + +oc delete baremetalhost -l metal3demo diff --git a/vendor/github.com/metalkube/baremetal-operator/tools/clean_host.sh b/vendor/github.com/metal3-io/baremetal-operator/tools/clean_host.sh similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/tools/clean_host.sh rename to vendor/github.com/metal3-io/baremetal-operator/tools/clean_host.sh diff --git a/vendor/github.com/metal3-io/baremetal-operator/tools/run_local_ironic.sh b/vendor/github.com/metal3-io/baremetal-operator/tools/run_local_ironic.sh new file mode 100755 index 000000000..9b25acef5 --- /dev/null +++ b/vendor/github.com/metal3-io/baremetal-operator/tools/run_local_ironic.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +set -ex + +IRONIC_IMAGE=${IRONIC_IMAGE:-"quay.io/metal3-io/ironic"} +IRONIC_INSPECTOR_IMAGE=${IRONIC_INSPECTOR_IMAGE:-"quay.io/metal3-io/ironic-inspector"} +IRONIC_DATA_DIR="$PWD/ironic" + +sudo podman pull $IRONIC_IMAGE +sudo podman pull $IRONIC_INSPECTOR_IMAGE + +mkdir -p "$IRONIC_DATA_DIR/html/images" +pushd $IRONIC_DATA_DIR/html/images + +# The images directory should contain images and an associated md5sum. +# - image.qcow2 +# - image.qcow2.md5sum + +for name in ironic ironic-inspector dnsmasq httpd mariadb; do + sudo podman ps | grep -w "$name$" && sudo podman kill $name + sudo podman ps --all | grep -w "$name$" && sudo podman rm $name -f +done + +# Remove existing pod +if sudo podman pod exists ironic-pod ; then + sudo podman pod rm ironic-pod -f +fi + +# set password for mariadb +mariadb_password=$(echo $(date;hostname)|sha256sum |cut -c-20) + +# Create pod +sudo podman pod create -n ironic-pod + +# Start dnsmasq, http, mariadb, and ironic containers using same image + +# See this file for env vars you can set, like IP, DHCP_RANGE, INTERFACE +# https://github.com/metal3-io/ironic/blob/master/rundnsmasq.sh +sudo podman run -d --net host --privileged --name dnsmasq --pod ironic-pod \ + -v $IRONIC_DATA_DIR:/shared --entrypoint /bin/rundnsmasq ${IRONIC_IMAGE} + +# For available env vars, see: +# https://github.com/metal3-io/ironic/blob/master/runhttpd.sh +sudo podman run -d --net host --privileged --name httpd --pod ironic-pod \ + -v $IRONIC_DATA_DIR:/shared --entrypoint /bin/runhttpd ${IRONIC_IMAGE} + +# https://github.com/metal3-io/ironic/blob/master/runmariadb.sh +sudo podman run -d --net host --privileged --name mariadb --pod ironic-pod \ + -v $IRONIC_DATA_DIR:/shared --entrypoint /bin/runmariadb \ + --env MARIADB_PASSWORD=$mariadb_password ${IRONIC_IMAGE} + +# See this file for additional env vars you may want to pass, like IP and INTERFACE +# https://github.com/metal3-io/ironic/blob/master/runironic.sh +sudo podman run -d --net host --privileged --name ironic --pod ironic-pod \ + --env MARIADB_PASSWORD=$mariadb_password \ + -v $IRONIC_DATA_DIR:/shared ${IRONIC_IMAGE} + +# Start Ironic Inspector +sudo podman run -d --net host --privileged --name ironic-inspector --pod ironic-pod "${IRONIC_INSPECTOR_IMAGE}" diff --git a/vendor/github.com/metalkube/baremetal-operator/tools/show_host_status.sh b/vendor/github.com/metal3-io/baremetal-operator/tools/show_host_status.sh similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/tools/show_host_status.sh rename to vendor/github.com/metal3-io/baremetal-operator/tools/show_host_status.sh diff --git a/vendor/github.com/metalkube/baremetal-operator/version/version.go b/vendor/github.com/metal3-io/baremetal-operator/version/version.go similarity index 100% rename from vendor/github.com/metalkube/baremetal-operator/version/version.go rename to vendor/github.com/metal3-io/baremetal-operator/version/version.go diff --git a/vendor/github.com/metalkube/baremetal-operator/docs/BaremetalHost_ProvisioningState.png b/vendor/github.com/metalkube/baremetal-operator/docs/BaremetalHost_ProvisioningState.png deleted file mode 100644 index 0611258f5934258fdb426eab8a04ba09094bfa19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 167653 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

gy4?)qTGSz+q)|_VNDH2y_8~Y~?x0kBM*JDoacE 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ʴ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 diff --git a/vendor/github.com/metalkube/baremetal-operator/docs/baremetalhost-states.md b/vendor/github.com/metalkube/baremetal-operator/docs/baremetalhost-states.md deleted file mode 100644 index a98432ec6..000000000 --- a/vendor/github.com/metalkube/baremetal-operator/docs/baremetalhost-states.md +++ /dev/null @@ -1,5 +0,0 @@ -# BaremetalHost Provisioning States - -The following diagram shows the possible Provisioning State transitions for the BaremetalHost object: - -![BaremetalHost ProvisioningState transitions](BaremetalHost_ProvisioningState.png) diff --git a/vendor/github.com/metalkube/baremetal-operator/pkg/bmc/credentials.go b/vendor/github.com/metalkube/baremetal-operator/pkg/bmc/credentials.go deleted file mode 100644 index e8360f274..000000000 --- a/vendor/github.com/metalkube/baremetal-operator/pkg/bmc/credentials.go +++ /dev/null @@ -1,37 +0,0 @@ -package bmc - -const ( - // MissingCredentialsMsg is returned as a validation failure - // reason when there are no credentials at all. - MissingCredentialsMsg string = "Missing BMC connection details: Credentials" - - // MissingAddressMsg is returned as a validation failure - // reason when there is no address for the BMC. - MissingAddressMsg string = "Missing BMC connection details: Address" - - // MissingUsernameMsg is returned as a validation failure reason - // when the credentials do not include a "username" field. - MissingUsernameMsg string = "Missing BMC connection details: 'username' in credentials" - - // MissingPasswordMsg is returned as a validation failure reason - // when the credentials do not include a "password" field. - MissingPasswordMsg string = "Missing BMC connection details: 'password' in credentials" -) - -// Credentials holds the information for authenticating with the BMC. -type Credentials struct { - Username string - Password string -} - -// AreValid returns a boolean indicating whether the credentials are -// valid, and if false a string explaining why not. -func (creds Credentials) AreValid() (bool, string) { - if creds.Username == "" { - return false, MissingUsernameMsg - } - if creds.Password == "" { - return false, MissingPasswordMsg - } - return true, "" -} diff --git a/vendor/github.com/metalkube/baremetal-operator/tools/clean_demo_hosts.sh b/vendor/github.com/metalkube/baremetal-operator/tools/clean_demo_hosts.sh deleted file mode 100755 index 0a7183057..000000000 --- a/vendor/github.com/metalkube/baremetal-operator/tools/clean_demo_hosts.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -x - -oc delete baremetalhost -l metalkubedemo From 0e43ed954daa4647d5a6bf90983d348e3c77553f Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Fri, 26 Apr 2019 13:31:21 -0400 Subject: [PATCH 03/14] handle discovered hosts in chooseHost A discovered host is not available but also does not have a machineRef set, so the pointer is nil. --- pkg/cloud/baremetal/actuators/machine/actuator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cloud/baremetal/actuators/machine/actuator.go b/pkg/cloud/baremetal/actuators/machine/actuator.go index c60bd7e4a..a8e84e42e 100644 --- a/pkg/cloud/baremetal/actuators/machine/actuator.go +++ b/pkg/cloud/baremetal/actuators/machine/actuator.go @@ -230,7 +230,7 @@ func (a *Actuator) chooseHost(ctx context.Context, machine *machinev1.Machine) ( for i, host := range hosts.Items { if host.Available() { availableHosts = append(availableHosts, &hosts.Items[i]) - } else if host.Spec.MachineRef.Name == machine.Name && host.Spec.MachineRef.Namespace == machine.Namespace { + } else if host.Spec.MachineRef != nil && host.Spec.MachineRef.Name == machine.Name && host.Spec.MachineRef.Namespace == machine.Namespace { log.Printf("found host %s with existing MachineRef", host.Name) return &hosts.Items[i], nil } From 7236b2b7b0ef33814e5283f085557f1a4997aa99 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Fri, 26 Apr 2019 13:51:46 -0400 Subject: [PATCH 04/14] add unit test to deal with unprovisioned host with error Discovered hosts are not ready and have an error message. Add a unit test with a host like that so we can ensure we don't have a problem ignoring them. --- .../actuators/machine/actuator_test.go | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pkg/cloud/baremetal/actuators/machine/actuator_test.go b/pkg/cloud/baremetal/actuators/machine/actuator_test.go index c7e060b2e..aaee0fd4b 100644 --- a/pkg/cloud/baremetal/actuators/machine/actuator_test.go +++ b/pkg/cloud/baremetal/actuators/machine/actuator_test.go @@ -55,6 +55,15 @@ func TestChooseHost(t *testing.T) { Namespace: "someotherns", }, } + discoveredHost := bmh.BareMetalHost{ + ObjectMeta: metav1.ObjectMeta{ + Name: "discoveredHost", + Namespace: "myns", + }, + Status: bmh.BareMetalHostStatus{ + ErrorMessage: "this host is discovered and not usable", + }, + } testCases := []struct { Machine machinev1.Machine @@ -72,6 +81,17 @@ func TestChooseHost(t *testing.T) { Hosts: []runtime.Object{&host2, &host1}, ExpectedHostName: host2.Name, }, + { + // should ignore discoveredHost and pick host2, which lacks a MachineRef + Machine: machinev1.Machine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "machine1", + Namespace: "myns", + }, + }, + Hosts: []runtime.Object{&discoveredHost, &host2, &host1}, + ExpectedHostName: host2.Name, + }, { // should pick host3, which already has a matching MachineRef Machine: machinev1.Machine{ From 448b65070e7b9591306dca61412360fdec09abcb Mon Sep 17 00:00:00 2001 From: Michael Hrivnak Date: Mon, 15 Apr 2019 17:03:41 -0400 Subject: [PATCH 05/14] Adds ProviderSpec with settings for image and userdata. The values passed in with the ProviderSpec were previously hard-coded. Any user of Machines or MachineSets with this controller will need to start including a suitable ProviderSpec. --- Gopkg.lock | 2 + README.md | 26 +++ ...v1alpha1_baremetalmachineproviderspec.yaml | 22 ++- ...alpha1_baremetalmachineproviderstatus.yaml | 4 - .../baremetalmachineproviderspec_types.go | 60 ++++-- ...baremetalmachineproviderspec_types_test.go | 105 ++++++++++- .../baremetalmachineproviderstatus_types.go | 18 -- .../v1alpha1/zz_generated.deepcopy.go | 67 ++----- .../baremetal/actuators/machine/actuator.go | 128 ++++++++++--- .../actuators/machine/actuator_test.go | 173 +++++++++++++++++- 10 files changed, 474 insertions(+), 131 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index fa72aedad..7a4be60ea 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -993,6 +993,7 @@ "k8s.io/code-generator/cmd/deepcopy-gen", "k8s.io/klog", "sigs.k8s.io/cluster-api/pkg/apis", + "sigs.k8s.io/cluster-api/pkg/apis/cluster/common", "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1", "sigs.k8s.io/cluster-api/pkg/controller/error", "sigs.k8s.io/cluster-api/pkg/controller/machine", @@ -1009,6 +1010,7 @@ "sigs.k8s.io/controller-runtime/pkg/source", "sigs.k8s.io/controller-tools/cmd/controller-gen", "sigs.k8s.io/testing_frameworks/integration", + "sigs.k8s.io/yaml", ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/README.md b/README.md index a5309309b..08b1e099a 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,29 @@ For more information about this actuator and related repositories, see * [Setting up for tests](docs/dev/setup.md) * Using [Minikube](docs/dev/minikube.md) * Using [OpenShift 4](docs/dev/openshift.md) + +## ProviderSpec + +In order to create a valid Machine resource, you must include a ProviderSpec +that looks like the following example. See the +[type definition](pkg/apis/baremetal/v1alpha1/baremetalmachineproviderspec_types.go) +for details on each field. + +``` +apiVersion: cluster.k8s.io/v1alpha1 +kind: Machine +metadata: + labels: + controller-tools.k8s.io: "1.0" + name: sample0 +spec: + providerSpec: + value: + apiVersion: "baremetal.cluster.k8s.io/v1alpha1" + kind: "BareMetalMachineProviderSpec" + image: + url: "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2" + checksum: "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2.md5sum" + userData: + Name: "worker-user-data" +``` diff --git a/config/crds/baremetal_v1alpha1_baremetalmachineproviderspec.yaml b/config/crds/baremetal_v1alpha1_baremetalmachineproviderspec.yaml index 272ede197..b4639fc2a 100644 --- a/config/crds/baremetal_v1alpha1_baremetalmachineproviderspec.yaml +++ b/config/crds/baremetal_v1alpha1_baremetalmachineproviderspec.yaml @@ -19,6 +19,19 @@ spec: of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' type: string + image: + description: Image is the image to be provisioned. + properties: + checksum: + description: Checksum is a md5sum value or a URL to retrieve one. + type: string + url: + description: URL is a location of an image to deploy. + type: string + required: + - url + - checksum + type: object kind: description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client @@ -26,10 +39,13 @@ spec: type: string metadata: type: object - spec: - type: object - status: + userData: + description: UserData references the Secret that holds user data needed + by the bare metal operator. The Namespace is optional; it will default + to the Machine's namespace if not specified. type: object + required: + - image version: v1alpha1 status: acceptedNames: diff --git a/config/crds/baremetal_v1alpha1_baremetalmachineproviderstatus.yaml b/config/crds/baremetal_v1alpha1_baremetalmachineproviderstatus.yaml index 714223705..336153fa2 100644 --- a/config/crds/baremetal_v1alpha1_baremetalmachineproviderstatus.yaml +++ b/config/crds/baremetal_v1alpha1_baremetalmachineproviderstatus.yaml @@ -26,10 +26,6 @@ spec: type: string metadata: type: object - spec: - type: object - status: - type: object version: v1alpha1 status: acceptedNames: diff --git a/pkg/apis/baremetal/v1alpha1/baremetalmachineproviderspec_types.go b/pkg/apis/baremetal/v1alpha1/baremetalmachineproviderspec_types.go index 391c0477b..825af9573 100644 --- a/pkg/apis/baremetal/v1alpha1/baremetalmachineproviderspec_types.go +++ b/pkg/apis/baremetal/v1alpha1/baremetalmachineproviderspec_types.go @@ -17,35 +17,59 @@ limitations under the License. package v1alpha1 import ( + "fmt" + + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - -// BareMetalMachineProviderSpecSpec defines the desired state of BareMetalMachineProviderSpec -type BareMetalMachineProviderSpecSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file -} - -// BareMetalMachineProviderSpecStatus defines the observed state of BareMetalMachineProviderSpec -type BareMetalMachineProviderSpecStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file -} - // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// BareMetalMachineProviderSpec is the Schema for the baremetalmachineproviderspecs API +// BareMetalMachineProviderSpec holds data that the actuator needs to provision +// and manage a Machine. // +k8s:openapi-gen=true type BareMetalMachineProviderSpec struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec BareMetalMachineProviderSpecSpec `json:"spec,omitempty"` - Status BareMetalMachineProviderSpecStatus `json:"status,omitempty"` + // Image is the image to be provisioned. + Image Image `json:"image"` + + // UserData references the Secret that holds user data needed by the bare metal + // operator. The Namespace is optional; it will default to the Machine's + // namespace if not specified. + UserData *corev1.SecretReference `json:"userData,omitempty"` +} + +// Image holds the details of an image to use during provisioning. +type Image struct { + // URL is a location of an image to deploy. + URL string `json:"url"` + + // Checksum is a md5sum value or a URL to retrieve one. + Checksum string `json:"checksum"` +} + +// IsValid returns an error if the object is not valid, otherwise nil. The +// string representation of the error is suitable for human consumption. +func (s *BareMetalMachineProviderSpec) IsValid() error { + missing := []string{} + if s.Image.URL == "" { + missing = append(missing, "Image.URL") + } + if s.Image.Checksum == "" { + missing = append(missing, "Image.Checksum") + } + if s.UserData == nil { + missing = append(missing, "UserData") + } else if s.UserData.Name == "" { + missing = append(missing, "UserData.Name") + } + if len(missing) > 0 { + return fmt.Errorf("Missing fields from ProviderSpec: %v", missing) + } + return nil } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/baremetal/v1alpha1/baremetalmachineproviderspec_types_test.go b/pkg/apis/baremetal/v1alpha1/baremetalmachineproviderspec_types_test.go index 53e8a2957..52a0ef3b0 100644 --- a/pkg/apis/baremetal/v1alpha1/baremetalmachineproviderspec_types_test.go +++ b/pkg/apis/baremetal/v1alpha1/baremetalmachineproviderspec_types_test.go @@ -21,10 +21,109 @@ import ( "github.com/onsi/gomega" "golang.org/x/net/context" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ) +func TestProviderSpecIsValid(t *testing.T) { + cases := []struct { + Spec BareMetalMachineProviderSpec + ErrorExpected bool + Name string + }{ + { + Spec: BareMetalMachineProviderSpec{}, + ErrorExpected: true, + Name: "empty spec", + }, + { + Spec: BareMetalMachineProviderSpec{ + Image: Image{ + URL: "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2", + Checksum: "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2.md5sum", + }, + UserData: &corev1.SecretReference{ + Name: "worker-user-data", + }, + }, + ErrorExpected: false, + Name: "Valid spec without UserData.Namespace", + }, + { + Spec: BareMetalMachineProviderSpec{ + Image: Image{ + URL: "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2", + Checksum: "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2.md5sum", + }, + UserData: &corev1.SecretReference{ + Name: "worker-user-data", + Namespace: "otherns", + }, + }, + ErrorExpected: false, + Name: "Valid spec with UserData.Namespace", + }, + { + Spec: BareMetalMachineProviderSpec{ + Image: Image{ + Checksum: "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2.md5sum", + }, + UserData: &corev1.SecretReference{ + Name: "worker-user-data", + }, + }, + ErrorExpected: true, + Name: "missing Image.URL", + }, + { + Spec: BareMetalMachineProviderSpec{ + Image: Image{ + URL: "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2", + }, + UserData: &corev1.SecretReference{ + Name: "worker-user-data", + }, + }, + ErrorExpected: true, + Name: "missing Image.Checksum", + }, + { + Spec: BareMetalMachineProviderSpec{ + Image: Image{ + URL: "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2", + Checksum: "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2.md5sum", + }, + }, + ErrorExpected: true, + Name: "missing UserData", + }, + { + Spec: BareMetalMachineProviderSpec{ + Image: Image{ + URL: "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2", + Checksum: "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2.md5sum", + }, + UserData: &corev1.SecretReference{ + Namespace: "otherns", + }, + }, + ErrorExpected: true, + Name: "missing UserData.Name", + }, + } + + for _, tc := range cases { + err := tc.Spec.IsValid() + if tc.ErrorExpected && err == nil { + t.Errorf("Did not get error from case \"%v\"", tc.Name) + } + if !tc.ErrorExpected && err != nil { + t.Errorf("Got unexpected error from case \"%v\": %v", tc.Name, err) + } + } +} + func TestStorageBareMetalMachineProviderSpec(t *testing.T) { key := types.NamespacedName{ Name: "foo", @@ -34,7 +133,11 @@ func TestStorageBareMetalMachineProviderSpec(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "foo", Namespace: "default", - }} + }, + UserData: &corev1.SecretReference{ + Name: "foo", + }, + } g := gomega.NewGomegaWithT(t) // Test Create diff --git a/pkg/apis/baremetal/v1alpha1/baremetalmachineproviderstatus_types.go b/pkg/apis/baremetal/v1alpha1/baremetalmachineproviderstatus_types.go index 1fc46c26b..122ba5f7b 100644 --- a/pkg/apis/baremetal/v1alpha1/baremetalmachineproviderstatus_types.go +++ b/pkg/apis/baremetal/v1alpha1/baremetalmachineproviderstatus_types.go @@ -20,21 +20,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - -// BareMetalMachineProviderStatusSpec defines the desired state of BareMetalMachineProviderStatus -type BareMetalMachineProviderStatusSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file -} - -// BareMetalMachineProviderStatusStatus defines the observed state of BareMetalMachineProviderStatus -type BareMetalMachineProviderStatusStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file -} - // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -43,9 +28,6 @@ type BareMetalMachineProviderStatusStatus struct { type BareMetalMachineProviderStatus struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec BareMetalMachineProviderStatusSpec `json:"spec,omitempty"` - Status BareMetalMachineProviderStatusStatus `json:"status,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/baremetal/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/baremetal/v1alpha1/zz_generated.deepcopy.go index f481f2b4d..5e1ab76a6 100644 --- a/pkg/apis/baremetal/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/baremetal/v1alpha1/zz_generated.deepcopy.go @@ -20,6 +20,7 @@ limitations under the License. package v1alpha1 import ( + v1 "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -28,8 +29,12 @@ func (in *BareMetalMachineProviderSpec) DeepCopyInto(out *BareMetalMachineProvid *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status + out.Image = in.Image + if in.UserData != nil { + in, out := &in.UserData, &out.UserData + *out = new(v1.SecretReference) + **out = **in + } return } @@ -84,45 +89,11 @@ func (in *BareMetalMachineProviderSpecList) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BareMetalMachineProviderSpecSpec) DeepCopyInto(out *BareMetalMachineProviderSpecSpec) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BareMetalMachineProviderSpecSpec. -func (in *BareMetalMachineProviderSpecSpec) DeepCopy() *BareMetalMachineProviderSpecSpec { - if in == nil { - return nil - } - out := new(BareMetalMachineProviderSpecSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BareMetalMachineProviderSpecStatus) DeepCopyInto(out *BareMetalMachineProviderSpecStatus) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BareMetalMachineProviderSpecStatus. -func (in *BareMetalMachineProviderSpecStatus) DeepCopy() *BareMetalMachineProviderSpecStatus { - if in == nil { - return nil - } - out := new(BareMetalMachineProviderSpecStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BareMetalMachineProviderStatus) DeepCopyInto(out *BareMetalMachineProviderStatus) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status return } @@ -178,33 +149,17 @@ func (in *BareMetalMachineProviderStatusList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BareMetalMachineProviderStatusSpec) DeepCopyInto(out *BareMetalMachineProviderStatusSpec) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BareMetalMachineProviderStatusSpec. -func (in *BareMetalMachineProviderStatusSpec) DeepCopy() *BareMetalMachineProviderStatusSpec { - if in == nil { - return nil - } - out := new(BareMetalMachineProviderStatusSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BareMetalMachineProviderStatusStatus) DeepCopyInto(out *BareMetalMachineProviderStatusStatus) { +func (in *Image) DeepCopyInto(out *Image) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BareMetalMachineProviderStatusStatus. -func (in *BareMetalMachineProviderStatusStatus) DeepCopy() *BareMetalMachineProviderStatusStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Image. +func (in *Image) DeepCopy() *Image { if in == nil { return nil } - out := new(BareMetalMachineProviderStatusStatus) + out := new(Image) in.DeepCopyInto(out) return out } diff --git a/pkg/cloud/baremetal/actuators/machine/actuator.go b/pkg/cloud/baremetal/actuators/machine/actuator.go index a8e84e42e..28068fb82 100644 --- a/pkg/cloud/baremetal/actuators/machine/actuator.go +++ b/pkg/cloud/baremetal/actuators/machine/actuator.go @@ -24,12 +24,15 @@ import ( "time" bmh "github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1" + bmv1alpha1 "github.com/metal3-io/cluster-api-provider-baremetal/pkg/apis/baremetal/v1alpha1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/client-go/tools/cache" + "sigs.k8s.io/cluster-api/pkg/apis/cluster/common" machinev1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" clustererror "sigs.k8s.io/cluster-api/pkg/controller/error" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/yaml" ) const ( @@ -37,12 +40,7 @@ const ( // HostAnnotation is the key for an annotation that should go on a Machine to // reference what BareMetalHost it corresponds to. HostAnnotation = "metal3.io/BareMetalHost" - // FIXME(dhellmann): These image values should probably come from - // configuration settings and something that can tell the IP - // address of the web server hosting the image in the ironic pod. - instanceImageSource = "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2" - instanceImageChecksumURL = instanceImageSource + ".md5sum" - requeueAfter = time.Second * 30 + requeueAfter = time.Second * 30 ) // Add RBAC rules to access cluster-api resources @@ -70,6 +68,27 @@ func NewActuator(params ActuatorParams) (*Actuator, error) { // Create creates a machine and is invoked by the Machine Controller func (a *Actuator) Create(ctx context.Context, cluster *machinev1.Cluster, machine *machinev1.Machine) error { log.Printf("Creating machine %v .", machine.Name) + + // load and validate the config + if machine.Spec.ProviderSpec.Value == nil { + return a.setError(ctx, machine, "ProviderSpec is missing") + } + config, err := configFromProviderSpec(machine.Spec.ProviderSpec) + if err != nil { + log.Printf("Error reading ProviderSpec for machine %s: %s", machine.Name, err.Error()) + return err + } + err = config.IsValid() + if err != nil { + return a.setError(ctx, machine, err.Error()) + } + + // clear an error if one was previously set + err = a.clearError(ctx, machine) + if err != nil { + return err + } + // look for associated BMH host, err := a.getHost(ctx, machine) if err != nil { @@ -91,6 +110,11 @@ func (a *Actuator) Create(ctx context.Context, cluster *machinev1.Cluster, machi log.Printf("Machine %s already associated with host %s", machine.Name, host.Name) } + err = a.setHostSpec(ctx, host, machine, config) + if err != nil { + return err + } + err = a.ensureAnnotation(ctx, machine, host) if err != nil { return err @@ -127,6 +151,14 @@ func (a *Actuator) Delete(ctx context.Context, cluster *machinev1.Cluster, machi // Update updates a machine and is invoked by the Machine Controller func (a *Actuator) Update(ctx context.Context, cluster *machinev1.Cluster, machine *machinev1.Machine) error { log.Printf("Updating machine %v .", machine.Name) + + // clear any error message that was previously set. This method doesn't set + // error messages yet, so we know that it's incorrect to have one here. + err := a.clearError(ctx, machine) + if err != nil { + return err + } + host, err := a.getHost(ctx, machine) if err != nil { return err @@ -209,9 +241,7 @@ func (a *Actuator) getHost(ctx context.Context, machine *machinev1.Machine) (*bm // chooseHost iterates through known hosts and returns one that can be // associated with the machine. It searches all hosts in case one already has an -// association with this machine. It will add a Machine reference and update the -// host via the kube API before returning the host. Returns nil if a host is not -// available. +// association with this machine. func (a *Actuator) chooseHost(ctx context.Context, machine *machinev1.Machine) (*bmh.BareMetalHost, error) { // get list of BMH hosts := bmh.BareMetalHostList{} @@ -243,29 +273,31 @@ func (a *Actuator) chooseHost(ctx context.Context, machine *machinev1.Machine) ( // choose a host at random from available hosts rand.Seed(time.Now().Unix()) chosenHost := availableHosts[rand.Intn(len(availableHosts))] - chosenHost.Spec.MachineRef = &corev1.ObjectReference{ + + return chosenHost, nil +} + +// setHostSpec will ensure the host's Spec is set according to the machine's +// details. It will then update the host via the kube API. If UserData does not +// include a Namespace, it will default to the Machine's namespace. +func (a *Actuator) setHostSpec(ctx context.Context, host *bmh.BareMetalHost, machine *machinev1.Machine, + config *bmv1alpha1.BareMetalMachineProviderSpec) error { + + host.Spec.MachineRef = &corev1.ObjectReference{ Name: machine.Name, Namespace: machine.Namespace, } - // FIXME(dhellmann): When we stop using the consts for these - // settings, we need to pass the right values. - chosenHost.Spec.Image = &bmh.Image{ - URL: instanceImageSource, - Checksum: instanceImageChecksumURL, - } - chosenHost.Spec.Online = true - chosenHost.Spec.UserData = &corev1.SecretReference{ - Namespace: machine.Namespace, // is it safe to assume the same namespace? - // FIXME(dhellmann): Is this name openshift-specific? - Name: "worker-user-data", + host.Spec.Image = &bmh.Image{ + URL: config.Image.URL, + Checksum: config.Image.Checksum, } - err = a.client.Update(ctx, chosenHost) - if err != nil { - return nil, err + host.Spec.Online = true + host.Spec.UserData = config.UserData + if host.Spec.UserData.Namespace == "" { + host.Spec.UserData.Namespace = machine.Namespace } - - return chosenHost, nil + return a.client.Update(ctx, host) } // ensureAnnotation makes sure the machine has an annotation that references the @@ -291,3 +323,47 @@ func (a *Actuator) ensureAnnotation(ctx context.Context, machine *machinev1.Mach machine.ObjectMeta.SetAnnotations(annotations) return a.client.Update(ctx, machine) } + +// setError sets the ErrorMessage and ErrorReason fields on the machine and logs +// the message. It assumes the reason is invalid configuration, since that is +// currently the only relevant MachineStatusError choice. +func (a *Actuator) setError(ctx context.Context, machine *machinev1.Machine, message string) error { + machine.Status.ErrorMessage = &message + reason := common.InvalidConfigurationMachineError + machine.Status.ErrorReason = &reason + log.Printf("Machine %s: %s", machine.Name, message) + return a.client.Status().Update(ctx, machine) +} + +// clearError removes the ErrorMessage from the machine's Status if set. Returns +// nil if ErrorMessage was already nil. Returns a RequeueAfterError if the +// machine was updated. +func (a *Actuator) clearError(ctx context.Context, machine *machinev1.Machine) error { + if machine.Status.ErrorMessage != nil || machine.Status.ErrorReason != nil { + machine.Status.ErrorMessage = nil + machine.Status.ErrorReason = nil + err := a.client.Status().Update(ctx, machine) + if err != nil { + return err + } + log.Printf("Cleared error message from machine %s", machine.Name) + return &clustererror.RequeueAfterError{} + } + return nil +} + +// configFromProviderSpec returns a BareMetalMachineProviderSpec by +// deserializing the contents of a ProviderSpec +func configFromProviderSpec(providerSpec machinev1.ProviderSpec) (*bmv1alpha1.BareMetalMachineProviderSpec, error) { + if providerSpec.Value == nil { + return nil, fmt.Errorf("ProviderSpec missing") + } + + var config bmv1alpha1.BareMetalMachineProviderSpec + err := yaml.UnmarshalStrict(providerSpec.Value.Raw, &config) + if err != nil { + return nil, err + } + + return &config, nil +} diff --git a/pkg/cloud/baremetal/actuators/machine/actuator_test.go b/pkg/cloud/baremetal/actuators/machine/actuator_test.go index aaee0fd4b..11ffbda92 100644 --- a/pkg/cloud/baremetal/actuators/machine/actuator_test.go +++ b/pkg/cloud/baremetal/actuators/machine/actuator_test.go @@ -6,6 +6,7 @@ import ( bmoapis "github.com/metal3-io/baremetal-operator/pkg/apis" bmh "github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1" + bmv1alpha1 "github.com/metal3-io/cluster-api-provider-baremetal/pkg/apis/baremetal/v1alpha1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -13,6 +14,14 @@ import ( machinev1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/yaml" +) + +const ( + testImageURL = "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2" + testImageChecksumURL = "http://172.22.0.1/images/rhcos-ootpa-latest.qcow2.md5sum" + testUserDataSecretName = "worker-user-data" + testUserDataSecretNamespace = "myns" ) func TestChooseHost(t *testing.T) { @@ -65,6 +74,8 @@ func TestChooseHost(t *testing.T) { }, } + _, providerSpec := newConfig(t, "") + testCases := []struct { Machine machinev1.Machine Hosts []runtime.Object @@ -77,6 +88,9 @@ func TestChooseHost(t *testing.T) { Name: "machine1", Namespace: "myns", }, + Spec: machinev1.MachineSpec{ + ProviderSpec: providerSpec, + }, }, Hosts: []runtime.Object{&host2, &host1}, ExpectedHostName: host2.Name, @@ -99,6 +113,9 @@ func TestChooseHost(t *testing.T) { Name: "machine1", Namespace: "myns", }, + Spec: machinev1.MachineSpec{ + ProviderSpec: providerSpec, + }, }, Hosts: []runtime.Object{&host1, &host3, &host2}, ExpectedHostName: host3.Name, @@ -111,6 +128,9 @@ func TestChooseHost(t *testing.T) { Name: "machine2", Namespace: "myns", }, + Spec: machinev1.MachineSpec{ + ProviderSpec: providerSpec, + }, }, Hosts: []runtime.Object{&host1, &host3, &host4}, ExpectedHostName: "", @@ -136,20 +156,108 @@ func TestChooseHost(t *testing.T) { } if err != nil { t.Errorf("%v", err) - } - if result.Spec.MachineRef.Name != tc.Machine.Name { - t.Errorf("found machine ref %v", result.Spec.MachineRef) + return } if result.Name != tc.ExpectedHostName { t.Errorf("host %s chosen instead of %s", result.Name, tc.ExpectedHostName) } + } +} + +func TestSetHostSpec(t *testing.T) { + for _, tc := range []struct { + UserDataNamespace string + ExpectedUserDataNamespace string + }{ + { + UserDataNamespace: "otherns", + ExpectedUserDataNamespace: "otherns", + }, + { + UserDataNamespace: "", + ExpectedUserDataNamespace: "myns", + }, + } { + + // test data + config, providerSpec := newConfig(t, tc.UserDataNamespace) + host := bmh.BareMetalHost{ + ObjectMeta: metav1.ObjectMeta{ + Name: "host2", + Namespace: "myns", + }, + } + machine := machinev1.Machine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "machine1", + Namespace: "myns", + }, + Spec: machinev1.MachineSpec{ + ProviderSpec: providerSpec, + }, + } + + // test setup + scheme := runtime.NewScheme() + bmoapis.AddToScheme(scheme) + c := fakeclient.NewFakeClientWithScheme(scheme, &host) + + actuator, err := NewActuator(ActuatorParams{ + Client: c, + }) + if err != nil { + t.Errorf("%v", err) + return + } + + // run the function + err = actuator.setHostSpec(context.TODO(), &host, &machine, config) + if err != nil { + t.Errorf("%v", err) + return + } + + // get the saved result savedHost := bmh.BareMetalHost{} - err = c.Get(context.TODO(), client.ObjectKey{Name: result.Name, Namespace: result.Namespace}, &savedHost) + err = c.Get(context.TODO(), client.ObjectKey{Name: host.Name, Namespace: host.Namespace}, &savedHost) if err != nil { t.Errorf("%v", err) + return } + + // validate the result if savedHost.Spec.MachineRef == nil { - t.Errorf("machine ref %v not saved to host", result.Spec.MachineRef) + t.Errorf("MachineRef not set") + return + } + if savedHost.Spec.MachineRef.Name != machine.Name { + t.Errorf("found machine ref %v", savedHost.Spec.MachineRef) + } + if savedHost.Spec.MachineRef.Namespace != machine.Namespace { + t.Errorf("found machine ref %v", savedHost.Spec.MachineRef) + } + if savedHost.Spec.Online != true { + t.Errorf("host not set to Online") + } + if savedHost.Spec.Image == nil { + t.Errorf("Image not set") + return + } + if savedHost.Spec.Image.URL != testImageURL { + t.Errorf("expected ImageURL %s, got %s", testImageURL, savedHost.Spec.Image.URL) + } + if savedHost.Spec.Image.Checksum != testImageChecksumURL { + t.Errorf("expected ImageChecksumURL %s, got %s", testImageChecksumURL, savedHost.Spec.Image.Checksum) + } + if savedHost.Spec.UserData == nil { + t.Errorf("UserData not set") + return + } + if savedHost.Spec.UserData.Namespace != tc.ExpectedUserDataNamespace { + t.Errorf("expected Userdata.Namespace %s, got %s", tc.ExpectedUserDataNamespace, savedHost.Spec.UserData.Namespace) + } + if savedHost.Spec.UserData.Name != testUserDataSecretName { + t.Errorf("expected Userdata.Name %s, got %s", testUserDataSecretName, savedHost.Spec.UserData.Name) } } } @@ -521,3 +629,58 @@ func TestDelete(t *testing.T) { } } } + +func TestConfigFromProviderSpec(t *testing.T) { + ps := machinev1.ProviderSpec{ + Value: &runtime.RawExtension{ + Raw: []byte(`{"apiVersion":"baremetal.cluster.k8s.io/v1alpha1","userData":{"Name":"worker-user-data","Namespace":"myns"},"image":{"Checksum":"http://172.22.0.1/images/rhcos-ootpa-latest.qcow2.md5sum","URL":"http://172.22.0.1/images/rhcos-ootpa-latest.qcow2"},"kind":"BareMetalMachineProviderSpec"}`), + }, + } + config, err := configFromProviderSpec(ps) + if err != nil { + t.Errorf("Error: %s", err.Error()) + return + } + if config == nil { + t.Errorf("got a nil config") + return + } + + if config.Image.URL != testImageURL { + t.Errorf("expected Image.URL %s, got %s", testImageURL, config.Image.URL) + } + if config.Image.Checksum != testImageChecksumURL { + t.Errorf("expected Image.Checksum %s, got %s", testImageChecksumURL, config.Image.Checksum) + } + if config.UserData == nil { + t.Errorf("UserData not set") + return + } + if config.UserData.Name != testUserDataSecretName { + t.Errorf("expected UserData.Name %s, got %s", testUserDataSecretName, config.UserData.Name) + } + if config.UserData.Namespace != testUserDataSecretNamespace { + t.Errorf("expected UserData.Namespace %s, got %s", testUserDataSecretNamespace, config.UserData.Namespace) + } +} + +func newConfig(t *testing.T, UserDataNamespace string) (*bmv1alpha1.BareMetalMachineProviderSpec, machinev1.ProviderSpec) { + config := bmv1alpha1.BareMetalMachineProviderSpec{ + Image: bmv1alpha1.Image{ + URL: testImageURL, + Checksum: testImageChecksumURL, + }, + UserData: &corev1.SecretReference{ + Name: testUserDataSecretName, + Namespace: UserDataNamespace, + }, + } + out, err := yaml.Marshal(&config) + if err != nil { + t.Logf("could not marshal BareMetalMachineProviderSpec: %v", err) + t.FailNow() + } + return &config, machinev1.ProviderSpec{ + Value: &runtime.RawExtension{Raw: out}, + } +} From 8d426fc54d8601a4d75e60c2ff3fd2b21233e101 Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Tue, 7 May 2019 16:48:27 -0400 Subject: [PATCH 06/14] Use the metal3 namespace by default. This matches what the baremetal-operator uses by default, as well. --- config/kustomization.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/kustomization.yaml b/config/kustomization.yaml index a00f2f7c5..35f202103 100644 --- a/config/kustomization.yaml +++ b/config/kustomization.yaml @@ -1,5 +1,5 @@ # Adds namespace to all resources. -namespace: cluster-api-provider-baremetal-system +namespace: metal3 # Value of this field is prepended to the # names of all resources, e.g. a deployment named From 59693a493bc5a779ff0d039f13007f2983e0df10 Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Tue, 7 May 2019 17:05:49 -0400 Subject: [PATCH 07/14] Use metal3 namepsace for cluster-api components. Use the metal3 namespace for the components built from the cluster-api repository, as well. --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c49dba465..88ec50f5e 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,8 @@ manifests: go run vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go all kustomize build config/ > provider-components.yaml echo "---" >> provider-components.yaml - cd vendor && kustomize build sigs.k8s.io/cluster-api/config/default/ >> ../provider-components.yaml + cd vendor && kustomize build sigs.k8s.io/cluster-api/config/default/ | \ + sed -e 's/namespace: cluster-api-system/namespace: metal3/' >> ../provider-components.yaml # Run go fmt against code fmt: From e1322cf7d314cff005d54c695a0081d89d58fbd6 Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Thu, 9 May 2019 11:22:57 -0400 Subject: [PATCH 08/14] Shorten resource name. cluster-api-provider-baremetal-controller-manager-metrics-service is too long. s/service/svc/ gets it under the limit. --- config/rbac/auth_proxy_service.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/rbac/auth_proxy_service.yaml b/config/rbac/auth_proxy_service.yaml index 027073f95..a7fc14ddc 100644 --- a/config/rbac/auth_proxy_service.yaml +++ b/config/rbac/auth_proxy_service.yaml @@ -8,7 +8,7 @@ metadata: labels: control-plane: controller-manager controller-tools.k8s.io: "1.0" - name: controller-manager-metrics-service + name: controller-manager-metrics-svc namespace: system spec: ports: From bf0e3c30af3243c0e4f7e1d9a465890fa1d46f2b Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Thu, 9 May 2019 15:44:29 -0400 Subject: [PATCH 09/14] manager_image_patch.yaml: Set image name. --- config/default/manager_image_patch.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config/default/manager_image_patch.yaml b/config/default/manager_image_patch.yaml index fcbf39dc6..a093e3a71 100644 --- a/config/default/manager_image_patch.yaml +++ b/config/default/manager_image_patch.yaml @@ -7,6 +7,5 @@ spec: template: spec: containers: - # Change the value of image field below to your controller image URL - - image: IMAGE_URL + - image: quay.io/metal3-io/cluster-api-provider-baremetal:master name: manager From 509c5cb1c42f42587af0ea8d5e1b19f742884789 Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Fri, 10 May 2019 13:21:03 -0400 Subject: [PATCH 10/14] manager.yaml: Update command to match our image. Update to match the name of the binary in the container image we build. --- config/manager/manager.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index b718bb4ec..401bb08a3 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -43,7 +43,7 @@ spec: spec: containers: - command: - - /manager + - /machine-controller-manager image: controller:latest imagePullPolicy: Always name: manager From da85d71daf6da26a5bf89ca1d0ac30c3cb3ea05c Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Fri, 10 May 2019 13:36:37 -0400 Subject: [PATCH 11/14] Add RBAC for accessing BareMetalHost objects. --- config/rbac/rbac_role.yaml | 10 ++++++++++ pkg/cloud/baremetal/actuators/machine/actuator.go | 3 +++ 2 files changed, 13 insertions(+) diff --git a/config/rbac/rbac_role.yaml b/config/rbac/rbac_role.yaml index 0766795cf..39422b64a 100644 --- a/config/rbac/rbac_role.yaml +++ b/config/rbac/rbac_role.yaml @@ -44,6 +44,16 @@ rules: - update - patch - delete +- apiGroups: + - metal3.io + resources: + - baremetalhosts + verbs: + - get + - list + - watch + - update + - patch - apiGroups: - baremetal.k8s.io resources: diff --git a/pkg/cloud/baremetal/actuators/machine/actuator.go b/pkg/cloud/baremetal/actuators/machine/actuator.go index 28068fb82..c35802315 100644 --- a/pkg/cloud/baremetal/actuators/machine/actuator.go +++ b/pkg/cloud/baremetal/actuators/machine/actuator.go @@ -48,6 +48,9 @@ const ( //+kubebuilder:rbac:groups=cluster.k8s.io,resources=clusters;clusters/status,verbs=get;list;watch //+kubebuilder:rbac:groups="",resources=nodes;events,verbs=get;list;watch;create;update;patch;delete +// RBAC to access BareMetalHost resources from metal3.io +//+kubebuilder:rbac:groups=metal3.io,resources=baremetalhosts,verbs=get;list;watch;update;patch + // Actuator is responsible for performing machine reconciliation type Actuator struct { client client.Client From 5f95d6442dca53566ffd0c308995ebd6906965a7 Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Sat, 11 May 2019 16:33:12 -0400 Subject: [PATCH 12/14] Fix ProviderName to be "baremetal". This constant came from the sample provider implementation in cluster-api docs. It was missed when changing the sample to use "baremetal" everywhere instead of the sample "solas" provider. --- pkg/cloud/baremetal/actuators/machine/actuator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cloud/baremetal/actuators/machine/actuator.go b/pkg/cloud/baremetal/actuators/machine/actuator.go index c35802315..cc61beaa9 100644 --- a/pkg/cloud/baremetal/actuators/machine/actuator.go +++ b/pkg/cloud/baremetal/actuators/machine/actuator.go @@ -36,7 +36,7 @@ import ( ) const ( - ProviderName = "solas" + ProviderName = "baremetal" // HostAnnotation is the key for an annotation that should go on a Machine to // reference what BareMetalHost it corresponds to. HostAnnotation = "metal3.io/BareMetalHost" From 98138a3deabfa106ca8aa4c19eadd95ddf6be6e8 Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Mon, 13 May 2019 10:27:08 -0400 Subject: [PATCH 13/14] Add API docs. --- docs/api.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 docs/api.md diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 000000000..a6ff31ea8 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,72 @@ +# API and Resource Definitions + +## Machine + +The `Machine` resource is defined by the +[cluster-api](https://github.com/kubernetes-sigs/cluster-api) project. A +`Machine` includes a `providerSpec` field which includes the data specific to +this `cluster-api` provider. + +## BareMetalMachineProviderSpec + +* **image** -- This includes two sub-fields, `url` and `checksum`, which + include the URL to the image and the URL to a checksum for that image. These + fields are required. The image will be used for provisioning of the + `BareMetalHost` chosen by the `Machine` actuator. + +* **userData** -- This includes two sub-fields, `name` and `namespace`, which + reference a `Secret` that contains base64 encoded user-data to be written to + a config drive on the provisioned `BareMetalHost`. This field is optional. + +## Sample Machine + +```yaml +apiVersion: cluster.k8s.io/v1alpha1 +kind: Machine +metadata: + annotations: + metal3.io/BareMetalHost: metal3/master-0 + creationTimestamp: "2019-05-13T13:00:51Z" + finalizers: + - machine.cluster.k8s.io + generateName: baremetal-machine- + generation: 2 + name: centos + namespace: metal3 + resourceVersion: "1112" + selfLink: /apis/cluster.k8s.io/v1alpha1/namespaces/metal3/machines/centos + uid: 22acee54-757f-11e9-8091-280d3563c053 +spec: + metadata: + creationTimestamp: null + providerSpec: + value: + apiVersion: baremetal.cluster.k8s.io/v1alpha1 + image: + checksum: http://172.22.0.1/images/CentOS-7-x86_64-GenericCloud-1901.qcow2.md5sum + url: http://172.22.0.1/images/CentOS-7-x86_64-GenericCloud-1901.qcow2 + kind: BareMetalMachineProviderSpec + userData: + name: centos-user-data + namespace: metal3 + versions: + kubelet: "" +``` + +## Sample userData Secret + +```yaml +apiVersion: v1 +data: + userData: BASE64_ENCODED_USER_DATA +kind: Secret +metadata: + annotations: + creationTimestamp: 2019-05-13T13:00:51Z + name: centos-user-data + namespace: metal3 + resourceVersion: "1108" + selfLink: /api/v1/namespaces/metal3/secrets/centos-user-data + uid: 22792b3e-757f-11e9-8091-280d3563c053 +type: Opaque +``` From b1513b8cfb653b424f6db0e431851b2ab3e2107f Mon Sep 17 00:00:00 2001 From: Russell Bryant Date: Mon, 13 May 2019 10:32:45 -0400 Subject: [PATCH 14/14] Update to latest master from cluster-api. This syncs with master of cluster-api, which closes #71. --- Gopkg.lock | 4 +- .../.github/ISSUE_TEMPLATE/bug_report.md | 1 + .../.github/ISSUE_TEMPLATE/feature_request.md | 7 - .../sigs.k8s.io/cluster-api/CONTRIBUTING.md | 4 + vendor/sigs.k8s.io/cluster-api/Dockerfile | 6 +- vendor/sigs.k8s.io/cluster-api/Gopkg.lock | 15 +- vendor/sigs.k8s.io/cluster-api/Makefile | 49 +++++-- vendor/sigs.k8s.io/cluster-api/OWNERS_ALIASES | 11 +- vendor/sigs.k8s.io/cluster-api/README.md | 14 +- .../sigs.k8s.io/cluster-api/SECURITY_CONTACTS | 4 +- vendor/sigs.k8s.io/cluster-api/WORKSPACE | 10 +- .../cluster-api/cmd/clusterctl/README.md | 34 ++++- .../clusterdeployer/bootstrap/kind/kind.go | 16 ++- .../bootstrap/minikube/minikube.go | 17 +++ .../clusterclient/clusterclient.go | 33 +++-- .../clusterdeployer/clusterdeployer.go | 21 ++- .../cmd/clusterctl/cmd/BUILD.bazel | 1 + .../cmd/alpha_phase_get_kubeconfig.go | 2 +- .../cmd/clusterctl/cmd/create_cluster.go | 2 +- .../cluster-api/cmd/clusterctl/cmd/root.go | 2 + .../phases/createbootstrapcluster.go | 2 +- .../cmd/clusterctl/phases/getkubeconfig.go | 19 ++- ...create-cluster-no-args-invalid-flag.golden | 14 +- .../testdata/create-cluster-no-args.golden | 14 +- .../create-no-args-invalid-flag.golden | 12 +- .../clusterctl/testdata/create-no-args.golden | 12 +- ...delete-cluster-no-args-invalid-flag.golden | 12 +- .../testdata/delete-cluster-no-args.golden | 12 +- .../delete-no-args-invalid-flag.golden | 12 +- .../clusterctl/testdata/delete-no-args.golden | 12 +- .../testdata/no-args-invalid-flag.golden | 12 +- .../cmd/clusterctl/testdata/no-args.golden | 12 +- ...lidate-cluster-no-args-invalid-flag.golden | 12 +- .../validate-no-args-invalid-flag.golden | 12 +- .../testdata/validate-no-args.golden | 12 +- .../cluster-api/cmd/manager/main.go | 15 +- .../config/crds/cluster_v1alpha1_machine.yaml | 37 +++-- .../cluster_v1alpha1_machinedeployment.yaml | 20 +-- .../crds/cluster_v1alpha1_machineset.yaml | 29 ++-- .../cluster-api/config/rbac/rbac_role.yaml | 4 +- .../cluster-api/docs/book/SUMMARY.md | 1 + .../docs/book/common_code/architecture.md | 4 +- .../book/common_code/cluster_controller.md | 19 ++- .../book/common_code/machine_controller.md | 19 ++- .../machinedeployment_controller.md | 20 +++ .../book/common_code/machineset_controller.md | 21 +++ .../register_controllers.md | 4 +- .../register_schemes.md | 3 + .../cluster-api/docs/developer/releasing.md | 25 ++-- .../docs/proposals/machine-api-proposal.md | 132 ------------------ .../cluster-api/hack/update-bazel.sh | 7 +- .../cluster-api/hack/verify_clientset.sh | 3 +- .../apis/cluster/v1alpha1/machine_types.go | 20 ++- .../apis/cluster/v1alpha1/machineset_types.go | 35 +++++ .../pkg/controller/machine/controller.go | 6 +- .../machinedeployment/controller.go | 85 +++++------ .../pkg/controller/machineset/BUILD.bazel | 1 + .../pkg/controller/machineset/controller.go | 26 ++-- .../controller/machineset/delete_policy.go | 111 +++++++++++---- .../pkg/controller/machineset/status.go | 2 +- .../pkg/controller/noderefutil/util.go | 2 +- .../pkg/provider/example/container/Dockerfile | 4 +- .../sigs.k8s.io/cluster-api/pkg/util/util.go | 21 +-- .../cluster-api/scripts/ci-build.sh | 4 +- .../cluster-api/scripts/ci-integration.sh | 26 ++-- .../scripts/ci-is-vendor-in-sync.sh | 4 +- .../cluster-api/scripts/ci-make.sh | 4 +- .../cluster-api/scripts/ci-test.sh | 4 +- .../cluster-api/scripts/fetch_ext_bins.sh | 2 - 69 files changed, 700 insertions(+), 454 deletions(-) delete mode 100644 vendor/sigs.k8s.io/cluster-api/docs/proposals/machine-api-proposal.md diff --git a/Gopkg.lock b/Gopkg.lock index 7a4be60ea..3455b88a4 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -870,7 +870,7 @@ [[projects]] branch = "master" - digest = "1:5c6ceec6293d32faf211ee10d8e8ab54543a857790943ccdf637aa72480a22da" + digest = "1:75ff2e4a98ad27c9b303c4199d5eea3a701615b4c580cb7338ac68cb3b030608" name = "sigs.k8s.io/cluster-api" packages = [ "pkg/apis", @@ -881,7 +881,7 @@ "pkg/util", ] pruneopts = "T" - revision = "26aa37ad8bcffd5b69e2a514b5e9a8ac30744134" + revision = "2461be4bcef798177d17e67746abc6dcb7767053" [[projects]] digest = "1:5aa50779f75cc439edd3455a6dee7cf179b52f8dde764a47cc929693485d1afb" diff --git a/vendor/sigs.k8s.io/cluster-api/.github/ISSUE_TEMPLATE/bug_report.md b/vendor/sigs.k8s.io/cluster-api/.github/ISSUE_TEMPLATE/bug_report.md index 3a7d08c61..ab086f02a 100644 --- a/vendor/sigs.k8s.io/cluster-api/.github/ISSUE_TEMPLATE/bug_report.md +++ b/vendor/sigs.k8s.io/cluster-api/.github/ISSUE_TEMPLATE/bug_report.md @@ -20,5 +20,6 @@ about: Tell us about a problem you are experiencing **Environment:** - Cluster-api version: +- Minikube/KIND version: - Kubernetes version: (use `kubectl version`): - OS (e.g. from `/etc/os-release`): diff --git a/vendor/sigs.k8s.io/cluster-api/.github/ISSUE_TEMPLATE/feature_request.md b/vendor/sigs.k8s.io/cluster-api/.github/ISSUE_TEMPLATE/feature_request.md index fc06aa712..ec7c001bd 100644 --- a/vendor/sigs.k8s.io/cluster-api/.github/ISSUE_TEMPLATE/feature_request.md +++ b/vendor/sigs.k8s.io/cluster-api/.github/ISSUE_TEMPLATE/feature_request.md @@ -12,10 +12,3 @@ about: Suggest an idea for this project **Anything else you would like to add:** [Miscellaneous information that will assist in solving the issue.] - - -**Environment:** - -- Cluster-api version: -- Kubernetes version: (use `kubectl version`): -- OS (e.g. from `/etc/os-release`): diff --git a/vendor/sigs.k8s.io/cluster-api/CONTRIBUTING.md b/vendor/sigs.k8s.io/cluster-api/CONTRIBUTING.md index cc52280e0..b0be9a45f 100644 --- a/vendor/sigs.k8s.io/cluster-api/CONTRIBUTING.md +++ b/vendor/sigs.k8s.io/cluster-api/CONTRIBUTING.md @@ -26,6 +26,10 @@ All changes must be code reviewed. Coding conventions and standards are explaine Cluster API maintainers may add "LGTM" (Looks Good To Me) or an equivalent comment to indicate that a PR is acceptable. Any change requires at least one LGTM. No pull requests can be merged until at least one Cluster API maintainer signs off with an LGTM. +### Google Doc Viewing Permissions + +To gain viewing permissions to google docs in this project, please join either the [kubernetes-dev](https://groups.google.com/forum/#!forum/kubernetes-dev) or [kubernetes-sig-cluster-lifecycle](https://groups.google.com/forum/#!forum/kubernetes-sig-cluster-lifecycle) google group. + ## Cloud Provider Developer Guide ### Overview diff --git a/vendor/sigs.k8s.io/cluster-api/Dockerfile b/vendor/sigs.k8s.io/cluster-api/Dockerfile index f0d00be9f..bf3b691a2 100644 --- a/vendor/sigs.k8s.io/cluster-api/Dockerfile +++ b/vendor/sigs.k8s.io/cluster-api/Dockerfile @@ -13,7 +13,9 @@ # limitations under the License. # Build the manager binary -FROM golang:1.11.5 as builder +FROM golang:1.12.5 as builder + +ARG ARCH # Copy in the go src WORKDIR $GOPATH/src/sigs.k8s.io/cluster-api @@ -22,7 +24,7 @@ COPY cmd/ cmd/ COPY vendor/ vendor/ # Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -ldflags '-extldflags "-static"' -o manager sigs.k8s.io/cluster-api/cmd/manager +RUN CGO_ENABLED=0 GOOS=linux GOARCH=${ARCH} go build -a -ldflags '-extldflags "-static"' -o manager sigs.k8s.io/cluster-api/cmd/manager # Copy the controller-manager into a thin image FROM gcr.io/distroless/static:latest diff --git a/vendor/sigs.k8s.io/cluster-api/Gopkg.lock b/vendor/sigs.k8s.io/cluster-api/Gopkg.lock index 724e64ff2..dad988a85 100644 --- a/vendor/sigs.k8s.io/cluster-api/Gopkg.lock +++ b/vendor/sigs.k8s.io/cluster-api/Gopkg.lock @@ -1079,6 +1079,14 @@ pruneopts = "UT" revision = "3a2206dd6a78497deceb3ae058417fdeb2036c7e" +[[projects]] + branch = "master" + digest = "1:dc54d24e166e75a89145883b6080306f5684db2e8ab7a068625f010dee604c6e" + name = "k8s.io/component-base" + packages = ["cli/flag"] + pruneopts = "UT" + revision = "fd5d14dd6d20b9f3b0c066e2702d877b0b8c049f" + [[projects]] branch = "master" digest = "1:28514fabca4356625720ffb012408790a9d00d31963a9bd9daf7b5ccd894c301" @@ -1096,12 +1104,12 @@ revision = "f8a0810f38afb8478882b3835a615aebfda39afa" [[projects]] - digest = "1:e2999bf1bb6eddc2a6aa03fe5e6629120a53088926520ca3b4765f77d7ff7eab" + digest = "1:c696379ad201c1e86591785579e16bf6cf886c362e9a7534e8eb0d1028b20582" name = "k8s.io/klog" packages = ["."] pruneopts = "UT" - revision = "a5bc97fbc634d635061f3146511332c7e313a55a" - version = "v0.1.0" + revision = "e531227889390a39d9533dde61f590fe9f4b0035" + version = "v0.3.0" [[projects]] branch = "master" @@ -1254,6 +1262,7 @@ "k8s.io/code-generator/cmd/deepcopy-gen", "k8s.io/code-generator/cmd/informer-gen", "k8s.io/code-generator/cmd/lister-gen", + "k8s.io/component-base/cli/flag", "k8s.io/klog", "sigs.k8s.io/controller-runtime/pkg/client", "sigs.k8s.io/controller-runtime/pkg/client/config", diff --git a/vendor/sigs.k8s.io/cluster-api/Makefile b/vendor/sigs.k8s.io/cluster-api/Makefile index a03f37077..5dca9e22e 100644 --- a/vendor/sigs.k8s.io/cluster-api/Makefile +++ b/vendor/sigs.k8s.io/cluster-api/Makefile @@ -21,9 +21,18 @@ export KUBEBUILDER_CONTROLPLANE_START_TIMEOUT ?=60s export KUBEBUILDER_CONTROLPLANE_STOP_TIMEOUT ?=60s +# This option is for running docker manifest command +export DOCKER_CLI_EXPERIMENTAL := enabled + # Image URL to use all building/pushing image targets -export CONTROLLER_IMG ?= gcr.io/k8s-cluster-api/cluster-api-controller:latest -export EXAMPLE_PROVIDER_IMG ?= gcr.io/k8s-cluster-api/example-provider-controller:latest +REGISTRY ?= gcr.io/k8s-cluster-api +CONTROLLER_IMG ?= $(REGISTRY)/cluster-api-controller +EXAMPLE_PROVIDER_IMG ?= $(REGISTRY)/example-provider-controller + +TAG ?= latest + +ARCH?=amd64 +ALL_ARCH = amd64 arm arm64 ppc64le s390x all: test manager clusterctl @@ -107,25 +116,49 @@ clientset: ## Generate a typed clientset clean: ## Remove all generated files rm -f bazel-* +.PHONY: all-docker-build +all-docker-build: $(addprefix sub-docker-build-,$(ALL_ARCH)) + @echo "updating kustomize image patch file for manager resource" + hack/sed.sh -i.tmp -e 's@image: .*@image: '"$(CONTROLLER_IMG):$(TAG)"'@' ./config/default/manager_image_patch.yaml + +sub-docker-build-%: + $(MAKE) ARCH=$* docker-build + .PHONY: docker-build docker-build: generate fmt vet manifests ## Build the docker image for controller-manager - docker build . -t ${CONTROLLER_IMG} + docker build --build-arg ARCH=$(ARCH) . -t $(CONTROLLER_IMG)-$(ARCH):$(TAG) @echo "updating kustomize image patch file for manager resource" - sed -i.tmp -e 's@image: .*@image: '"${CONTROLLER_IMG}"'@' ./config/default/manager_image_patch.yaml + hack/sed.sh -i.tmp -e 's@image: .*@image: '"${CONTROLLER_IMG}-$(ARCH):$(TAG)"'@' ./config/default/manager_image_patch.yaml + +.PHONY:all-push ## Push all the architecture docker images and fat manifest docker image +all-push: all-docker-push push-manifest + +.PHONY:all-docker-push ## Push all the architecture docker images +all-docker-push: $(addprefix sub-docker-push-,$(ALL_ARCH)) + +sub-docker-push-%: + $(MAKE) ARCH=$* docker-push + +.PHONY: push-manifest +push-manifest: ## Push the fat manifest docker image. TODO: Update bazel build to push manifest once https://github.com/bazelbuild/rules_docker/issues/300 get merged + ## Minimum docker version 18.06.0 is required for creating and pushing manifest images + docker manifest create --amend $(CONTROLLER_IMG):$(TAG) $(shell echo $(ALL_ARCH) | sed -e "s~[^ ]*~$(CONTROLLER_IMG)\-&:$(TAG)~g") + @for arch in $(ALL_ARCH); do docker manifest annotate --arch $${arch} ${CONTROLLER_IMG}:${TAG} ${CONTROLLER_IMG}-$${arch}:${TAG}; done + docker manifest push --purge ${CONTROLLER_IMG}:${TAG} .PHONY: docker-push docker-push: docker-build ## Push the docker image - docker push "$(CONTROLLER_IMG)" + docker push $(CONTROLLER_IMG)-$(ARCH):$(TAG) .PHONY: docker-build-ci docker-build-ci: generate fmt vet manifests ## Build the docker image for example provider - docker build . -f ./pkg/provider/example/container/Dockerfile -t ${EXAMPLE_PROVIDER_IMG} + docker build --build-arg ARCH=$(ARCH) . -f ./pkg/provider/example/container/Dockerfile -t $(EXAMPLE_PROVIDER_IMG)-$(ARCH):$(TAG) @echo "updating kustomize image patch file for ci" - sed -i.tmp -e 's@image: .*@image: '"${EXAMPLE_PROVIDER_IMG}"'@' ./config/ci/manager_image_patch.yaml + hack/sed.sh -i.tmp -e 's@image: .*@image: '"${EXAMPLE_PROVIDER_IMG}-$(ARCH):$(TAG)"'@' ./config/ci/manager_image_patch.yaml .PHONY: docker-push-ci docker-push-ci: docker-build-ci ## Build the docker image for ci - docker push "$(EXAMPLE_PROVIDER_IMG)" + docker push "$(EXAMPLE_PROVIDER_IMG)-$(ARCH):$(TAG)" .PHONY: verify verify: diff --git a/vendor/sigs.k8s.io/cluster-api/OWNERS_ALIASES b/vendor/sigs.k8s.io/cluster-api/OWNERS_ALIASES index d8f6e3cea..e7059b003 100644 --- a/vendor/sigs.k8s.io/cluster-api/OWNERS_ALIASES +++ b/vendor/sigs.k8s.io/cluster-api/OWNERS_ALIASES @@ -3,16 +3,13 @@ aliases: sig-cluster-lifecycle-leads: - luxas - - roberthbailey + - justinsb - timothysc cluster-api-admins: - justinsb - - kris-nova - - krousey - - luxas - - roberthbailey + - detiber + - davidewatson cluster-api-maintainers: - justinsb - - krousey - - roberthbailey + - detiber - vincepri diff --git a/vendor/sigs.k8s.io/cluster-api/README.md b/vendor/sigs.k8s.io/cluster-api/README.md index 147e99a9a..96268ed64 100644 --- a/vendor/sigs.k8s.io/cluster-api/README.md +++ b/vendor/sigs.k8s.io/cluster-api/README.md @@ -12,10 +12,12 @@ API design. Because of this, all of the prototype code is rapidly changing. ![Cluster API Architecture](./docs/book/common_code/architecture.svg "Cluster API Architecture") -To learn more, see the [Cluster API KEP][cluster-api-kep]. +Learn more about the project's [scope, objectives, goals and requirements](./docs/scope-and-objectives.md), [feature proposals](./docs/proposals/) and [reference use cases](./docs/staging-use-cases.md). ## Get involved! +* Join the [Cluster API discuss forum](https://discuss.kubernetes.io/c/contributors/cluster-api). + * Join the [sig-cluster-lifecycle](https://groups.google.com/forum/#!forum/kubernetes-sig-cluster-lifecycle) Google Group for access to documents and calendars. @@ -36,12 +38,14 @@ Provider specific code is being developed in separate repositories, some of whic are also sponsored by SIG-cluster-lifecycle: * AWS, https://github.com/kubernetes-sigs/cluster-api-provider-aws - * AWS/Openshift, https://github.com/openshift/cluster-operator * Azure, https://github.com/kubernetes-sigs/cluster-api-provider-azure * Baidu Cloud, https://github.com/baidu/cluster-api-provider-baiducloud + * Bare Metal, https://github.com/metal3-io/cluster-api-provider-baremetal * DigitalOcean, https://github.com/kubernetes-sigs/cluster-api-provider-digitalocean * GCE, https://github.com/kubernetes-sigs/cluster-api-provider-gcp + * IBM Cloud, https://github.com/kubernetes-sigs/cluster-api-provider-ibmcloud * OpenStack, https://github.com/kubernetes-sigs/cluster-api-provider-openstack + * Talos, https://github.com/talos-systems/cluster-api-provider-talos * Tencent Cloud, https://github.com/TencentCloud/cluster-api-provider-tencent * vSphere, https://github.com/kubernetes-sigs/cluster-api-provider-vsphere @@ -54,11 +58,15 @@ Following are the implementations managed by third-parties adopting the standard * Machine-controller-manager, https://github.com/gardener/machine-controller-manager/tree/cluster-api ## Getting Started + +### Resources + +* GitBook: [kubernetes-sigs.github.io/cluster-api](https://kubernetes-sigs.github.io/cluster-api) + ### Prerequisites * `kubectl` is required, see [here](http://kubernetes.io/docs/user-guide/prereqs/). * `clusterctl` is a SIG-cluster-lifecycle sponsored tool to manage Cluster API clusters. See [here](cmd/clusterctl) -[cluster-api-kep]: https://github.com/kubernetes/enhancements/blob/master/keps/sig-cluster-lifecycle/0003-cluster-api.md [notes]: https://docs.google.com/document/d/1Ys-DOR5UsgbMEeciuG0HOgDQc8kZsaWIWJeKJ1-UfbY/edit [recordings]: https://www.youtube.com/playlist?list=PL69nYSiGNLP29D0nYgAGWt1ZFqS9Z7lw4 [zoomMeeting]: https://zoom.us/j/861487554 diff --git a/vendor/sigs.k8s.io/cluster-api/SECURITY_CONTACTS b/vendor/sigs.k8s.io/cluster-api/SECURITY_CONTACTS index 686757663..7da23c405 100644 --- a/vendor/sigs.k8s.io/cluster-api/SECURITY_CONTACTS +++ b/vendor/sigs.k8s.io/cluster-api/SECURITY_CONTACTS @@ -10,7 +10,7 @@ # DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE # INSTRUCTIONS AT https://kubernetes.io/security/ -lukemarsden +detiber +justinsb luxas -roberthbailey timothysc diff --git a/vendor/sigs.k8s.io/cluster-api/WORKSPACE b/vendor/sigs.k8s.io/cluster-api/WORKSPACE index 6ad79c86d..d4409ad2b 100644 --- a/vendor/sigs.k8s.io/cluster-api/WORKSPACE +++ b/vendor/sigs.k8s.io/cluster-api/WORKSPACE @@ -3,8 +3,8 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") http_archive( name = "io_bazel_rules_go", - sha256 = "301c8b39b0808c49f98895faa6aa8c92cbd605ab5ad4b6a3a652da33a1a2ba2e", - urls = ["https://github.com/bazelbuild/rules_go/releases/download/0.18.0/rules_go-0.18.0.tar.gz"], + sha256 = "3743a20704efc319070957c45e24ae4626a05ba4b1d6a8961e87520296f1b676", + url = "https://github.com/bazelbuild/rules_go/releases/download/0.18.4/rules_go-0.18.4.tar.gz", ) http_archive( @@ -18,7 +18,7 @@ load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_depe go_rules_dependencies() go_register_toolchains( - go_version = "1.11.5", + go_version = "1.12.5", ) load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") @@ -27,13 +27,13 @@ gazelle_dependencies() go_repository( name = "io_k8s_sigs_kustomize", - commit = "58492e2d83c59ed63881311f46ad6251f77dabc3", importpath = "sigs.k8s.io/kustomize", + tag = "v1.0.11", ) go_repository( name = "com_github_golangci_golangci-lint", build_file_generation = "on", importpath = "github.com/golangci/golangci-lint", - tag = "v1.15.0", + tag = "v1.16.0", ) diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/README.md b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/README.md index 9499ef26b..5b3147030 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/README.md +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/README.md @@ -2,7 +2,7 @@ `clusterctl` is the SIG-cluster-lifecycle sponsored tool that implements the Cluster API. -Read the [experience doc here](https://docs.google.com/document/d/1-sYb3EdkRga49nULH1kSwuQFf1o6GvAw_POrsNo5d8c/edit#). +Read the [experience doc here](https://docs.google.com/document/d/1-sYb3EdkRga49nULH1kSwuQFf1o6GvAw_POrsNo5d8c/edit#). To gain viewing permissions, please join either the [kubernetes-dev](https://groups.google.com/forum/#!forum/kubernetes-dev) or [kubernetes-sig-cluster-lifecycle](https://groups.google.com/forum/#!forum/kubernetes-sig-cluster-lifecycle) google group. ## Getting Started @@ -13,8 +13,11 @@ this repository.** ### Prerequisites -1. Install [minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) -2. Install a [driver](https://github.com/kubernetes/minikube/blob/master/docs/drivers.md) for minikube. For Linux, we recommend kvm2. For MacOS, we recommend VirtualBox. +1. Cluster API runs its operations in Kubernetes. A pre-existing or temporary bootstrap cluster is required. Currently, we support multiple methods to bootstrap Cluster API: `kind` (preferred), `minikube` or any pre-existing cluster. + - If you want to use container, install [kind](https://github.com/kubernetes-sigs/kind#installation-and-usage). This is preferred. + - If you want to use VM, install [minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/), version 0.30.0 or greater. + - If you want to use existing Kubernetes cluster, prepare your kubeconfig. +2. If you are using `kind` or existing Kubernetes cluster, go to step 3. If you are using `minikube`, install a [driver](https://github.com/kubernetes/minikube/blob/master/docs/drivers.md). For Linux, we recommend `kvm2`. For MacOS, we recommend VirtualBox. 2. Build the `clusterctl` tool ```bash @@ -40,20 +43,39 @@ https://github.com/kubernetes-sigs/cluster-api/issues/158 and https://github.com 1. Create a cluster: + - __Bootstrap Cluster__: Use `bootstrap-type`, currently only `kind` and `minikube` are supported. + ```shell - ./clusterctl create cluster --provider -c cluster.yaml -m machines.yaml -p provider-components.yaml -a addons.yaml + ./clusterctl create cluster --provider --bootstrap-type -c cluster.yaml \ + -m machines.yaml -p provider-components.yaml -a addons.yaml ``` -To choose a specific minikube driver, please use the `--vm-driver` command line parameter. For example to use the kvm2 driver with clusterctl you would add `--vm-driver kvm2` + If you are using minikube, to choose a specific minikube driver, please use the `--bootstrap-flags vm-driver=xxx` command line parameter. For example to use the kvm2 driver with clusterctl you woud add `--bootstrap-flags vm-driver=kvm2`. + + - __Existing Cluster__: Use `bootstrap-cluster-kubeconfig`. This flag is used when you have an existing Kubernetes cluster. + + ```shell + ./clusterctl create cluster --provider --bootstrap-cluster-kubeconfig \ + -c cluster.yaml -m machines.yaml -p provider-components.yaml -a addons.yaml + ``` Additional advanced flags can be found via help. +Also, some environment variables are supported: +`CLUSTER_API_MACHINE_READY_TIMEOUT`: set this value to adjust the timeout value in minutes for a machine to become ready, The default timeout is currently 30 minutes, `export CLUSTER_API_MACHINE_READY_TIMEOUT=45` will extend the timeout value to 45 minutes. + ```shell ./clusterctl create cluster --help ``` ### Interacting with your cluster +If you are using kind, set the `KUBECONFIG` environment variable first before using kubectl: + +``` +export KUBECONFIG="$(kind get kubeconfig-path --name="clusterapi")" +``` + Once you have created a cluster, you can interact with the cluster and machine resources using kubectl: @@ -63,6 +85,8 @@ $ kubectl --kubeconfig kubeconfig get machines $ kubectl --kubeconfig kubeconfig get machines -o yaml ``` +**NOTE:** There is no need to specify `--kubeconfig` if your `kubeconfig` was located in the default directory under `$HOME/.kube/config` or if you have already exposed env variable `KUBECONFIG`. + #### Scaling your cluster You can scale your cluster by adding additional individual Machines, or by adding a MachineSet or MachineDeployment diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/bootstrap/kind/kind.go b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/bootstrap/kind/kind.go index 24fec4b69..dcf1538a1 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/bootstrap/kind/kind.go +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/bootstrap/kind/kind.go @@ -32,7 +32,7 @@ const ( var ( // ignoredOptions lists the options not supported by delete and kubeconfig-path. - ignoredOptions = []string{"config", "image", "retain"} + ignoredOptions = []string{"config", "image", "retain", "wait"} ) type Kind struct { @@ -46,9 +46,21 @@ func New() *Kind { } func WithOptions(options []string) *Kind { + // Set name if it is not provided. + if func() bool { + for _, opt := range options { + if strings.HasPrefix(opt, "name=") { + return false + } + } + return true + }() { + options = append(options, fmt.Sprintf("name=%s", kindClusterName)) + } + return &Kind{ execFunc: execFunc, - options: append(options, fmt.Sprintf("name=%s", kindClusterName)), + options: options, } } diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/bootstrap/minikube/minikube.go b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/bootstrap/minikube/minikube.go index fceb4df31..bb9044a10 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/bootstrap/minikube/minikube.go +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/bootstrap/minikube/minikube.go @@ -27,6 +27,10 @@ import ( "k8s.io/klog" ) +const ( + minikubeClusterName = "clusterapi" +) + type Minikube struct { kubeconfigpath string options []string @@ -47,6 +51,19 @@ func WithOptionsAndKubeConfigPath(options []string, kubeconfigpath string) *Mini // Arbitrary file name. Can potentially be randomly generated. kubeconfigpath = "minikube.kubeconfig" } + + // Set profile if it is not provided. + if func() bool { + for _, opt := range options { + if strings.HasPrefix(opt, "p=") { + return false + } + } + return true + }() { + options = append(options, fmt.Sprintf("profile=%s", minikubeClusterName)) + } + return &Minikube{ minikubeExec: minikubeExec, options: options, diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/clusterclient/clusterclient.go b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/clusterclient/clusterclient.go index 9bdd7a9fc..3511982c8 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/clusterclient/clusterclient.go +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/clusterclient/clusterclient.go @@ -55,6 +55,10 @@ const ( machineClusterLabelName = "cluster.k8s.io/cluster-name" ) +const ( + TimeoutMachineReady = "CLUSTER_API_MACHINE_READY_TIMEOUT" +) + // Provides interaction with a cluster type Client interface { Apply(string) error @@ -919,7 +923,7 @@ func (c *client) waitForKubectlApply(manifest string) error { klog.V(2).Infof("Waiting for kubectl apply...") err := c.kubectlApply(manifest) if err != nil { - if strings.Contains(err.Error(), io.EOF.Error()) || strings.Contains(err.Error(), "refused") { + if strings.Contains(err.Error(), io.EOF.Error()) || strings.Contains(err.Error(), "refused") || strings.Contains(err.Error(), "no such host") { // Connection was refused, probably because the API server is not ready yet. klog.V(4).Infof("Waiting for kubectl apply... server not yet available: %v", err) return false, nil @@ -968,7 +972,17 @@ func waitForClusterResourceReady(cs clientset.Interface) error { } func waitForMachineReady(cs clientset.Interface, machine *clusterv1.Machine) error { - err := util.PollImmediate(retryIntervalResourceReady, timeoutMachineReady, func() (bool, error) { + timeout := timeoutMachineReady + if p := os.Getenv(TimeoutMachineReady); p != "" { + t, err := strconv.Atoi(p) + if err == nil { + // only valid value will be used + timeout = time.Duration(t) * time.Minute + klog.V(4).Info("Setting wait for machine timeout value to ", timeout) + } + } + + err := util.PollImmediate(retryIntervalResourceReady, timeout, func() (bool, error) { klog.V(2).Infof("Waiting for Machine %v to become ready...", machine.Name) m, err := cs.ClusterV1alpha1().Machines(machine.Namespace).Get(machine.Name, metav1.GetOptions{}) if err != nil { @@ -1019,17 +1033,16 @@ func GetClusterAPIObject(client Client, clusterName, namespace string) (*cluster return nil, nil, nil, errors.Wrapf(err, "unable to fetch cluster %s/%s", namespace, clusterName) } - controlPlane, nodes, err := ExtractControlPlaneMachine(machines) + controlPlane, nodes, err := ExtractControlPlaneMachines(machines) if err != nil { return nil, nil, nil, errors.Wrapf(err, "unable to fetch control plane machine in cluster %s/%s", namespace, clusterName) } - return cluster, controlPlane, nodes, nil + return cluster, controlPlane[0], nodes, nil } -// ExtractControlPlaneMachine separates the machines running the control plane (singular) from the incoming machines. +// ExtractControlPlaneMachines separates the machines running the control plane from the incoming machines. // This is currently done by looking at which machine specifies the control plane version. -// TODO: Cleanup. -func ExtractControlPlaneMachine(machines []*clusterv1.Machine) (*clusterv1.Machine, []*clusterv1.Machine, error) { +func ExtractControlPlaneMachines(machines []*clusterv1.Machine) ([]*clusterv1.Machine, []*clusterv1.Machine, error) { nodes := []*clusterv1.Machine{} controlPlaneMachines := []*clusterv1.Machine{} for _, machine := range machines { @@ -1039,8 +1052,8 @@ func ExtractControlPlaneMachine(machines []*clusterv1.Machine) (*clusterv1.Machi nodes = append(nodes, machine) } } - if len(controlPlaneMachines) != 1 { - return nil, nil, errors.Errorf("expected one control plane machine, got: %v", len(controlPlaneMachines)) + if len(controlPlaneMachines) < 1 { + return nil, nil, errors.Errorf("expected one or more control plane machines, got: %v", len(controlPlaneMachines)) } - return controlPlaneMachines[0], nodes, nil + return controlPlaneMachines, nodes, nil } diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/clusterdeployer.go b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/clusterdeployer.go index 73d17b3e0..f3263b508 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/clusterdeployer.go +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/clusterdeployer.go @@ -57,7 +57,7 @@ func New( // Create the cluster from the provided cluster definition and machine list. func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*clusterv1.Machine, provider provider.Deployer, kubeconfigOutput string, providerComponentsStoreFactory provider.ComponentsStoreFactory) error { - controlPlaneMachine, nodes, err := clusterclient.ExtractControlPlaneMachine(machines) + controlPlaneMachines, nodes, err := clusterclient.ExtractControlPlaneMachines(machines) if err != nil { return errors.Wrap(err, "unable to separate control plane machines from node machines") } @@ -89,12 +89,12 @@ func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*cluster cluster.Namespace = bootstrapClient.GetContextNamespace() } - klog.Infof("Creating control plane %v in namespace %q", controlPlaneMachine.Name, cluster.Namespace) - if err := phases.ApplyMachines(bootstrapClient, cluster.Namespace, []*clusterv1.Machine{controlPlaneMachine}); err != nil { + klog.Infof("Creating control plane %v in namespace %q", controlPlaneMachines[0].Name, cluster.Namespace) + if err := phases.ApplyMachines(bootstrapClient, cluster.Namespace, []*clusterv1.Machine{controlPlaneMachines[0]}); err != nil { return errors.Wrap(err, "unable to create control plane machine") } - klog.Infof("Updating bootstrap cluster object for cluster %v in namespace %q with control plane endpoint running on %s", cluster.Name, cluster.Namespace, controlPlaneMachine.Name) + klog.Infof("Updating bootstrap cluster object for cluster %v in namespace %q with control plane endpoint running on %s", cluster.Name, cluster.Namespace, controlPlaneMachines[0].Name) if err := d.updateClusterEndpoint(bootstrapClient, provider, cluster.Name, cluster.Namespace); err != nil { return errors.Wrap(err, "unable to update bootstrap cluster endpoint") } @@ -130,11 +130,22 @@ func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*cluster // For some reason, endpoint doesn't get updated in bootstrap cluster sometimes. So we // update the target cluster endpoint as well to be sure. - klog.Infof("Updating target cluster object with control plane endpoint running on %s", controlPlaneMachine.Name) + klog.Infof("Updating target cluster object with control plane endpoint running on %s", controlPlaneMachines[0].Name) if err := d.updateClusterEndpoint(targetClient, provider, cluster.Name, cluster.Namespace); err != nil { return errors.Wrap(err, "unable to update target cluster endpoint") } + if len(controlPlaneMachines) > 1 { + // TODO(h0tbird) Done serially until kubernetes/kubeadm#1097 is resolved and all + // supported versions of k8s we are deploying (using kubeadm) have the fix. + klog.Info("Creating additional control plane machines in target cluster.") + for _, controlPlaneMachine := range controlPlaneMachines[1:] { + if err := phases.ApplyMachines(targetClient, cluster.Namespace, []*clusterv1.Machine{controlPlaneMachine}); err != nil { + return errors.Wrap(err, "unable to create additional control plane machines") + } + } + } + klog.Info("Creating node machines in target cluster.") if err := phases.ApplyMachines(targetClient, cluster.Namespace, nodes); err != nil { return errors.Wrap(err, "unable to create node machines") diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/BUILD.bazel b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/BUILD.bazel index b215680d1..48fc8fc2d 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/BUILD.bazel +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/BUILD.bazel @@ -43,6 +43,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//vendor/k8s.io/client-go/tools/clientcmd:go_default_library", + "//vendor/k8s.io/component-base/cli/flag:go_default_library", "//vendor/k8s.io/klog:go_default_library", "//vendor/sigs.k8s.io/controller-runtime/pkg/client:go_default_library", "//vendor/sigs.k8s.io/controller-runtime/pkg/client/config:go_default_library", diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/alpha_phase_get_kubeconfig.go b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/alpha_phase_get_kubeconfig.go index eb0decdac..5dac0147b 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/alpha_phase_get_kubeconfig.go +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/alpha_phase_get_kubeconfig.go @@ -88,7 +88,7 @@ func init() { alphaPhaseGetKubeconfigCmd.Flags().StringVarP(&pgko.Kubeconfig, "kubeconfig", "", "", "Path for the kubeconfig file to use") alphaPhaseGetKubeconfigCmd.Flags().StringVarP(&pgko.ClusterName, "cluster-name", "", "", "Cluster Name") // TODO: Remove as soon as code allows https://github.com/kubernetes-sigs/cluster-api/issues/157 - alphaPhaseGetKubeconfigCmd.Flags().StringVarP(&pgko.Provider, "provider", "", "", "Which provider deployment logic to use (google/vsphere/azure)") + alphaPhaseGetKubeconfigCmd.Flags().StringVarP(&pgko.Provider, "provider", "", "", "Which provider deployment logic to use") // Optional flags alphaPhaseGetKubeconfigCmd.Flags().StringVarP(&pgko.KubeconfigOutput, "kubeconfig-out", "", "kubeconfig", "Where to output the kubeconfig for the provisioned cluster") diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/create_cluster.go b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/create_cluster.go index a2d37172a..a46bd4ef4 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/create_cluster.go +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/create_cluster.go @@ -121,7 +121,7 @@ func init() { createClusterCmd.Flags().StringVarP(&co.ProviderComponents, "provider-components", "p", "", "A yaml file containing cluster api provider controllers and supporting objects. Required.") createClusterCmd.MarkFlagRequired("provider-components") // TODO: Remove as soon as code allows https://github.com/kubernetes-sigs/cluster-api/issues/157 - createClusterCmd.Flags().StringVarP(&co.Provider, "provider", "", "", "Which provider deployment logic to use (google/vsphere/azure). Required.") + createClusterCmd.Flags().StringVarP(&co.Provider, "provider", "", "", "Which provider deployment logic to use. Required.") createClusterCmd.MarkFlagRequired("provider") // Optional flags diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/root.go b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/root.go index 8266a6030..b584efb16 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/root.go +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/cmd/root.go @@ -22,6 +22,7 @@ import ( "os" "github.com/spf13/cobra" + cliflag "k8s.io/component-base/cli/flag" "k8s.io/klog" ) @@ -54,6 +55,7 @@ func exitWithHelp(cmd *cobra.Command, err string) { func init() { klog.InitFlags(flag.CommandLine) flag.CommandLine.Set("logtostderr", "true") + RootCmd.SetGlobalNormalizationFunc(cliflag.WordSepNormalizeFunc) RootCmd.PersistentFlags().AddGoFlagSet(flag.CommandLine) InitLogs() } diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/phases/createbootstrapcluster.go b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/phases/createbootstrapcluster.go index 2b4cdce6d..9c6643651 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/phases/createbootstrapcluster.go +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/phases/createbootstrapcluster.go @@ -24,7 +24,7 @@ import ( ) func CreateBootstrapCluster(provisioner bootstrap.ClusterProvisioner, cleanupBootstrapCluster bool, clientFactory clusterclient.Factory) (clusterclient.Client, func(), error) { - klog.Info("Creating bootstrap cluster") + klog.Info("Preparing bootstrap cluster") cleanupFn := func() {} if err := provisioner.Create(); err != nil { diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/phases/getkubeconfig.go b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/phases/getkubeconfig.go index fe5feee11..a119a883c 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/phases/getkubeconfig.go +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/phases/getkubeconfig.go @@ -25,7 +25,6 @@ import ( "k8s.io/klog" "sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/clusterclient" "sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/provider" - clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" "sigs.k8s.io/cluster-api/pkg/util" ) @@ -36,13 +35,8 @@ const ( // GetKubeconfig returns a kubeconfig for the target cluster func GetKubeconfig(bootstrapClient clusterclient.Client, provider provider.Deployer, kubeconfigOutput string, clusterName, namespace string) (string, error) { - cluster, controlPlane, _, err := clusterclient.GetClusterAPIObject(bootstrapClient, clusterName, namespace) - if err != nil { - return "", err - } - klog.V(1).Info("Getting target cluster kubeconfig.") - targetKubeconfig, err := waitForKubeconfigReady(provider, cluster, controlPlane) + targetKubeconfig, err := waitForKubeconfigReady(bootstrapClient, provider, clusterName, namespace) if err != nil { return "", fmt.Errorf("unable to get target cluster kubeconfig: %v", err) } @@ -54,11 +48,16 @@ func GetKubeconfig(bootstrapClient clusterclient.Client, provider provider.Deplo return targetKubeconfig, nil } -func waitForKubeconfigReady(provider provider.Deployer, cluster *clusterv1.Cluster, machine *clusterv1.Machine) (string, error) { +func waitForKubeconfigReady(bootstrapClient clusterclient.Client, provider provider.Deployer, clusterName, namespace string) (string, error) { kubeconfig := "" err := util.PollImmediate(retryKubeConfigReady, timeoutKubeconfigReady, func() (bool, error) { - klog.V(2).Infof("Waiting for kubeconfig on %v to become ready...", machine.Name) - k, err := provider.GetKubeConfig(cluster, machine) + cluster, controlPlane, _, err := clusterclient.GetClusterAPIObject(bootstrapClient, clusterName, namespace) + if err != nil { + return false, err + } + + klog.V(2).Infof("Waiting for kubeconfig on %v to become ready...", controlPlane.Name) + k, err := provider.GetKubeConfig(cluster, controlPlane) if err != nil { klog.V(4).Infof("error getting kubeconfig: %v", err) return false, nil diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-cluster-no-args-invalid-flag.golden b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-cluster-no-args-invalid-flag.golden index 12b804750..4e89d69d1 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-cluster-no-args-invalid-flag.golden +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-cluster-no-args-invalid-flag.golden @@ -13,21 +13,23 @@ Flags: -h, --help help for cluster --kubeconfig-out string Where to output the kubeconfig for the provisioned cluster (default "kubeconfig") -m, --machines string A yaml file containing machine object definition(s). Required. - --provider string Which provider deployment logic to use (google/vsphere/azure). Required. + --provider string Which provider deployment logic to use. Required. -p, --provider-components string A yaml file containing cluster api provider controllers and supporting objects. Required. Global Flags: --alsologtostderr log to standard error as well as files --kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster. + --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log-dir string If non-empty, write log files in this directory + --log-file string If non-empty, use this log file + --log-file-max-size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) - --log_dir string If non-empty, write log files in this directory - --log_file string If non-empty, use this log file --logtostderr log to standard error instead of files (default true) --master string The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. - --skip_headers If true, avoid header prefixes in the log messages + --skip-headers If true, avoid header prefixes in the log messages + --skip-log-headers If true, avoid headers when openning log files --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level log level for V logs + -v, --v Level number for the log level verbosity --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging unknown flag: --invalid-flag diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-cluster-no-args.golden b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-cluster-no-args.golden index 2c1241221..c3b2f2d26 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-cluster-no-args.golden +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-cluster-no-args.golden @@ -13,21 +13,23 @@ Flags: -h, --help help for cluster --kubeconfig-out string Where to output the kubeconfig for the provisioned cluster (default "kubeconfig") -m, --machines string A yaml file containing machine object definition(s). Required. - --provider string Which provider deployment logic to use (google/vsphere/azure). Required. + --provider string Which provider deployment logic to use. Required. -p, --provider-components string A yaml file containing cluster api provider controllers and supporting objects. Required. Global Flags: --alsologtostderr log to standard error as well as files --kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster. + --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log-dir string If non-empty, write log files in this directory + --log-file string If non-empty, use this log file + --log-file-max-size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) - --log_dir string If non-empty, write log files in this directory - --log_file string If non-empty, use this log file --logtostderr log to standard error instead of files (default true) --master string The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. - --skip_headers If true, avoid header prefixes in the log messages + --skip-headers If true, avoid header prefixes in the log messages + --skip-log-headers If true, avoid headers when openning log files --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level log level for V logs + -v, --v Level number for the log level verbosity --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging required flag(s) "cluster", "machines", "provider", "provider-components" not set diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-no-args-invalid-flag.golden b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-no-args-invalid-flag.golden index c13e2242f..b6ca97cd1 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-no-args-invalid-flag.golden +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-no-args-invalid-flag.golden @@ -11,15 +11,17 @@ Flags: Global Flags: --alsologtostderr log to standard error as well as files --kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster. + --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log-dir string If non-empty, write log files in this directory + --log-file string If non-empty, use this log file + --log-file-max-size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) - --log_dir string If non-empty, write log files in this directory - --log_file string If non-empty, use this log file --logtostderr log to standard error instead of files (default true) --master string The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. - --skip_headers If true, avoid header prefixes in the log messages + --skip-headers If true, avoid header prefixes in the log messages + --skip-log-headers If true, avoid headers when openning log files --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level log level for V logs + -v, --v Level number for the log level verbosity --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging Use "clusterctl create [command] --help" for more information about a command. diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-no-args.golden b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-no-args.golden index 436766ad1..5369b32f3 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-no-args.golden +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/create-no-args.golden @@ -12,15 +12,17 @@ Flags: Global Flags: --alsologtostderr log to standard error as well as files --kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster. + --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log-dir string If non-empty, write log files in this directory + --log-file string If non-empty, use this log file + --log-file-max-size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) - --log_dir string If non-empty, write log files in this directory - --log_file string If non-empty, use this log file --logtostderr log to standard error instead of files (default true) --master string The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. - --skip_headers If true, avoid header prefixes in the log messages + --skip-headers If true, avoid header prefixes in the log messages + --skip-log-headers If true, avoid headers when openning log files --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level log level for V logs + -v, --v Level number for the log level verbosity --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging Use "clusterctl create [command] --help" for more information about a command. diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-cluster-no-args-invalid-flag.golden b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-cluster-no-args-invalid-flag.golden index dc7a15fb3..4c2c40b13 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-cluster-no-args-invalid-flag.golden +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-cluster-no-args-invalid-flag.golden @@ -16,15 +16,17 @@ Flags: Global Flags: --alsologtostderr log to standard error as well as files --kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster. + --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log-dir string If non-empty, write log files in this directory + --log-file string If non-empty, use this log file + --log-file-max-size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) - --log_dir string If non-empty, write log files in this directory - --log_file string If non-empty, use this log file --logtostderr log to standard error instead of files (default true) --master string The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. - --skip_headers If true, avoid header prefixes in the log messages + --skip-headers If true, avoid header prefixes in the log messages + --skip-log-headers If true, avoid headers when openning log files --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level log level for V logs + -v, --v Level number for the log level verbosity --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging unknown flag: --invalid-flag diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-cluster-no-args.golden b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-cluster-no-args.golden index 3eaea4c89..82e0add99 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-cluster-no-args.golden +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-cluster-no-args.golden @@ -18,13 +18,15 @@ Flags: Global Flags: --alsologtostderr log to standard error as well as files --kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster. + --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log-dir string If non-empty, write log files in this directory + --log-file string If non-empty, use this log file + --log-file-max-size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) - --log_dir string If non-empty, write log files in this directory - --log_file string If non-empty, use this log file --logtostderr log to standard error instead of files (default true) --master string The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. - --skip_headers If true, avoid header prefixes in the log messages + --skip-headers If true, avoid header prefixes in the log messages + --skip-log-headers If true, avoid headers when openning log files --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level log level for V logs + -v, --v Level number for the log level verbosity --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-no-args-invalid-flag.golden b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-no-args-invalid-flag.golden index 2641ea0f9..54ed0e0eb 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-no-args-invalid-flag.golden +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-no-args-invalid-flag.golden @@ -11,15 +11,17 @@ Flags: Global Flags: --alsologtostderr log to standard error as well as files --kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster. + --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log-dir string If non-empty, write log files in this directory + --log-file string If non-empty, use this log file + --log-file-max-size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) - --log_dir string If non-empty, write log files in this directory - --log_file string If non-empty, use this log file --logtostderr log to standard error instead of files (default true) --master string The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. - --skip_headers If true, avoid header prefixes in the log messages + --skip-headers If true, avoid header prefixes in the log messages + --skip-log-headers If true, avoid headers when openning log files --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level log level for V logs + -v, --v Level number for the log level verbosity --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging Use "clusterctl delete [command] --help" for more information about a command. diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-no-args.golden b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-no-args.golden index a0f13986c..86cc1d355 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-no-args.golden +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/delete-no-args.golden @@ -12,15 +12,17 @@ Flags: Global Flags: --alsologtostderr log to standard error as well as files --kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster. + --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log-dir string If non-empty, write log files in this directory + --log-file string If non-empty, use this log file + --log-file-max-size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) - --log_dir string If non-empty, write log files in this directory - --log_file string If non-empty, use this log file --logtostderr log to standard error instead of files (default true) --master string The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. - --skip_headers If true, avoid header prefixes in the log messages + --skip-headers If true, avoid header prefixes in the log messages + --skip-log-headers If true, avoid headers when openning log files --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level log level for V logs + -v, --v Level number for the log level verbosity --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging Use "clusterctl delete [command] --help" for more information about a command. diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/no-args-invalid-flag.golden b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/no-args-invalid-flag.golden index d96b83c83..cbe4f8e4e 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/no-args-invalid-flag.golden +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/no-args-invalid-flag.golden @@ -14,15 +14,17 @@ Flags: --alsologtostderr log to standard error as well as files -h, --help help for clusterctl --kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster. + --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log-dir string If non-empty, write log files in this directory + --log-file string If non-empty, use this log file + --log-file-max-size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) - --log_dir string If non-empty, write log files in this directory - --log_file string If non-empty, use this log file --logtostderr log to standard error instead of files (default true) --master string The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. - --skip_headers If true, avoid header prefixes in the log messages + --skip-headers If true, avoid header prefixes in the log messages + --skip-log-headers If true, avoid headers when openning log files --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level log level for V logs + -v, --v Level number for the log level verbosity --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging Use "clusterctl [command] --help" for more information about a command. diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/no-args.golden b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/no-args.golden index 3394e22f2..005a7c359 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/no-args.golden +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/no-args.golden @@ -15,15 +15,17 @@ Flags: --alsologtostderr log to standard error as well as files -h, --help help for clusterctl --kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster. + --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log-dir string If non-empty, write log files in this directory + --log-file string If non-empty, use this log file + --log-file-max-size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) - --log_dir string If non-empty, write log files in this directory - --log_file string If non-empty, use this log file --logtostderr log to standard error instead of files (default true) --master string The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. - --skip_headers If true, avoid header prefixes in the log messages + --skip-headers If true, avoid header prefixes in the log messages + --skip-log-headers If true, avoid headers when openning log files --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level log level for V logs + -v, --v Level number for the log level verbosity --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging Use "clusterctl [command] --help" for more information about a command. diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/validate-cluster-no-args-invalid-flag.golden b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/validate-cluster-no-args-invalid-flag.golden index 75243978b..9ca44dee7 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/validate-cluster-no-args-invalid-flag.golden +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/validate-cluster-no-args-invalid-flag.golden @@ -11,15 +11,17 @@ Flags: Global Flags: --alsologtostderr log to standard error as well as files --kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster. + --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log-dir string If non-empty, write log files in this directory + --log-file string If non-empty, use this log file + --log-file-max-size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) - --log_dir string If non-empty, write log files in this directory - --log_file string If non-empty, use this log file --logtostderr log to standard error instead of files (default true) --master string The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. - --skip_headers If true, avoid header prefixes in the log messages + --skip-headers If true, avoid header prefixes in the log messages + --skip-log-headers If true, avoid headers when openning log files --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level log level for V logs + -v, --v Level number for the log level verbosity --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging unknown flag: --invalid-flag diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/validate-no-args-invalid-flag.golden b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/validate-no-args-invalid-flag.golden index fe1fd2036..943573942 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/validate-no-args-invalid-flag.golden +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/validate-no-args-invalid-flag.golden @@ -11,15 +11,17 @@ Flags: Global Flags: --alsologtostderr log to standard error as well as files --kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster. + --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log-dir string If non-empty, write log files in this directory + --log-file string If non-empty, use this log file + --log-file-max-size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) - --log_dir string If non-empty, write log files in this directory - --log_file string If non-empty, use this log file --logtostderr log to standard error instead of files (default true) --master string The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. - --skip_headers If true, avoid header prefixes in the log messages + --skip-headers If true, avoid header prefixes in the log messages + --skip-log-headers If true, avoid headers when openning log files --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level log level for V logs + -v, --v Level number for the log level verbosity --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging Use "clusterctl validate [command] --help" for more information about a command. diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/validate-no-args.golden b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/validate-no-args.golden index ba2421051..e88396b03 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/validate-no-args.golden +++ b/vendor/sigs.k8s.io/cluster-api/cmd/clusterctl/testdata/validate-no-args.golden @@ -12,15 +12,17 @@ Flags: Global Flags: --alsologtostderr log to standard error as well as files --kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster. + --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log-dir string If non-empty, write log files in this directory + --log-file string If non-empty, use this log file + --log-file-max-size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) - --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) - --log_dir string If non-empty, write log files in this directory - --log_file string If non-empty, use this log file --logtostderr log to standard error instead of files (default true) --master string The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster. - --skip_headers If true, avoid header prefixes in the log messages + --skip-headers If true, avoid header prefixes in the log messages + --skip-log-headers If true, avoid headers when openning log files --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level log level for V logs + -v, --v Level number for the log level verbosity --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging Use "clusterctl validate [command] --help" for more information about a command. diff --git a/vendor/sigs.k8s.io/cluster-api/cmd/manager/main.go b/vendor/sigs.k8s.io/cluster-api/cmd/manager/main.go index 85baa98ab..f10eb9906 100644 --- a/vendor/sigs.k8s.io/cluster-api/cmd/manager/main.go +++ b/vendor/sigs.k8s.io/cluster-api/cmd/manager/main.go @@ -19,6 +19,7 @@ package main import ( "flag" "log" + "time" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" "k8s.io/klog" @@ -32,8 +33,14 @@ import ( func main() { flag.Set("logtostderr", "true") klog.InitFlags(nil) - flag.Parse() + watchNamespace := flag.String("namespace", "", + "Namespace that the controller watches to reconcile cluster-api objects. If unspecified, the controller watches for cluster-api objects across all namespaces.") + flag.Parse() + if *watchNamespace != "" { + log.Printf("Watching cluster-api objects only in namespace %q for reconciliation.", *watchNamespace) + } + log.Printf("Registering Components.") // Get a config to talk to the apiserver cfg, err := config.GetConfig() if err != nil { @@ -41,7 +48,11 @@ func main() { } // Create a new Cmd to provide shared dependencies and start components - mgr, err := manager.New(cfg, manager.Options{}) + syncPeriod := 10 * time.Minute + mgr, err := manager.New(cfg, manager.Options{ + SyncPeriod: &syncPeriod, + Namespace: *watchNamespace, + }) if err != nil { log.Fatal(err) } diff --git a/vendor/sigs.k8s.io/cluster-api/config/crds/cluster_v1alpha1_machine.yaml b/vendor/sigs.k8s.io/cluster-api/config/crds/cluster_v1alpha1_machine.yaml index d513c9c1a..ecc0f00fa 100644 --- a/vendor/sigs.k8s.io/cluster-api/config/crds/cluster_v1alpha1_machine.yaml +++ b/vendor/sigs.k8s.io/cluster-api/config/crds/cluster_v1alpha1_machine.yaml @@ -6,6 +6,20 @@ metadata: controller-tools.k8s.io: "1.0" name: machines.cluster.k8s.io spec: + additionalPrinterColumns: + - JSONPath: .spec.providerID + description: Provider ID + name: ProviderID + type: string + - JSONPath: .status.phase + description: Machine status such as Terminating/Pending/Running/Failed etc + name: Phase + type: string + - JSONPath: .status.nodeRef.name + description: Node name associated with this machine + name: NodeName + priority: 1 + type: string group: cluster.k8s.io names: kind: Machine @@ -47,16 +61,16 @@ spec: by the provider. This field must match the provider ID as seen on the node object corresponding to this machine. This field is required by higher level consumers of cluster-api. Example use case is cluster - autoscaler with cluster-api as provider. Clean-up login in the autoscaler - compares machines v/s nodes to find out machines at provider which + autoscaler with cluster-api as provider. Clean-up logic in the autoscaler + compares machines to nodes to find out machines at provider which could not get registered as Kubernetes nodes. With cluster-api as a generic out-of-tree provider for autoscaler, this field is required by autoscaler to be able to have a provider view of the list of machines. - Another list of nodes is queries from the k8s apiserver and then comparison - is done to find out unregistered machines and are marked for delete. - This field will be set by the actuators and consumed by higher level - entities like autoscaler who will be interfacing with cluster-api - as generic provider. + Another list of nodes is queried from the k8s apiserver and then a + comparison is done to find out unregistered machines and are marked + for delete. This field will be set by the actuators and consumed by + higher level entities like autoscaler that will be interfacing with + cluster-api as generic provider. type: string providerSpec: description: ProviderSpec details Provider-specific configuration to @@ -84,9 +98,12 @@ spec: type: object type: object taints: - description: Taints is the full, authoritative list of taints to apply - to the corresponding Node. This list will overwrite any modifications - made to the Node on an ongoing basis. + description: The list of the taints to be applied to the corresponding + Node in additive manner. This list will not overwrite any other taints + added to the Node on an ongoing basis by other entities. These taints + should be actively reconciled e.g. if you ask the machine controller + to apply a taint and then manually remove the taint the machine controller + will put it back) but not have the machine controller remove any taints items: type: object type: array diff --git a/vendor/sigs.k8s.io/cluster-api/config/crds/cluster_v1alpha1_machinedeployment.yaml b/vendor/sigs.k8s.io/cluster-api/config/crds/cluster_v1alpha1_machinedeployment.yaml index f0d5bf730..853225344 100644 --- a/vendor/sigs.k8s.io/cluster-api/config/crds/cluster_v1alpha1_machinedeployment.yaml +++ b/vendor/sigs.k8s.io/cluster-api/config/crds/cluster_v1alpha1_machinedeployment.yaml @@ -142,16 +142,16 @@ spec: ID as seen on the node object corresponding to this machine. This field is required by higher level consumers of cluster-api. Example use case is cluster autoscaler with cluster-api as - provider. Clean-up login in the autoscaler compares machines - v/s nodes to find out machines at provider which could not + provider. Clean-up logic in the autoscaler compares machines + to nodes to find out machines at provider which could not get registered as Kubernetes nodes. With cluster-api as a generic out-of-tree provider for autoscaler, this field is required by autoscaler to be able to have a provider view - of the list of machines. Another list of nodes is queries - from the k8s apiserver and then comparison is done to find + of the list of machines. Another list of nodes is queried + from the k8s apiserver and then a comparison is done to find out unregistered machines and are marked for delete. This field will be set by the actuators and consumed by higher - level entities like autoscaler who will be interfacing with + level entities like autoscaler that will be interfacing with cluster-api as generic provider. type: string providerSpec: @@ -181,9 +181,13 @@ spec: type: object type: object taints: - description: Taints is the full, authoritative list of taints - to apply to the corresponding Node. This list will overwrite - any modifications made to the Node on an ongoing basis. + description: The list of the taints to be applied to the corresponding + Node in additive manner. This list will not overwrite any + other taints added to the Node on an ongoing basis by other + entities. These taints should be actively reconciled e.g. + if you ask the machine controller to apply a taint and then + manually remove the taint the machine controller will put + it back) but not have the machine controller remove any taints items: type: object type: array diff --git a/vendor/sigs.k8s.io/cluster-api/config/crds/cluster_v1alpha1_machineset.yaml b/vendor/sigs.k8s.io/cluster-api/config/crds/cluster_v1alpha1_machineset.yaml index dcc20f00d..73fda8c33 100644 --- a/vendor/sigs.k8s.io/cluster-api/config/crds/cluster_v1alpha1_machineset.yaml +++ b/vendor/sigs.k8s.io/cluster-api/config/crds/cluster_v1alpha1_machineset.yaml @@ -34,6 +34,15 @@ spec: type: object spec: properties: + deletePolicy: + description: DeletePolicy defines the policy used to identify nodes + to delete when downscaling. Defaults to "Random". Valid values are + "Random, "Newest", "Oldest" + enum: + - Random + - Newest + - Oldest + type: string minReadySeconds: description: MinReadySeconds is the minimum number of seconds for which a newly created machine should be ready. Defaults to 0 (machine will @@ -82,16 +91,16 @@ spec: ID as seen on the node object corresponding to this machine. This field is required by higher level consumers of cluster-api. Example use case is cluster autoscaler with cluster-api as - provider. Clean-up login in the autoscaler compares machines - v/s nodes to find out machines at provider which could not + provider. Clean-up logic in the autoscaler compares machines + to nodes to find out machines at provider which could not get registered as Kubernetes nodes. With cluster-api as a generic out-of-tree provider for autoscaler, this field is required by autoscaler to be able to have a provider view - of the list of machines. Another list of nodes is queries - from the k8s apiserver and then comparison is done to find + of the list of machines. Another list of nodes is queried + from the k8s apiserver and then a comparison is done to find out unregistered machines and are marked for delete. This field will be set by the actuators and consumed by higher - level entities like autoscaler who will be interfacing with + level entities like autoscaler that will be interfacing with cluster-api as generic provider. type: string providerSpec: @@ -121,9 +130,13 @@ spec: type: object type: object taints: - description: Taints is the full, authoritative list of taints - to apply to the corresponding Node. This list will overwrite - any modifications made to the Node on an ongoing basis. + description: The list of the taints to be applied to the corresponding + Node in additive manner. This list will not overwrite any + other taints added to the Node on an ongoing basis by other + entities. These taints should be actively reconciled e.g. + if you ask the machine controller to apply a taint and then + manually remove the taint the machine controller will put + it back) but not have the machine controller remove any taints items: type: object type: array diff --git a/vendor/sigs.k8s.io/cluster-api/config/rbac/rbac_role.yaml b/vendor/sigs.k8s.io/cluster-api/config/rbac/rbac_role.yaml index 89327bcde..90cbd805e 100644 --- a/vendor/sigs.k8s.io/cluster-api/config/rbac/rbac_role.yaml +++ b/vendor/sigs.k8s.io/cluster-api/config/rbac/rbac_role.yaml @@ -29,6 +29,7 @@ rules: - cluster.k8s.io resources: - machines + - machines/status verbs: - get - list @@ -41,6 +42,7 @@ rules: - cluster.k8s.io resources: - machinedeployments + - machinedeployments/status verbs: - get - list @@ -53,6 +55,7 @@ rules: - cluster.k8s.io resources: - machinesets + - machinesets/status verbs: - get - list @@ -89,7 +92,6 @@ rules: - cluster.k8s.io resources: - machines - - machines/status verbs: - get - list diff --git a/vendor/sigs.k8s.io/cluster-api/docs/book/SUMMARY.md b/vendor/sigs.k8s.io/cluster-api/docs/book/SUMMARY.md index dfdf2b3a2..30ada6bdf 100644 --- a/vendor/sigs.k8s.io/cluster-api/docs/book/SUMMARY.md +++ b/vendor/sigs.k8s.io/cluster-api/docs/book/SUMMARY.md @@ -14,6 +14,7 @@ * [Machine Controller](common_code/machine_controller.md) * [MachineSet Controller](common_code/machineset_controller.md) * [MachineDeployment Controller](common_code/machinedeployment_controller.md) +* [Node Controller](common_code/node_controller.md) ## Creating a New Provider diff --git a/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/architecture.md b/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/architecture.md index cc88459c1..658ad54d2 100644 --- a/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/architecture.md +++ b/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/architecture.md @@ -4,8 +4,8 @@ It may be useful to read at least the following chapters of the Kubebuilder book in order to better understand this section. -- [What is a Resource](https://github.com/kubernetes-sigs/kubebuilder/blob/master/docs/book/basics/what_is_a_resource.md) -- [What is a Controller](https://github.com/kubernetes-sigs/kubebuilder/blob/master/docs/book/basics/what_is_a_controller.md) +- [What is a Resource](https://book.kubebuilder.io/basics/what_is_a_resource.html) +- [What is a Controller](https://book.kubebuilder.io/basics/what_is_a_controller.html) {% endpanel %} {% panel style="warning", title="Architecture Diagram" %} diff --git a/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/cluster_controller.md b/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/cluster_controller.md index e24bfd82e..32a248f4d 100644 --- a/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/cluster_controller.md +++ b/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/cluster_controller.md @@ -87,13 +87,6 @@ If a `Cluster` resource is deleted, the controller will call the actuator's ## Cluster Controller Semantics -{% panel style="info", title="Logic sequence" %} -We need a diagram tracing the logic from resource creation through updates -and finally deletion. This was done using the sequences GitBook plugin. -Unfortunately there are (possibly personal) problems with phantomjs which -are making this difficult. -{% endpanel %} - 0. If the `Cluster` hasn't been deleted and doesn't have a finalizer, add one. - If the `Cluster` is being deleted, and there is no finalizer, we're done. - Call the provider specific `Delete()` method. @@ -101,3 +94,15 @@ are making this difficult. - If the `Cluster` has not been deleted, call the `Reconcile()` method. [cluster_source]: https://github.com/kubernetes-sigs/cluster-api/blob/master/pkg/apis/cluster/v1alpha1/cluster_types.go + +#### cluster object reconciliation logic + +![cluster object reconciliation logic](images/activity_cluster_reconciliation.svg) + +#### cluster object creation sequence + +![Cluster object creation](images/sequence_cluster_creation.svg) + +#### cluster object deletion sequence + +![Cluster object deletion](images/sequence_cluster_deletion.svg) diff --git a/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/machine_controller.md b/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/machine_controller.md index 198b3baea..b4770eeb8 100644 --- a/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/machine_controller.md +++ b/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/machine_controller.md @@ -109,11 +109,6 @@ The definition of `Exists()` is determined by the provider. ## Machine Controller Semantics -{% panel style="info", title="Logic sequence" %} -We need a diagram tracing the logic from resource creation through updates -and finally deletion. -{% endpanel %} - 0. Determine the `Cluster` associated with the `Machine` from its `cluster.k8s.io/cluster-name` label. - If the `Machine` hasn't been deleted and doesn't have a finalizer, add one. - If the `Machine` is being deleted, and there is no finalizer, we're done @@ -142,6 +137,20 @@ There are two consequences of this: delete the `Machine`. Therefore `Machine`s must be deleted before `Cluster`s. {% endpanel %} +#### machine reconciliation logic +![machine reconciliation logic](images/activity_machine_controller_reconciliation.svg) + +#### machine deletion block +![machine deletion block](images/activity_machine_deletion_block.svg) + +#### machine object creation sequence + +![machine object creation](images/sequence_machine_creation.svg) + +#### machine object deletion sequence + +![machine object deletion](images/sequence_machine_deletion.svg) + --- [^1] One reason a `Machine` may not be deleted is if it corresponds to the diff --git a/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/machinedeployment_controller.md b/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/machinedeployment_controller.md index e8d868ce6..510ec5102 100644 --- a/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/machinedeployment_controller.md +++ b/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/machinedeployment_controller.md @@ -15,8 +15,28 @@ {% endmethod %} {% method %} +## MachineDeploymentStrategy + +{% sample lang="go" %} +[import:'MachineDeploymentStrategy'](../../../pkg/apis/cluster/v1alpha1/machinedeployment_types.go) +{% endmethod %} + +{% method %} +## MachineRollingUpdateDeployment + +{% sample lang="go" %} +[import:'MachineRollingUpdateDeployment'](../../../pkg/apis/cluster/v1alpha1/machinedeployment_types.go) +{% endmethod %} + +{% method %} + ## MachineDeploymentStatus {% sample lang="go" %} [import:'MachineDeploymentStatus'](../../../pkg/apis/cluster/v1alpha1/machinedeployment_types.go) {% endmethod %} + +## MachineDeployment Controller Semantics + +![machinedeployment object reconciliation logic](images/activity_machinedeployment_reconciliation.svg) + diff --git a/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/machineset_controller.md b/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/machineset_controller.md index c4ca6014d..37cbeea99 100644 --- a/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/machineset_controller.md +++ b/vendor/sigs.k8s.io/cluster-api/docs/book/common_code/machineset_controller.md @@ -32,3 +32,24 @@ which implement their intent by modifying provider-specific `Cluster` and {% sample lang="go" %} [import:'MachineSetStatus'](../../../pkg/apis/cluster/v1alpha1/machineset_types.go) {% endmethod %} + +## MachineSet Controller Semantics + +![machineset object reconciliation logic](images/activity_machineset_reconciliation.svg) + +#### filter machine BLOCK + +This code block examines all machines in the namespace of the machineset and filters out machines that do NOT +have all the following conditions (in this order): + +1. The machine has a controller and is controlled by the machineset. +2. The machine is not scheduled for deletion. +3. The machine's label selector matches that of the machineset. + +For machines that fails condition 1, an attempt is made to adopt the machine into the machineset. The result +of this code block is a filtered list of machines that will be processed in the next code block. + +#### sync replica BLOCK + +This code block looks at the filtered machine list and determines whether to scale up or down the number of +machines to match the replica count defined in the machineset. diff --git a/vendor/sigs.k8s.io/cluster-api/docs/book/provider_implementations/register_controllers.md b/vendor/sigs.k8s.io/cluster-api/docs/book/provider_implementations/register_controllers.md index b72032423..aba469542 100644 --- a/vendor/sigs.k8s.io/cluster-api/docs/book/provider_implementations/register_controllers.md +++ b/vendor/sigs.k8s.io/cluster-api/docs/book/provider_implementations/register_controllers.md @@ -28,7 +28,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" ) -//+kubebuilder:rbac:groups=solas.k8s.io,resources=solasclusterproviderspecs;solasclusterproviderstatuses,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=solas.cluster.k8s.io,resources=solasclusterproviderspecs;solasclusterproviderstatuses,verbs=get;list;watch;create;update;patch;delete func init() { // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. AddToManagerFuncs = append(AddToManagerFuncs, func(m manager.Manager) error { @@ -68,7 +68,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" ) -//+kubebuilder:rbac:groups=solas.k8s.io,resources=solasmachineproviderspecs;solasmachineproviderstatuses,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=solas.cluster.k8s.io,resources=solasmachineproviderspecs;solasmachineproviderstatuses,verbs=get;list;watch;create;update;patch;delete func init() { // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. AddToManagerFuncs = append(AddToManagerFuncs, func(m manager.Manager) error { diff --git a/vendor/sigs.k8s.io/cluster-api/docs/book/provider_implementations/register_schemes.md b/vendor/sigs.k8s.io/cluster-api/docs/book/provider_implementations/register_schemes.md index 08d42988e..50b532645 100644 --- a/vendor/sigs.k8s.io/cluster-api/docs/book/provider_implementations/register_schemes.md +++ b/vendor/sigs.k8s.io/cluster-api/docs/book/provider_implementations/register_schemes.md @@ -28,6 +28,7 @@ import ( "fmt" "os" + "k8s.io/klog" "sigs.k8s.io/cluster-api-provider-solas/pkg/apis" "sigs.k8s.io/cluster-api-provider-solas/pkg/cloud/solas/actuators/cluster" "sigs.k8s.io/cluster-api-provider-solas/pkg/cloud/solas/actuators/machine" @@ -43,6 +44,8 @@ import ( ) func main() { + klog.InitFlags(nil) + cfg := config.GetConfigOrDie() if cfg == nil { panic(fmt.Errorf("GetConfigOrDie didn't die")) diff --git a/vendor/sigs.k8s.io/cluster-api/docs/developer/releasing.md b/vendor/sigs.k8s.io/cluster-api/docs/developer/releasing.md index 979d82f76..d6c2bee64 100644 --- a/vendor/sigs.k8s.io/cluster-api/docs/developer/releasing.md +++ b/vendor/sigs.k8s.io/cluster-api/docs/developer/releasing.md @@ -16,18 +16,25 @@ ## Process -1. Create a pull request that contains two changes: +For version 0.x.y: + +1. We will target a branch called `release-0.x`. If this is `0.x.0` then we'll + create a branch from master using `git push origin master:release-0.x`, otherwise + simply checkout the existing branch `git checkout release-0.x` +2. Make two changes: 1. Change [the cluster api controller manager image tag][managerimg] from `:latest` to whatever version is being released - 2. Change the `CONTROLLER_IMAGE` variable in the [Makefile][makefile] to the + 2. Change the `CONTROLLER_IMG` variable in the [Makefile][makefile] to the version being released -2. Get the pull request merged -3. From the commit in step 1 (that is now in the master branch), build and push - the container image with `make docker-push` -4. Create a tag from this same commit and push the tag to the github repository -5. Revert the commit made in step 1 -6. Open a pull request with the revert change -7. Get that pull request merged + (Note that we do not release the example-provider image, so we don't tag that) +3. Commit it using `git commit -m "Release 0.x.y"` +4. Submit a PR to the `release-0.x` branch, e.g. `git push $USER; hub pull-request -b release-0.x` +5. Get the pull request merged +6. Switch to the release branch and update to pick up the commit. (e.g. `git + checkout release 0.x && git pull`). From there build and push the container + images and fat manifest with `make all-push` (on the 0.1 release branch, we + do `make docker-push`) +7. Create a tag from this same commit `git tag 0.x.y` and push the tag to the github repository `git push origin 0.x.y` 8. Create a release in github based on the tag created above 9. Manually create the release notes by going through the merged PRs since the last release diff --git a/vendor/sigs.k8s.io/cluster-api/docs/proposals/machine-api-proposal.md b/vendor/sigs.k8s.io/cluster-api/docs/proposals/machine-api-proposal.md deleted file mode 100644 index 971973a87..000000000 --- a/vendor/sigs.k8s.io/cluster-api/docs/proposals/machine-api-proposal.md +++ /dev/null @@ -1,132 +0,0 @@ -Minimalistic Machines API -========================= - -This proposal is for a minimalistic start to a new Machines API, as part of the -overall Cluster API project. It is intended to live outside of core Kubernetes -and add optional machine management features to Kubernetes clusters. - -## Capabilities - -This API strives to be able to add these capabilities: - -1. A new Node can be created in a declarative way, including Kubernetes version. - It should also be able to specify provider-specific information such as OS image, - instance type, disk configuration, etc., though this will not be portable. - -1. A specific Node can be deleted, freeing external resources associated with - it. - -1. A specific Node can have its kubelet version upgraded or downgraded in a - declarative way\*. - -1. A specific Node can have its OS image upgraded or downgraded in a declarative - way\*. - -\* It is an implementation detail of the provider if these operations are -performed in-place or via Node replacement. - -## Proposal - -This proposal introduces a new API type: Machine. See the full definition in -[types.go](types.go). - -A "Machine" is the declarative spec for a Node, as represented in Kuberenetes -core. If a new Machine object is created, a provider-specific controller will -handle provisioning and installing a new host to register as a new Node matching -the Machine spec. If the Machine's spec is updated, a provider-specific -controller is responsible for updating the Node in-place or replacing the host -with a new one matching the updated spec. If a Machine object is deleted, the -corresponding Node should have its external resources released by the -provider-specific controller, and should be deleted as well. - -Fields like the kubelet version are modeled as fields on the Machine's spec. -Any other information that is provider-specific, though, is part of an opaque -ProviderSpec string that is not portable between different providers. - -The ProviderSpec is recommended to be a serialized API object in a format -owned by that provider, akin to the [Component Config](https://goo.gl/opSc2o) -pattern. This will allow the configuration to be strongly typed, versioned, and -have as much nested depth as appropriate. These provider-specific API -definitions are meant to live outside of the Machines API, which will allow them -to evolve independently of it. Attributes like instance type, which network to -use, and the OS image all belong in the ProviderSpec. - -## In-place vs. Replace - -One simplification that might be controversial in this proposal is the lack of -API control over "in-place" versus "replace" reconciliation strategies. For -instance, if a Machine's spec is updated with a different version of kubelet -than is actually running, it is up to the provider-specific controller whether -the request would best be fulfilled by performing an in-place upgrade on the -Node, or by deleting the Node and creating a new one in its place (or reporting -an error if this particular update is not supported). One can force a Node -replacement by deleting and recreating the Machine object rather than updating -it, but no similar mechanism exists to force an in-place change. - -Another approach considered was that modifying an existing Machine should only -ever attempt an in-place modification to the Node, and Node replacement should -only occur by deleting and creating a new Machine. In that case, a provider -would set an error field in the status if it wasn't able to fulfill the -requested in-place change (such as changing the OS image or instance type in a -cloud provider). - -The reason this approach wasn't used was because most cluster upgrade tools -built on top of the Machines API would follow the same pattern: - - for machine in machines: - attempt to upgrade machine in-place - if error: - create new machine - delete old machine - -Since updating a Node in-place is likely going to be faster than completely -replacing it, most tools would opt to use this pattern to attempt an in-place -modification first, before falling back to a full replacement. - -It seems like a much more powerful concept to allow every tool to instead say: - - for machine in machines: - update machine - -and allow the provider to decide if it is capable of performing an in-place -update, or if a full Node replacement is necessary. - -## Omitted Capabilities - -### A provider-agnostic mechanism to request new nodes - -In this proposal, only certain attributes of Machines are provider-agnostic and -can be operated on in a generic way. In other iterations of similar proposals, -much care had been taken to allow the creation of truly provider-agnostic -Machines that could be mapped to provider-specific attributes in order to better -support usecases around automated Machine scaling. This introduced a lot of -upfront complexity in the API proposals. - -This proposal starts much more minimalistic, but doesn't preclude the option of -extending the API to support these advanced concepts in the future (see -https://github.com/kubernetes-sigs/cluster-api/issues/22). - -### Dynamic API endpoint - -This proposal lacks the ability to declaratively update the kube-apiserver -endpoint for the kubelet to register with. This feature could be added later, -but doesn't seem to have demand now. Rather than modeling the kube-apiserver -endpoint in the Machine object, it is expected that the cluster installation -tool resolves the correct endpoint to use, starts a provider-specific Machines -controller configured with this endpoint, and that the controller injects the -endpoint into any hosts it provisions. - -## Conditions - -Brian Grant (@bgrant0607) and Eric Tune (@erictune) have indicated that the API pattern of having -"Conditions" lists in object statuses is soon to be deprecated. These have -generally been used as a timeline of state transitions for the object's -reconcilation, and difficult to consume for clients that just want a meaningful -representation of the object's current state. There are no existing examples of -the new pattern to follow instead, just the guidance that we should use -top-level fields in the status to reprensent meaningful information. We can -revisit the specifics when new patterns start to emerge in core. - -## Types - -Please see the full types [here](https://github.com/kubernetes-sigs/cluster-api/blob/master/pkg/apis/cluster/v1alpha1/machine_types.go). diff --git a/vendor/sigs.k8s.io/cluster-api/hack/update-bazel.sh b/vendor/sigs.k8s.io/cluster-api/hack/update-bazel.sh index b32b85096..a00bd54e3 100755 --- a/vendor/sigs.k8s.io/cluster-api/hack/update-bazel.sh +++ b/vendor/sigs.k8s.io/cluster-api/hack/update-bazel.sh @@ -17,8 +17,9 @@ set -o errexit set -o nounset set -o pipefail -export KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. +KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +export KUBE_ROOT -cd $KUBE_ROOT -find $KUBE_ROOT/vendor -name 'BUILD' -delete +cd "${KUBE_ROOT}" +find "${KUBE_ROOT}/vendor" -name 'BUILD' -delete bazel run //:gazelle diff --git a/vendor/sigs.k8s.io/cluster-api/hack/verify_clientset.sh b/vendor/sigs.k8s.io/cluster-api/hack/verify_clientset.sh index 939c87348..8554bbac0 100755 --- a/vendor/sigs.k8s.io/cluster-api/hack/verify_clientset.sh +++ b/vendor/sigs.k8s.io/cluster-api/hack/verify_clientset.sh @@ -18,11 +18,12 @@ set -o errexit set -o nounset set -o pipefail -SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")/.. +SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. DIFFROOT="${SCRIPT_ROOT}/pkg/client" TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/pkg" _tmp="${SCRIPT_ROOT}/_tmp" GOPATH=$(go env GOPATH) +export GOPATH cleanup() { rm -rf "${_tmp}" diff --git a/vendor/sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1/machine_types.go b/vendor/sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1/machine_types.go index 60fd59ab1..ac5623bad 100644 --- a/vendor/sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1/machine_types.go +++ b/vendor/sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1/machine_types.go @@ -38,6 +38,9 @@ const ( // Machine is the Schema for the machines API // +k8s:openapi-gen=true // +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="ProviderID",type="string",JSONPath=".spec.providerID",description="Provider ID" +// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="Machine status such as Terminating/Pending/Running/Failed etc" +// +kubebuilder:printcolumn:name="NodeName",type="string",JSONPath=".status.nodeRef.name",description="Node name associated with this machine",priority=1 type Machine struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` @@ -57,9 +60,12 @@ type MachineSpec struct { // +optional metav1.ObjectMeta `json:"metadata,omitempty"` - // Taints is the full, authoritative list of taints to apply to the corresponding - // Node. This list will overwrite any modifications made to the Node on - // an ongoing basis. + // The list of the taints to be applied to the corresponding Node in additive + // manner. This list will not overwrite any other taints added to the Node on + // an ongoing basis by other entities. These taints should be actively reconciled + // e.g. if you ask the machine controller to apply a taint and then manually remove + // the taint the machine controller will put it back) but not have the machine controller + // remove any taints // +optional Taints []corev1.Taint `json:"taints,omitempty"` @@ -87,12 +93,12 @@ type MachineSpec struct { // ProviderID is the identification ID of the machine provided by the provider. // This field must match the provider ID as seen on the node object corresponding to this machine. // This field is required by higher level consumers of cluster-api. Example use case is cluster autoscaler - // with cluster-api as provider. Clean-up login in the autoscaler compares machines v/s nodes to find out + // with cluster-api as provider. Clean-up logic in the autoscaler compares machines to nodes to find out // machines at provider which could not get registered as Kubernetes nodes. With cluster-api as a // generic out-of-tree provider for autoscaler, this field is required by autoscaler to be - // able to have a provider view of the list of machines. Another list of nodes is queries from the k8s apiserver - // and then comparison is done to find out unregistered machines and are marked for delete. - // This field will be set by the actuators and consumed by higher level entities like autoscaler who will + // able to have a provider view of the list of machines. Another list of nodes is queried from the k8s apiserver + // and then a comparison is done to find out unregistered machines and are marked for delete. + // This field will be set by the actuators and consumed by higher level entities like autoscaler that will // be interfacing with cluster-api as generic provider. // +optional ProviderID *string `json:"providerID,omitempty"` diff --git a/vendor/sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1/machineset_types.go b/vendor/sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1/machineset_types.go index ded876889..6f9077028 100644 --- a/vendor/sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1/machineset_types.go +++ b/vendor/sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1/machineset_types.go @@ -58,6 +58,11 @@ type MachineSetSpec struct { // +optional MinReadySeconds int32 `json:"minReadySeconds,omitempty"` + // DeletePolicy defines the policy used to identify nodes to delete when downscaling. + // Defaults to "Random". Valid values are "Random, "Newest", "Oldest" + // +kubebuilder:validation:Enum=Random,Newest,Oldest + DeletePolicy string `json:"deletePolicy,omitempty"` + // Selector is a label query over machines that should match the replica count. // Label keys and values that must match in order to be controlled by this MachineSet. // It must match the machine template's labels. @@ -70,6 +75,30 @@ type MachineSetSpec struct { Template MachineTemplateSpec `json:"template,omitempty"` } +// MachineSetDeletePolicy defines how priority is assigned to nodes to delete when +// downscaling a MachineSet. Defaults to "Random". +type MachineSetDeletePolicy string + +const ( + // RandomMachineSetDeletePolicy prioritizes both Machines that have the annotation + // "cluster.k8s.io/delete-machine=yes" and Machines that are unhealthy + // (Status.ErrorReason or Status.ErrorMessage are set to a non-empty value). + // Finally, it picks Machines at random to delete. + RandomMachineSetDeletePolicy MachineSetDeletePolicy = "Random" + + // NewestMachineSetDeletePolicy prioritizes both Machines that have the annotation + // "cluster.k8s.io/delete-machine=yes" and Machines that are unhealthy + // (Status.ErrorReason or Status.ErrorMessage are set to a non-empty value). + // It then prioritizes the newest Machines for deletion based on the Machine's CreationTimestamp. + NewestMachineSetDeletePolicy MachineSetDeletePolicy = "Newest" + + // OldestMachineSetDeletePolicy prioritizes both Machines that have the annotation + // "cluster.k8s.io/delete-machine=yes" and Machines that are unhealthy + // (Status.ErrorReason or Status.ErrorMessage are set to a non-empty value). + // It then prioritizes the oldest Machines for deletion based on the Machine's CreationTimestamp. + OldestMachineSetDeletePolicy MachineSetDeletePolicy = "Oldest" +) + /// [MachineSetSpec] // doxygen marker /// [MachineTemplateSpec] // doxygen marker @@ -170,6 +199,12 @@ func (m *MachineSet) Default() { if len(m.Namespace) == 0 { m.Namespace = metav1.NamespaceDefault } + + if m.Spec.DeletePolicy == "" { + randomPolicy := string(RandomMachineSetDeletePolicy) + log.Printf("Defaulting to %s\n", randomPolicy) + m.Spec.DeletePolicy = randomPolicy + } } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/vendor/sigs.k8s.io/cluster-api/pkg/controller/machine/controller.go b/vendor/sigs.k8s.io/cluster-api/pkg/controller/machine/controller.go index 8d60fd229..d8fba73f8 100644 --- a/vendor/sigs.k8s.io/cluster-api/pkg/controller/machine/controller.go +++ b/vendor/sigs.k8s.io/cluster-api/pkg/controller/machine/controller.go @@ -90,7 +90,7 @@ type ReconcileMachine struct { // Reconcile reads that state of the cluster for a Machine object and makes changes based on the state read // and what is in the Machine.Spec -// +kubebuilder:rbac:groups=cluster.k8s.io,resources=machines,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=cluster.k8s.io,resources=machines;machines/status,verbs=get;list;watch;create;update;patch;delete func (r *ReconcileMachine) Reconcile(request reconcile.Request) (reconcile.Result, error) { // TODO(mvladev): Can context be passed from Kubebuilder? ctx := context.TODO() @@ -163,7 +163,7 @@ func (r *ReconcileMachine) Reconcile(request reconcile.Request) (reconcile.Resul } if !r.isDeleteAllowed(m) { - klog.Infof("Skipping reconciling of machine %q", name) + klog.Infof("Deleting machine hosting this controller is not allowed. Skipping reconciliation of machine %q", name) return reconcile.Result{}, nil } @@ -235,7 +235,7 @@ func (r *ReconcileMachine) Reconcile(request reconcile.Request) (reconcile.Resul func (r *ReconcileMachine) getCluster(ctx context.Context, machine *clusterv1.Machine) (*clusterv1.Cluster, error) { if machine.Labels[clusterv1.MachineClusterLabelName] == "" { - klog.Infof("Machine %q in namespace %q doesn't specify %q label, assuming nil cluster", machine.Name, clusterv1.MachineClusterLabelName, machine.Namespace) + klog.Infof("Machine %q in namespace %q doesn't specify %q label, assuming nil cluster", machine.Name, machine.Namespace, clusterv1.MachineClusterLabelName) return nil, nil } diff --git a/vendor/sigs.k8s.io/cluster-api/pkg/controller/machinedeployment/controller.go b/vendor/sigs.k8s.io/cluster-api/pkg/controller/machinedeployment/controller.go index ddcedc5f7..3fa7a4848 100644 --- a/vendor/sigs.k8s.io/cluster-api/pkg/controller/machinedeployment/controller.go +++ b/vendor/sigs.k8s.io/cluster-api/pkg/controller/machinedeployment/controller.go @@ -92,7 +92,9 @@ func add(mgr manager.Manager, r reconcile.Reconciler, mapFn handler.ToRequestsFu return err } - // Map MachineSet changes to MachineDeployment. + // Watch for changes to MachineSets using a mapping function to MachineDeployment. + // This watcher is required for use cases like adoption. In case a MachineSet doesn't have + // a controller reference, it'll look for potential matching MachineDeployments to reconcile. err = c.Watch( &source.Kind{Type: &v1alpha1.MachineSet{}}, &handler.EnqueueRequestsFromMapFunc{ToRequests: mapFn}, @@ -160,7 +162,7 @@ func (r *ReconcileMachineDeployment) adoptOrphan(deployment *v1alpha1.MachineDep // Reconcile reads that state of the cluster for a MachineDeployment object and makes changes based on the state read // and what is in the MachineDeployment.Spec -// +kubebuilder:rbac:groups=cluster.k8s.io,resources=machinedeployments,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=cluster.k8s.io,resources=machinedeployments;machinedeployments/status,verbs=get;list;watch;create;update;patch;delete func (r *ReconcileMachineDeployment) Reconcile(request reconcile.Request) (reconcile.Result, error) { // Fetch the MachineDeployment instance d := &v1alpha1.MachineDeployment{} @@ -229,10 +231,11 @@ func (r *ReconcileMachineDeployment) reconcile(ctx context.Context, d *v1alpha1. } // Add foregroundDeletion finalizer if MachineDeployment isn't deleted and linked to a cluster. - if cluster != nil && d.ObjectMeta.DeletionTimestamp.IsZero() { - if !util.Contains(d.Finalizers, metav1.FinalizerDeleteDependents) { - d.Finalizers = append(d.ObjectMeta.Finalizers, metav1.FinalizerDeleteDependents) - } + if cluster != nil && + d.ObjectMeta.DeletionTimestamp.IsZero() && + !util.Contains(d.Finalizers, metav1.FinalizerDeleteDependents) { + + d.Finalizers = append(d.ObjectMeta.Finalizers, metav1.FinalizerDeleteDependents) if err := r.Client.Update(context.Background(), d); err != nil { klog.Infof("Failed to add finalizers to MachineSet %q: %v", d.Name, err) @@ -271,7 +274,7 @@ func (r *ReconcileMachineDeployment) reconcile(ctx context.Context, d *v1alpha1. func (r *ReconcileMachineDeployment) getCluster(d *v1alpha1.MachineDeployment) (*v1alpha1.Cluster, error) { if d.Spec.Template.Labels[v1alpha1.MachineClusterLabelName] == "" { - klog.Infof("Deployment %q in namespace %q doesn't specify %q label, assuming nil cluster", d.Name, v1alpha1.MachineClusterLabelName, d.Namespace) + klog.Infof("Deployment %q in namespace %q doesn't specify %q label, assuming nil cluster", d.Name, d.Namespace, v1alpha1.MachineClusterLabelName) return nil, nil } @@ -288,39 +291,6 @@ func (r *ReconcileMachineDeployment) getCluster(d *v1alpha1.MachineDeployment) ( return cluster, nil } -// getMachineDeploymentsForMachineSet returns a list of Deployments that potentially -// match a MachineSet. -func (r *ReconcileMachineDeployment) getMachineDeploymentsForMachineSet(ms *v1alpha1.MachineSet) []*v1alpha1.MachineDeployment { - if len(ms.Labels) == 0 { - klog.Warningf("No machine deployments found for MachineSet %v because it has no labels", ms.Name) - return nil - } - - dList := &v1alpha1.MachineDeploymentList{} - listOptions := &client.ListOptions{Namespace: ms.Namespace} - if err := r.Client.List(context.Background(), listOptions, dList); err != nil { - klog.Warningf("Failed to list machine deployments: %v", err) - return nil - } - - deployments := make([]*v1alpha1.MachineDeployment, 0, len(dList.Items)) - for idx, d := range dList.Items { - selector, err := metav1.LabelSelectorAsSelector(&d.Spec.Selector) - if err != nil { - continue - } - - // If a deployment with a nil or empty selector creeps in, it should match nothing, not everything. - if selector.Empty() || !selector.Matches(labels.Set(ms.Labels)) { - continue - } - - deployments = append(deployments, &dList.Items[idx]) - } - - return deployments -} - // getMachineMapForDeployment returns the Machines managed by a Deployment. // // It returns a map from MachineSet UID to a list of Machines controlled by that MS, @@ -366,6 +336,39 @@ func (r *ReconcileMachineDeployment) getMachineMapForDeployment(d *v1alpha1.Mach return machineMap, nil } +// getMachineDeploymentsForMachineSet returns a list of Deployments that potentially +// match a MachineSet. +func (r *ReconcileMachineDeployment) getMachineDeploymentsForMachineSet(ms *v1alpha1.MachineSet) []*v1alpha1.MachineDeployment { + if len(ms.Labels) == 0 { + klog.Warningf("No machine deployments found for MachineSet %v because it has no labels", ms.Name) + return nil + } + + dList := &v1alpha1.MachineDeploymentList{} + listOptions := &client.ListOptions{Namespace: ms.Namespace} + if err := r.Client.List(context.Background(), listOptions, dList); err != nil { + klog.Warningf("Failed to list machine deployments: %v", err) + return nil + } + + deployments := make([]*v1alpha1.MachineDeployment, 0, len(dList.Items)) + for idx, d := range dList.Items { + selector, err := metav1.LabelSelectorAsSelector(&d.Spec.Selector) + if err != nil { + continue + } + + // If a deployment with a nil or empty selector creeps in, it should match nothing, not everything. + if selector.Empty() || !selector.Matches(labels.Set(ms.Labels)) { + continue + } + + deployments = append(deployments, &dList.Items[idx]) + } + + return deployments +} + func (r *ReconcileMachineDeployment) MachineSetToDeployments(o handler.MapObject) []reconcile.Request { result := []reconcile.Request{} @@ -376,6 +379,8 @@ func (r *ReconcileMachineDeployment) MachineSetToDeployments(o handler.MapObject return nil } + // Check if the controller reference is already set and + // return an empty result when one is found. for _, ref := range ms.ObjectMeta.OwnerReferences { if ref.Controller != nil && *ref.Controller { return result diff --git a/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/BUILD.bazel b/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/BUILD.bazel index 61918f353..dabadbad9 100644 --- a/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/BUILD.bazel +++ b/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/BUILD.bazel @@ -43,6 +43,7 @@ go_test( embed = [":go_default_library"], deps = [ "//pkg/apis:go_default_library", + "//pkg/apis/cluster/common:go_default_library", "//pkg/apis/cluster/v1alpha1:go_default_library", "//vendor/golang.org/x/net/context:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", diff --git a/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/controller.go b/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/controller.go index d97de04f7..86d0c8cbb 100644 --- a/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/controller.go +++ b/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/controller.go @@ -140,7 +140,7 @@ func (r *ReconcileMachineSet) MachineToMachineSets(o handler.MapObject) []reconc // Reconcile reads that state of the cluster for a MachineSet object and makes changes based on the state read // and what is in the MachineSet.Spec // Automatically generate RBAC rules to allow the Controller to read and write Deployments -// +kubebuilder:rbac:groups=cluster.k8s.io,resources=machinesets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=cluster.k8s.io,resources=machinesets;machinesets/status,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=cluster.k8s.io,resources=machines,verbs=get;list;watch;create;update;patch;delete func (r *ReconcileMachineSet) Reconcile(request reconcile.Request) (reconcile.Result, error) { // Fetch the MachineSet instance @@ -203,10 +203,11 @@ func (r *ReconcileMachineSet) reconcile(ctx context.Context, machineSet *cluster } // Add foregroundDeletion finalizer if MachineSet isn't deleted and linked to a cluster. - if cluster != nil && machineSet.ObjectMeta.DeletionTimestamp.IsZero() { - if !util.Contains(machineSet.Finalizers, metav1.FinalizerDeleteDependents) { - machineSet.Finalizers = append(machineSet.ObjectMeta.Finalizers, metav1.FinalizerDeleteDependents) - } + if cluster != nil && + machineSet.ObjectMeta.DeletionTimestamp.IsZero() && + !util.Contains(machineSet.Finalizers, metav1.FinalizerDeleteDependents) { + + machineSet.Finalizers = append(machineSet.ObjectMeta.Finalizers, metav1.FinalizerDeleteDependents) if err := r.Client.Update(context.Background(), machineSet); err != nil { klog.Infof("Failed to add finalizers to MachineSet %q: %v", machineSet.Name, err) @@ -250,6 +251,10 @@ func (r *ReconcileMachineSet) reconcile(ctx context.Context, machineSet *cluster return reconcile.Result{}, errors.Wrap(err, "failed to update machine set status") } + if syncErr != nil { + return reconcile.Result{}, errors.Wrapf(syncErr, "failed to sync Machineset replicas") + } + var replicas int32 if updatedMS.Spec.Replicas != nil { replicas = *updatedMS.Spec.Replicas @@ -262,7 +267,7 @@ func (r *ReconcileMachineSet) reconcile(ctx context.Context, machineSet *cluster // exceeds MinReadySeconds could be incorrect. // To avoid an available replica stuck in the ready state, we force a reconcile after MinReadySeconds, // at which point it should confirm any available replica to be available. - if syncErr == nil && updatedMS.Spec.MinReadySeconds > 0 && + if updatedMS.Spec.MinReadySeconds > 0 && updatedMS.Status.ReadyReplicas == replicas && updatedMS.Status.AvailableReplicas != replicas { @@ -274,7 +279,7 @@ func (r *ReconcileMachineSet) reconcile(ctx context.Context, machineSet *cluster func (r *ReconcileMachineSet) getCluster(ms *clusterv1alpha1.MachineSet) (*clusterv1alpha1.Cluster, error) { if ms.Spec.Template.Labels[clusterv1alpha1.MachineClusterLabelName] == "" { - klog.Infof("MachineSet %q in namespace %q doesn't specify %q label, assuming nil cluster", ms.Name, clusterv1alpha1.MachineClusterLabelName, ms.Namespace) + klog.Infof("MachineSet %q in namespace %q doesn't specify %q label, assuming nil cluster", ms.Name, ms.Namespace, clusterv1alpha1.MachineClusterLabelName) return nil, nil } @@ -329,8 +334,13 @@ func (r *ReconcileMachineSet) syncReplicas(ms *clusterv1alpha1.MachineSet, machi klog.Infof("Too many replicas for %v %s/%s, need %d, deleting %d", controllerKind, ms.Namespace, ms.Name, *(ms.Spec.Replicas), diff) + deletePriorityFunc, err := getDeletePriorityFunc(ms) + if err != nil { + return err + } + klog.Infof("Found %s delete policy", ms.Spec.DeletePolicy) // Choose which Machines to delete. - machinesToDelete := getMachinesToDeletePrioritized(machines, diff, simpleDeletePriority) + machinesToDelete := getMachinesToDeletePrioritized(machines, diff, deletePriorityFunc) // TODO: Add cap to limit concurrent delete calls. errCh := make(chan error, diff) diff --git a/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/delete_policy.go b/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/delete_policy.go index b2d5acbc0..91b79de0e 100644 --- a/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/delete_policy.go +++ b/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/delete_policy.go @@ -17,32 +17,90 @@ limitations under the License. package machineset import ( + "math" + "sort" + + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" ) -type deletePriority int +type deletePriority float64 const ( - mustDelete deletePriority = 100 - betterDelete deletePriority = 50 - couldDelete deletePriority = 20 - // mustNotDelete deletePriority = 0 + + // DeleteNodeAnnotation marks nodes that will be given priority for deletion + // when a machineset scales down. This annotation is given top priority on all delete policies. + DeleteNodeAnnotation = "cluster.k8s.io/delete-machine" + + mustDelete deletePriority = 100.0 + betterDelete deletePriority = 50.0 + couldDelete deletePriority = 20.0 + mustNotDelete deletePriority = 0.0 + + secondsPerTenDays float64 = 864000 ) type deletePriorityFunc func(machine *v1alpha1.Machine) deletePriority -func simpleDeletePriority(machine *v1alpha1.Machine) deletePriority { +// maps the creation timestamp onto the 0-100 priority range +func oldestDeletePriority(machine *v1alpha1.Machine) deletePriority { + if machine.DeletionTimestamp != nil && !machine.DeletionTimestamp.IsZero() { + return mustDelete + } + if machine.ObjectMeta.Annotations != nil && machine.ObjectMeta.Annotations[DeleteNodeAnnotation] != "" { + return mustDelete + } + if machine.Status.ErrorReason != nil || machine.Status.ErrorMessage != nil { + return mustDelete + } + if machine.ObjectMeta.CreationTimestamp.Time.IsZero() { + return mustNotDelete + } + d := metav1.Now().Sub(machine.ObjectMeta.CreationTimestamp.Time) + if d.Seconds() < 0 { + return mustNotDelete + } + return deletePriority(float64(mustDelete) * (1.0 - math.Exp(-d.Seconds()/secondsPerTenDays))) +} + +func newestDeletePriority(machine *v1alpha1.Machine) deletePriority { + if machine.DeletionTimestamp != nil && !machine.DeletionTimestamp.IsZero() { + return mustDelete + } + if machine.ObjectMeta.Annotations != nil && machine.ObjectMeta.Annotations[DeleteNodeAnnotation] != "" { + return mustDelete + } + if machine.Status.ErrorReason != nil || machine.Status.ErrorMessage != nil { + return mustDelete + } + return mustDelete - oldestDeletePriority(machine) +} + +func randomDeletePolicy(machine *v1alpha1.Machine) deletePriority { if machine.DeletionTimestamp != nil && !machine.DeletionTimestamp.IsZero() { return mustDelete } + if machine.ObjectMeta.Annotations != nil && machine.ObjectMeta.Annotations[DeleteNodeAnnotation] != "" { + return betterDelete + } if machine.Status.ErrorReason != nil || machine.Status.ErrorMessage != nil { return betterDelete } return couldDelete } -// TODO: Define machines deletion policies. -// see: https://github.com/kubernetes/kube-deploy/issues/625 +type sortableMachines struct { + machines []*v1alpha1.Machine + priority deletePriorityFunc +} + +func (m sortableMachines) Len() int { return len(m.machines) } +func (m sortableMachines) Swap(i, j int) { m.machines[i], m.machines[j] = m.machines[j], m.machines[i] } +func (m sortableMachines) Less(i, j int) bool { + return m.priority(m.machines[j]) < m.priority(m.machines[i]) // high to low +} + func getMachinesToDeletePrioritized(filteredMachines []*v1alpha1.Machine, diff int, fun deletePriorityFunc) []*v1alpha1.Machine { if diff >= len(filteredMachines) { return filteredMachines @@ -50,24 +108,27 @@ func getMachinesToDeletePrioritized(filteredMachines []*v1alpha1.Machine, diff i return []*v1alpha1.Machine{} } - machines := make(map[deletePriority][]*v1alpha1.Machine) - - for _, machine := range filteredMachines { - priority := fun(machine) - machines[priority] = append(machines[priority], machine) + sortable := sortableMachines{ + machines: filteredMachines, + priority: fun, } + sort.Sort(sortable) - result := []*v1alpha1.Machine{} - for _, priority := range []deletePriority{ - mustDelete, - betterDelete, - couldDelete, - } { - result = append(result, machines[priority]...) - if len(result) >= diff { - break - } - } + return sortable.machines[:diff] +} - return result[:diff] +func getDeletePriorityFunc(ms *v1alpha1.MachineSet) (deletePriorityFunc, error) { + // Map the Spec.DeletePolicy value to the appropriate delete priority function + switch msdp := v1alpha1.MachineSetDeletePolicy(ms.Spec.DeletePolicy); msdp { + case v1alpha1.RandomMachineSetDeletePolicy: + return randomDeletePolicy, nil + case v1alpha1.NewestMachineSetDeletePolicy: + return newestDeletePriority, nil + case v1alpha1.OldestMachineSetDeletePolicy: + return oldestDeletePriority, nil + case "": + return randomDeletePolicy, nil + default: + return nil, errors.Errorf("Unsupported delete policy %s. Must be one of 'Random', 'Newest', or 'Oldest'", msdp) + } } diff --git a/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/status.go b/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/status.go index c22b19ffb..645d0dbb8 100644 --- a/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/status.go +++ b/vendor/sigs.k8s.io/cluster-api/pkg/controller/machineset/status.go @@ -112,7 +112,7 @@ func updateMachineSetStatus(c client.Client, ms *v1alpha1.MachineSet, newStatus break } // Update the MachineSet with the latest resource version for the next poll - if getErr = c.Get(context.Background(), client.ObjectKey{Name: ms.Name}, ms); getErr != nil { + if getErr = c.Get(context.Background(), client.ObjectKey{Namespace: ms.Namespace, Name: ms.Name}, ms); getErr != nil { // If the GET fails we can't trust status.Replicas anymore. This error // is bound to be more interesting than the update failure. return nil, getErr diff --git a/vendor/sigs.k8s.io/cluster-api/pkg/controller/noderefutil/util.go b/vendor/sigs.k8s.io/cluster-api/pkg/controller/noderefutil/util.go index a5bf0b842..d7dd79b8d 100644 --- a/vendor/sigs.k8s.io/cluster-api/pkg/controller/noderefutil/util.go +++ b/vendor/sigs.k8s.io/cluster-api/pkg/controller/noderefutil/util.go @@ -60,7 +60,7 @@ func GetReadyCondition(status *corev1.NodeStatus) *corev1.NodeCondition { // IsNodeReady returns true if a node is ready; false otherwise. func IsNodeReady(node *corev1.Node) bool { - if node == nil || &node.Status == nil { + if node == nil { return false } for _, c := range node.Status.Conditions { diff --git a/vendor/sigs.k8s.io/cluster-api/pkg/provider/example/container/Dockerfile b/vendor/sigs.k8s.io/cluster-api/pkg/provider/example/container/Dockerfile index 3306494e1..3b3f64a9a 100644 --- a/vendor/sigs.k8s.io/cluster-api/pkg/provider/example/container/Dockerfile +++ b/vendor/sigs.k8s.io/cluster-api/pkg/provider/example/container/Dockerfile @@ -15,6 +15,8 @@ # Build the manager binary FROM golang:1.11.5 as builder +ARG ARCH + # Copy in the go src WORKDIR $GOPATH/src/sigs.k8s.io/cluster-api COPY pkg/ pkg/ @@ -22,7 +24,7 @@ COPY vendor/ vendor/ COPY cmd/ cmd/ # Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -ldflags '-extldflags "-static"' -o ./cmd/example-provider/manager sigs.k8s.io/cluster-api/cmd/example-provider +RUN CGO_ENABLED=0 GOOS=linux GOARCH=${ARCH} go build -a -ldflags '-extldflags "-static"' -o ./cmd/example-provider/manager sigs.k8s.io/cluster-api/cmd/example-provider # Copy the controller-manager into a thin image FROM gcr.io/distroless/static:latest diff --git a/vendor/sigs.k8s.io/cluster-api/pkg/util/util.go b/vendor/sigs.k8s.io/cluster-api/pkg/util/util.go index b6bd7d926..519b2add7 100644 --- a/vendor/sigs.k8s.io/cluster-api/pkg/util/util.go +++ b/vendor/sigs.k8s.io/cluster-api/pkg/util/util.go @@ -85,16 +85,6 @@ func GetControlPlaneMachines(machines []*clusterv1.Machine) (res []*clusterv1.Ma return } -// MachineP converts a slice of machines into a slice of machine pointers. -func MachineP(machines []clusterv1.Machine) []*clusterv1.Machine { - // Convert to list of pointers - ret := make([]*clusterv1.Machine, 0, len(machines)) - for _, machine := range machines { - ret = append(ret, machine.DeepCopy()) - } - return ret -} - // Home returns the user home directory. func Home() string { home := os.Getenv("HOME") @@ -247,8 +237,7 @@ func ParseMachinesYaml(file string) ([]*clusterv1.Machine, error) { var ( bytes [][]byte machineList clusterv1.MachineList - machine clusterv1.Machine - machines = []clusterv1.Machine{} + machines = []*clusterv1.Machine{} ) // TODO: use the universal decoder instead of doing this. @@ -264,7 +253,8 @@ func ParseMachinesYaml(file string) ([]*clusterv1.Machine, error) { if err := json.Unmarshal(ml, &machineList); err != nil { return nil, err } - for _, machine := range machineList.Items { + for i := range machineList.Items { + machine := &machineList.Items[i] if machine.APIVersion == "" || machine.Kind == "" { return nil, errors.New(MachineListFormatDeprecationMessage) } @@ -282,13 +272,14 @@ func ParseMachinesYaml(file string) ([]*clusterv1.Machine, error) { } for _, m := range bytes { - if err := json.Unmarshal(m, &machine); err != nil { + machine := &clusterv1.Machine{} + if err := json.Unmarshal(m, machine); err != nil { return nil, err } machines = append(machines, machine) } - return MachineP(machines), nil + return machines, nil } // isMissingKind reimplements runtime.IsMissingKind as the YAMLOrJSONDecoder diff --git a/vendor/sigs.k8s.io/cluster-api/scripts/ci-build.sh b/vendor/sigs.k8s.io/cluster-api/scripts/ci-build.sh index 132ec8260..fbd9fca08 100755 --- a/vendor/sigs.k8s.io/cluster-api/scripts/ci-build.sh +++ b/vendor/sigs.k8s.io/cluster-api/scripts/ci-build.sh @@ -18,6 +18,6 @@ set -o errexit set -o nounset set -o pipefail -REPO_ROOT=$(dirname "${BASH_SOURCE}")/.. +REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. -cd $REPO_ROOT && make manager clusterctl +cd "$REPO_ROOT" && make manager clusterctl diff --git a/vendor/sigs.k8s.io/cluster-api/scripts/ci-integration.sh b/vendor/sigs.k8s.io/cluster-api/scripts/ci-integration.sh index 5d664c336..e569a9c78 100755 --- a/vendor/sigs.k8s.io/cluster-api/scripts/ci-integration.sh +++ b/vendor/sigs.k8s.io/cluster-api/scripts/ci-integration.sh @@ -21,31 +21,32 @@ set -o pipefail MAKE="make" KUSTOMIZE="kustomize" KUBECTL="kubectl" +KUBECTL_VERSION="v1.13.2" CRD_YAML="crd.yaml" BOOTSTRAP_CLUSTER_NAME="clusterapi-bootstrap" CONTROLLER_REPO="controller-ci" # use arbitrary repo name since we don't need to publish it EXAMPLE_PROVIDER_REPO="example-provider-ci" INTEGRATION_TEST_DIR="./test/integration" +ARCH=${ARCH:=amd64} + install_kustomize() { go get sigs.k8s.io/kustomize } install_kubectl() { - wget https://storage.googleapis.com/kubernetes-release/release/v1.10.2/bin/linux/amd64/kubectl \ + wget https://storage.googleapis.com/kubernetes-release/release/"${KUBECTL_VERSION}"/bin/linux/amd64/kubectl \ --no-verbose -O /usr/local/bin/kubectl chmod +x /usr/local/bin/kubectl } build_containers() { - VERSION=$(git describe --exact-match 2> /dev/null || git describe --match=$(git rev-parse --short=8 HEAD) --always --dirty --abbrev=8) - CONTROLLER_IMG="${CONTROLLER_REPO}:${VERSION}" - EXAMPLE_PROVIDER_IMG="${EXAMPLE_PROVIDER_REPO}:${VERSION}" - export CONTROLLER_IMG="${CONTROLLER_IMG}" - export EXAMPLE_PROVIDER_IMG="${EXAMPLE_PROVIDER_IMG}" - - "${MAKE}" docker-build - "${MAKE}" docker-build-ci + VERSION="$(git describe --exact-match 2> /dev/null || git describe --match="$(git rev-parse --short=8 HEAD)" --always --dirty --abbrev=8)" + export CONTROLLER_IMG="${CONTROLLER_REPO}" + export EXAMPLE_PROVIDER_IMG="${EXAMPLE_PROVIDER_REPO}" + + "${MAKE}" docker-build TAG=${VERSION} ARCH=${ARCH} + "${MAKE}" docker-build-ci TAG=${VERSION} ARCH=${ARCH} } prepare_crd_yaml() { @@ -58,10 +59,11 @@ prepare_crd_yaml() { create_bootstrap() { go get sigs.k8s.io/kind kind create cluster --name "${BOOTSTRAP_CLUSTER_NAME}" - export KUBECONFIG="$(kind get kubeconfig-path --name="${BOOTSTRAP_CLUSTER_NAME}")" + KUBECONFIG="$(kind get kubeconfig-path --name="${BOOTSTRAP_CLUSTER_NAME}")" + export KUBECONFIG - kind load docker-image "${CONTROLLER_IMG}" --name "${BOOTSTRAP_CLUSTER_NAME}" - kind load docker-image "${EXAMPLE_PROVIDER_IMG}" --name "${BOOTSTRAP_CLUSTER_NAME}" + kind load docker-image "${CONTROLLER_IMG}-${ARCH}:${VERSION}" --name "${BOOTSTRAP_CLUSTER_NAME}" + kind load docker-image "${EXAMPLE_PROVIDER_IMG}-${ARCH}:${VERSION}" --name "${BOOTSTRAP_CLUSTER_NAME}" } delete_bootstrap() { diff --git a/vendor/sigs.k8s.io/cluster-api/scripts/ci-is-vendor-in-sync.sh b/vendor/sigs.k8s.io/cluster-api/scripts/ci-is-vendor-in-sync.sh index 71c11a718..d4d04d935 100755 --- a/vendor/sigs.k8s.io/cluster-api/scripts/ci-is-vendor-in-sync.sh +++ b/vendor/sigs.k8s.io/cluster-api/scripts/ci-is-vendor-in-sync.sh @@ -18,8 +18,8 @@ set -o errexit set -o nounset set -o pipefail -REPO_ROOT=$(dirname "${BASH_SOURCE}")/.. +REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. -cd $REPO_ROOT +cd "$REPO_ROOT" find vendor -name 'BUILD.bazel' -delete dep check diff --git a/vendor/sigs.k8s.io/cluster-api/scripts/ci-make.sh b/vendor/sigs.k8s.io/cluster-api/scripts/ci-make.sh index 7c55a20df..542f741cb 100755 --- a/vendor/sigs.k8s.io/cluster-api/scripts/ci-make.sh +++ b/vendor/sigs.k8s.io/cluster-api/scripts/ci-make.sh @@ -18,6 +18,6 @@ set -o errexit set -o nounset set -o pipefail -REPO_ROOT=$(dirname "${BASH_SOURCE}")/.. +REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. -cd $REPO_ROOT && make lint-full docker-build +cd "$REPO_ROOT" && make lint-full docker-build diff --git a/vendor/sigs.k8s.io/cluster-api/scripts/ci-test.sh b/vendor/sigs.k8s.io/cluster-api/scripts/ci-test.sh index 909cf6012..a697bc3b8 100755 --- a/vendor/sigs.k8s.io/cluster-api/scripts/ci-test.sh +++ b/vendor/sigs.k8s.io/cluster-api/scripts/ci-test.sh @@ -18,9 +18,9 @@ set -o errexit set -o nounset set -o pipefail -REPO_ROOT=$(dirname "${BASH_SOURCE}")/.. +REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. -cd $REPO_ROOT && \ +cd "$REPO_ROOT" && \ source ./scripts/fetch_ext_bins.sh && \ fetch_tools && \ setup_envs && \ diff --git a/vendor/sigs.k8s.io/cluster-api/scripts/fetch_ext_bins.sh b/vendor/sigs.k8s.io/cluster-api/scripts/fetch_ext_bins.sh index 7b0f29173..fcb4aa7e7 100755 --- a/vendor/sigs.k8s.io/cluster-api/scripts/fetch_ext_bins.sh +++ b/vendor/sigs.k8s.io/cluster-api/scripts/fetch_ext_bins.sh @@ -59,11 +59,9 @@ function header_text { echo "$header$*$reset" } -rc=0 tmp_root=/tmp kb_root_dir=$tmp_root/kubebuilder -kb_orig=$(pwd) # Skip fetching and untaring the tools by setting the SKIP_FETCH_TOOLS variable # in your environment to any value: