diff --git a/Makefile b/Makefile index f2ef0fc3b1..04fd365d6d 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ RUN_NAMESPACE = metal3 -GO_TEST_FLAGS = $(VERBOSE) +GO_TEST_FLAGS = $(TEST_FLAGS) DEBUG = --debug COVER_PROFILE = cover.out @@ -18,10 +18,9 @@ COVER_PROFILE = cover.out BIN_DIR := bin CRD_OPTIONS ?= "crd:trivialVersions=false,allowDangerousTypes=true,crdVersions=v1" -CONTROLLER_TOOLS_VERSION=v0.4.1 -CONTROLLER_GEN := $(BIN_DIR)/controller-gen -GOLANGCI_LINT := $(BIN_DIR)/golangci-lint -KUSTOMIZE := $(BIN_DIR)/kustomize +CONTROLLER_GEN ?= go run sigs.k8s.io/controller-tools/cmd/controller-gen +GOLANGCI_LINT ?= GOLANGCI_LINT_CACHE=$(GOLANGCI_LINT_CACHE) go run github.com/golangci/golangci-lint/cmd/golangci-lint +KUSTOMIZE ?= go run sigs.k8s.io/kustomize/kustomize/v3 # See pkg/version.go for details SOURCE_GIT_COMMIT ?= $(shell git rev-parse --short HEAD) @@ -53,15 +52,6 @@ help: ## Display this help # Image URL to use all building/pushing image targets IMG ?= baremetal-operator:latest -# Produce CRDs that work back to Kubernetes 1.11 (no version conversion) -CRD_OPTIONS ?= "crd:trivialVersions=true" - -# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) -ifeq (,$(shell go env GOBIN)) -GOBIN=$(shell go env GOPATH)/bin -else -GOBIN=$(shell go env GOBIN) -endif ## -------------------------------------- ## Test Targets @@ -73,7 +63,7 @@ test: generate lint manifests unit ## Run common developer tests .PHONY: unit unit: ## Run unit tests - go test ./... $(VERBOSE) -coverprofile $(COVER_PROFILE) + go test ./... $(GO_TEST_FLAGS) -coverprofile $(COVER_PROFILE) .PHONY: unit-cover unit-cover: ## Run unit tests with code coverage @@ -82,7 +72,7 @@ unit-cover: ## Run unit tests with code coverage .PHONY: unit-verbose unit-verbose: ## Run unit tests with verbose output - VERBOSE=-v make unit + TEST_FLAGS=-v make unit ## -------------------------------------- ## Linter Targets @@ -101,12 +91,9 @@ fmt: lint vet: lint .PHONY: lint -lint: $(GOLANGCI_LINT) +lint: $(GOLANGCI_LINT) run -$(GOLANGCI_LINT): - ./hack/install-golangci-lint.sh - .PHONY: manifest-lint manifest-lint: ## Run manifest validation ./hack/manifestlint.sh @@ -135,35 +122,27 @@ run-test-mode: generate fmt vet manifests ## Run against the configured Kubernet go run -ldflags $(LDFLAGS) ./main.go -namespace=$(RUN_NAMESPACE) -dev -test-mode .PHONY: install -install: $(KUSTOMIZE) manifests ## Install CRDs into a cluster +install: manifests ## Install CRDs into a cluster $(KUSTOMIZE) build config/crd | kubectl apply -f - .PHONY: uninstall -uninstall: $(KUSTOMIZE) manifests ## Uninstall CRDs from a cluster +uninstall: manifests ## Uninstall CRDs from a cluster $(KUSTOMIZE) build config/crd | kubectl delete -f - .PHONY: deploy -deploy: $(KUSTOMIZE) manifests ## Deploy controller in the configured Kubernetes cluster in ~/.kube/config +deploy: manifests ## Deploy controller in the configured Kubernetes cluster in ~/.kube/config cd config/manager && kustomize edit set image controller=${IMG} $(KUSTOMIZE) build config/default | kubectl apply -f - .PHONY: manifests -manifests: $(CONTROLLER_GEN) $(KUSTOMIZE) ## Generate manifests e.g. CRD, RBAC etc. - $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases +manifests: ## Generate manifests e.g. CRD, RBAC etc. + $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases $(KUSTOMIZE) build config/default > config/render/capm3.yaml .PHONY: generate -generate: $(CONTROLLER_GEN) ## Generate code +generate: ## Generate code $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." -.PHONY: $(KUSTOMIZE) -$(KUSTOMIZE): - ./tools/install_kustomize.sh - -# Build the version of controller-gen that we use -$(CONTROLLER_GEN): - ./hack/install-controller-gen.sh $(CONTROLLER_TOOLS_VERSION) $$(pwd)/$(CONTROLLER_GEN) - ## -------------------------------------- ## Docker Targets ## -------------------------------------- diff --git a/OWNERS b/OWNERS index ac30c877ef..c5bb0dca44 100644 --- a/OWNERS +++ b/OWNERS @@ -3,6 +3,7 @@ approvers: - andfasano - asalkeld - dhellmann + - dtantsur - hardys - honza - kirankt @@ -16,6 +17,7 @@ reviewers: - andfasano - asalkeld - dhellmann + - dtantsur - hardys - honza - kirankt diff --git a/apis/metal3.io/v1alpha1/baremetalhost_types.go b/apis/metal3.io/v1alpha1/baremetalhost_types.go index 634a625498..b059c031ec 100644 --- a/apis/metal3.io/v1alpha1/baremetalhost_types.go +++ b/apis/metal3.io/v1alpha1/baremetalhost_types.go @@ -118,6 +118,8 @@ const ( // has any sort of error. OperationalStatusError OperationalStatus = "error" + // OperationalStatusDelayed is the status value for when the host + // deployment needs to be delayed to limit simultaneous hosts provisioning OperationalStatusDelayed = "delayed" ) @@ -546,10 +548,13 @@ type CredentialsStatus struct { type RebootMode string const ( + // RebootModeHard defined for hard reset of a node RebootModeHard RebootMode = "hard" + // RebootModeSoft defined for soft reset of a node RebootModeSoft RebootMode = "soft" ) +// RebootAnnotationArguments defines the arguments of the RebootAnnotation type type RebootAnnotationArguments struct { Mode RebootMode `json:"mode"` } @@ -896,6 +901,7 @@ func (host *BareMetalHost) GetImageChecksum() (string, string, bool) { return host.Spec.Image.GetChecksum() } +// GetChecksum method returns the checksum of an image func (image *Image) GetChecksum() (checksum, checksumType string, ok bool) { if image == nil { return diff --git a/config/certmanager/certificate.yaml b/config/certmanager/certificate.yaml index 58db114fa0..d361f623c2 100644 --- a/config/certmanager/certificate.yaml +++ b/config/certmanager/certificate.yaml @@ -2,7 +2,7 @@ # More document can be found at https://docs.cert-manager.io # WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for # breaking changes -apiVersion: cert-manager.io/v1alpha2 +apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: selfsigned-issuer @@ -10,7 +10,7 @@ metadata: spec: selfSigned: {} --- -apiVersion: cert-manager.io/v1alpha2 +apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml diff --git a/config/crd/patches/cainjection_in_baremetalhosts.yaml b/config/crd/patches/cainjection_in_baremetalhosts.yaml index 03555d0e92..6c3666e7ea 100644 --- a/config/crd/patches/cainjection_in_baremetalhosts.yaml +++ b/config/crd/patches/cainjection_in_baremetalhosts.yaml @@ -1,6 +1,6 @@ # The following patch adds a directive for certmanager to inject CA into the CRD # CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 +apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: diff --git a/config/crd/patches/webhook_in_baremetalhosts.yaml b/config/crd/patches/webhook_in_baremetalhosts.yaml index f4c5dfc536..d99d8f1389 100644 --- a/config/crd/patches/webhook_in_baremetalhosts.yaml +++ b/config/crd/patches/webhook_in_baremetalhosts.yaml @@ -1,6 +1,6 @@ # The following patch enables conversion webhook for CRD # CRD conversion requires k8s 1.13 or later. -apiVersion: apiextensions.k8s.io/v1beta1 +apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: baremetalhosts.metal3.io diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index b7bab386b8..839be30f58 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -46,7 +46,7 @@ vars: # objref: # kind: Certificate # group: cert-manager.io -# version: v1alpha2 +# version: v1 # name: serving-cert # this name should match the one in certificate.yaml # fieldref: # fieldpath: metadata.namespace @@ -54,7 +54,7 @@ vars: # objref: # kind: Certificate # group: cert-manager.io -# version: v1alpha2 +# version: v1 # name: serving-cert # this name should match the one in certificate.yaml #- name: SERVICE_NAMESPACE # namespace of the service # objref: diff --git a/config/default/webhookcainjection_patch.yaml b/config/default/webhookcainjection_patch.yaml index 7e79bf9955..02ab515d42 100644 --- a/config/default/webhookcainjection_patch.yaml +++ b/config/default/webhookcainjection_patch.yaml @@ -1,13 +1,13 @@ # This patch add annotation to admission webhook config and # the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. -apiVersion: admissionregistration.k8s.io/v1beta1 +apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: name: mutating-webhook-configuration annotations: cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) --- -apiVersion: admissionregistration.k8s.io/v1beta1 +apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: name: validating-webhook-configuration diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 8cc788c93c..b7f9601f19 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -27,6 +27,10 @@ spec: valueFrom: fieldRef: fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace envFrom: - configMapRef: name: ironic @@ -37,4 +41,4 @@ spec: port: 9440 initialDelaySeconds: 3 periodSeconds: 3 - terminationGracePeriodSeconds: 10 \ No newline at end of file + terminationGracePeriodSeconds: 10 diff --git a/config/render/capm3.yaml b/config/render/capm3.yaml index 95779965f4..c36b761faf 100644 --- a/config/render/capm3.yaml +++ b/config/render/capm3.yaml @@ -1039,6 +1039,10 @@ spec: valueFrom: fieldRef: fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace envFrom: - configMapRef: name: baremetal-operator-ironic diff --git a/controllers/metal3.io/baremetalhost_controller.go b/controllers/metal3.io/baremetalhost_controller.go index e36e7d2fcf..ddfce81e36 100644 --- a/controllers/metal3.io/baremetalhost_controller.go +++ b/controllers/metal3.io/baremetalhost_controller.go @@ -94,6 +94,7 @@ func (r *BareMetalHostReconciler) Reconcile(ctx context.Context, request ctrl.Re }() reqLogger := r.Log.WithValues("baremetalhost", request.NamespacedName) + reqLogger.Info("start") // Fetch the BareMetalHost host := &metal3v1alpha1.BareMetalHost{} @@ -213,7 +214,7 @@ func (r *BareMetalHostReconciler) Reconcile(ctx context.Context, request ctrl.Re request: request, bmcCredsSecret: bmcCredsSecret, } - prov, err := r.ProvisionerFactory(*host, *bmcCreds, info.publishEvent) + prov, err := r.ProvisionerFactory(*host.DeepCopy(), *bmcCreds, info.publishEvent) if err != nil { return ctrl.Result{}, errors.Wrap(err, "failed to create provisioner") } diff --git a/docs/api.md b/docs/api.md index 1a677a3b9a..9be413c131 100644 --- a/docs/api.md +++ b/docs/api.md @@ -36,6 +36,10 @@ communicate with them. * `idrac://` (or `idrac+http://` to disable TLS). * `idrac-virtualmedia://` to use virtual media instead of PXE for attaching the provisioning image to the host. + * `idrac-redfish://` may be used to manage iDRAC controller with the + Redfish protocol over HTTPS. The URL must also contain a path to + the Redfish API system endpoint. + `idrac-redfish://myhost.example/redfish/v1/Systems/System.Embedded.1` * Fujitsu iRMC * `irmc://:`, where `` is optional if using the default. * HUAWEI ibmc diff --git a/go.mod b/go.mod index 6a12dab0a2..9e67c34904 100644 --- a/go.mod +++ b/go.mod @@ -10,9 +10,9 @@ require ( github.com/prometheus/client_golang v1.7.1 github.com/stretchr/testify v1.6.1 go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5 - k8s.io/api v0.20.0 - k8s.io/apimachinery v0.20.0 - k8s.io/client-go v0.20.0 + k8s.io/api v0.20.1 + k8s.io/apimachinery v0.20.1 + k8s.io/client-go v0.20.1 k8s.io/utils v0.0.0-20201110183641-67b214c5f920 sigs.k8s.io/controller-runtime v0.7.0 sigs.k8s.io/controller-tools v0.4.1 diff --git a/go.sum b/go.sum index ea5ef940d0..abfa830f56 100644 --- a/go.sum +++ b/go.sum @@ -1147,8 +1147,8 @@ k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI= k8s.io/api v0.17.3/go.mod h1:YZ0OTkuw7ipbe305fMpIdf3GLXZKRigjtZaV5gzC2J0= k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI= -k8s.io/api v0.20.0 h1:WwrYoZNM1W1aQEbyl8HNG+oWGzLpZQBlcerS9BQw9yI= -k8s.io/api v0.20.0/go.mod h1:HyLC5l5eoS/ygQYl1BXBgFzWNlkHiAuyNAbevIn+FKg= +k8s.io/api v0.20.1 h1:ud1c3W3YNzGd6ABJlbFfKXBKXO+1KdGfcgGGNgFR03E= +k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= k8s.io/apiextensions-apiserver v0.19.2 h1:oG84UwiDsVDu7dlsGQs5GySmQHCzMhknfhFExJMz9tA= k8s.io/apiextensions-apiserver v0.19.2/go.mod h1:EYNjpqIAvNZe+svXVx9j4uBaVhTB4C94HkY3w058qcg= @@ -1156,16 +1156,16 @@ k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZ k8s.io/apimachinery v0.17.3/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g= k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= -k8s.io/apimachinery v0.20.0 h1:jjzbTJRXk0unNS71L7h3lxGDH/2HPxMPaQY+MjECKL8= -k8s.io/apimachinery v0.20.0/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.1 h1:LAhz8pKbgR8tUwn7boK+b2HZdt7MiTu2mkYtFMUjTRQ= +k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA= k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k= k8s.io/client-go v0.17.3/go.mod h1:cLXlTMtWHkuK4tD360KpWz2gG2KtdWEr/OT02i3emRQ= k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA= -k8s.io/client-go v0.20.0 h1:Xlax8PKbZsjX4gFvNtt4F5MoJ1V5prDvCuoq9B7iax0= -k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY= +k8s.io/client-go v0.20.1 h1:Qquik0xNFbK9aUG92pxHYsyfea5/RPO9o9bSywNor+M= +k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= k8s.io/code-generator v0.19.2/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= diff --git a/hack/install-controller-gen.sh b/hack/install-controller-gen.sh deleted file mode 100755 index 338010e27e..0000000000 --- a/hack/install-controller-gen.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -set -ex - -if [ -z "${GOPATH:-}" ]; then - eval "$(go env | grep GOPATH)" -fi - -OUTPUT=bin/controller-gen - -# Check for a vendor directory if any downstream forks use that dependency -# tracking method -if [ -d "vendor" ] -then - go build -o "${OUTPUT}" ./vendor/sigs.k8s.io/controller-tools/cmd/controller-gen - exit 0; -fi - - -CMDPATH="$GOPATH/pkg/mod/sigs.k8s.io/controller-tools@${1:-v0.4.1}/cmd/controller-gen" - -if [ ! -f "$CMDPATH" ] -then - go mod download -fi - -go build -o "${OUTPUT}" "$CMDPATH" diff --git a/hack/install-golangci-lint.sh b/hack/install-golangci-lint.sh deleted file mode 100755 index f905154f6d..0000000000 --- a/hack/install-golangci-lint.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -set -ex - -if [ -z "${GOPATH:-}" ]; then - eval "$(go env | grep GOPATH)" -fi - -OUTPUT=bin/golangci-lint - -# Check for a vendor directory if any downstream forks use that dependency -# tracking method -if [ -d "vendor" ] -then - go build -o "${OUTPUT}" ./vendor/github.com/golangci/golangci-lint/cmd/golangci-lint - exit 0; -fi - - -CMDPATH="$GOPATH/pkg/mod/github.com/golangci/golangci-lint@v1.32.0/cmd/golangci-lint" - -if [ ! -f "$CMDPATH" ] -then - go mod download -fi - -go build -o "${OUTPUT}" "$CMDPATH" diff --git a/hack/unit.sh b/hack/unit.sh index 03fd741a82..05bace2a83 100755 --- a/hack/unit.sh +++ b/hack/unit.sh @@ -5,20 +5,21 @@ set -eux IS_CONTAINER=${IS_CONTAINER:-false} ARTIFACTS=${ARTIFACTS:-/tmp} CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-podman}" +TEST_FLAGS="${TEST_FLAGS:--v}" if [ "${IS_CONTAINER}" != "false" ]; then eval "$(go env)" cd "${GOPATH}"/src/github.com/metal3-io/baremetal-operator export XDG_CACHE_HOME="/tmp/.cache" - export VERBOSE=-v export COVER_PROFILE="${ARTIFACTS}"/cover.out - make -e unit-cover + TEST_FLAGS=${TEST_FLAGS} make -e unit-cover else "${CONTAINER_RUNTIME}" run --rm \ --env IS_CONTAINER=TRUE \ + --env TEST_FLAGS="${TEST_FLAGS}" \ --env DEPLOY_KERNEL_URL=http://172.22.0.1/images/ironic-python-agent.kernel \ --env DEPLOY_RAMDISK_URL=http://172.22.0.1/images/ironic-python-agent.initramfs \ --env IRONIC_ENDPOINT=http://localhost:6385/v1/ \ diff --git a/ironic-deployment/default/ironic_bmo_configmap.env b/ironic-deployment/default/ironic_bmo_configmap.env index 09884370f6..0849ea4a5f 100644 --- a/ironic-deployment/default/ironic_bmo_configmap.env +++ b/ironic-deployment/default/ironic_bmo_configmap.env @@ -8,3 +8,4 @@ IRONIC_INSPECTOR_ENDPOINT=http://172.22.0.2:5050/v1/ CACHEURL=http://172.22.0.1/images IRONIC_FAST_TRACK=true IRONIC_KERNEL_PARAMS=console=ttyS0 +INSPECTOR_REVERSE_PROXY_SETUP=false diff --git a/ironic-deployment/ironic/ironic.yaml b/ironic-deployment/ironic/ironic.yaml index 1043186b56..db8c223b8a 100644 --- a/ironic-deployment/ironic/ironic.yaml +++ b/ironic-deployment/ironic/ironic.yaml @@ -42,20 +42,6 @@ spec: secretKeyRef: name: mariadb-password key: password - - name: ironic-httpd - image: quay.io/metal3-io/ironic - imagePullPolicy: Always - securityContext: - capabilities: - add: ["NET_ADMIN"] - command: - - /bin/runhttpd - volumeMounts: - - mountPath: /shared - name: ironic-data-volume - envFrom: - - configMapRef: - name: ironic-bmo-configmap - name: ironic-api image: quay.io/metal3-io/ironic imagePullPolicy: Always @@ -104,6 +90,14 @@ spec: envFrom: - configMapRef: name: ironic-bmo-configmap + - name: httpd-reverse-proxy + image: quay.io/metal3-io/ironic-inspector + imagePullPolicy: Always + envFrom: + - configMapRef: + name: ironic-bmo-configmap + command: + - /bin/runhttpd - name: ironic-inspector-log-watch image: quay.io/metal3-io/ironic-inspector imagePullPolicy: Always @@ -126,4 +120,4 @@ spec: name: ironic-data-volume volumes: - name: ironic-data-volume - emptyDir: {} \ No newline at end of file + emptyDir: {} diff --git a/main.go b/main.go index 0e982b595b..5fea6ea657 100644 --- a/main.go +++ b/main.go @@ -31,10 +31,8 @@ import ( metal3iov1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" metal3iocontroller "github.com/metal3-io/baremetal-operator/controllers/metal3.io" - "github.com/metal3-io/baremetal-operator/pkg/bmc" "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/empty" "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/version" @@ -105,13 +103,18 @@ func main() { printVersion() + leaderElectionNamespace := os.Getenv("POD_NAMESPACE") + if leaderElectionNamespace == "" { + leaderElectionNamespace = watchNamespace + } + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, MetricsBindAddress: metricsAddr, Port: 0, // Add flag with default of 9443 when adding webhooks LeaderElection: enableLeaderElection, LeaderElectionID: "baremetal-operator", - LeaderElectionNamespace: watchNamespace, + LeaderElectionNamespace: leaderElectionNamespace, Namespace: watchNamespace, HealthProbeBindAddress: healthAddr, }) @@ -120,24 +123,17 @@ func main() { os.Exit(1) } - provisionerFactory := func(host metal3iov1alpha1.BareMetalHost, bmcCreds bmc.Credentials, publish provisioner.EventPublisher) (provisioner.Provisioner, error) { - isUnmanaged := host.Spec.ExternallyProvisioned && !host.HasBMCDetails() - - hostCopy := host.DeepCopy() - - if runInTestMode { - ctrl.Log.Info("using test provisioner") - fix := fixture.Fixture{} - return fix.New(*hostCopy, bmcCreds, publish) - } else if runInDemoMode { - ctrl.Log.Info("using demo provisioner") - return demo.New(*hostCopy, bmcCreds, publish) - } else if isUnmanaged { - ctrl.Log.Info("using empty provisioner") - return empty.New(*hostCopy, bmcCreds, publish) - } + var provisionerFactory provisioner.Factory + if runInTestMode { + ctrl.Log.Info("using test provisioner") + fix := fixture.Fixture{} + provisionerFactory = fix.New + } else if runInDemoMode { + ctrl.Log.Info("using demo provisioner") + provisionerFactory = demo.New + } else { ironic.LogStartup() - return ironic.New(*hostCopy, bmcCreds, publish) + provisionerFactory = ironic.New } if err = (&metal3iocontroller.BareMetalHostReconciler{ diff --git a/pkg/bmc/access_test.go b/pkg/bmc/access_test.go index 9125a8d8cd..c80b787498 100644 --- a/pkg/bmc/access_test.go +++ b/pkg/bmc/access_test.go @@ -538,6 +538,18 @@ func TestStaticDriverInfo(t *testing.T) { vendor: "", }, + { + Scenario: "idrac redfish", + input: "idrac-redfish://192.168.122.1", + needsMac: true, + driver: "idrac", + boot: "ipxe", + management: "idrac-redfish", + power: "idrac-redfish", + raid: "no-raid", + vendor: "no-vendor", + }, + { Scenario: "ilo5 virtual media", input: "ilo5-virtualmedia://192.168.122.1", @@ -886,6 +898,18 @@ func TestDriverInfo(t *testing.T) { }, }, + { + Scenario: "idrac redfish", + input: "idrac-redfish://192.168.122.1/foo/bar", + expects: map[string]interface{}{ + "redfish_address": "https://192.168.122.1", + "redfish_system_id": "/foo/bar", + "redfish_password": "", + "redfish_username": "", + "redfish_verify_ca": false, + }, + }, + { Scenario: "idrac virtual media", input: "idrac-virtualmedia://192.168.122.1/foo/bar", diff --git a/pkg/bmc/redfish.go b/pkg/bmc/redfish.go index c68c85bbc9..7f9c652617 100644 --- a/pkg/bmc/redfish.go +++ b/pkg/bmc/redfish.go @@ -9,6 +9,7 @@ func init() { schemes := []string{"http", "https"} RegisterFactory("redfish", newRedfishAccessDetails, schemes) RegisterFactory("ilo5-redfish", newRedfishAccessDetails, schemes) + RegisterFactory("idrac-redfish", newRedfishiDracAccessDetails, schemes) } func redfishDetails(parsedURL *url.URL, disableCertificateVerification bool) *redfishAccessDetails { @@ -24,6 +25,12 @@ func newRedfishAccessDetails(parsedURL *url.URL, disableCertificateVerification return redfishDetails(parsedURL, disableCertificateVerification), nil } +func newRedfishiDracAccessDetails(parsedURL *url.URL, disableCertificateVerification bool) (AccessDetails, error) { + return &redfishiDracAccessDetails{ + *redfishDetails(parsedURL, disableCertificateVerification), + }, nil +} + type redfishAccessDetails struct { bmcType string host string @@ -31,6 +38,10 @@ type redfishAccessDetails struct { disableCertificateVerification bool } +type redfishiDracAccessDetails struct { + redfishAccessDetails +} + const redfishDefaultScheme = "https" func (a *redfishAccessDetails) Type() string { @@ -110,3 +121,29 @@ func (a *redfishAccessDetails) VendorInterface() string { func (a *redfishAccessDetails) SupportsSecureBoot() bool { return true } + +// iDrac Redfish Overrides + +func (a *redfishiDracAccessDetails) Driver() string { + return "idrac" +} + +func (a *redfishiDracAccessDetails) BootInterface() string { + return "ipxe" +} + +func (a *redfishiDracAccessDetails) ManagementInterface() string { + return "idrac-redfish" +} + +func (a *redfishiDracAccessDetails) PowerInterface() string { + return "idrac-redfish" +} + +func (a *redfishiDracAccessDetails) RAIDInterface() string { + return "no-raid" +} + +func (a *redfishiDracAccessDetails) VendorInterface() string { + return "no-vendor" +} diff --git a/pkg/provisioner/empty/empty.go b/pkg/provisioner/empty/empty.go deleted file mode 100644 index a12826ca8e..0000000000 --- a/pkg/provisioner/empty/empty.go +++ /dev/null @@ -1,94 +0,0 @@ -package empty - -import ( - logz "sigs.k8s.io/controller-runtime/pkg/log/zap" - - metal3v1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" - "github.com/metal3-io/baremetal-operator/pkg/bmc" - "github.com/metal3-io/baremetal-operator/pkg/provisioner" -) - -var log = logz.New().WithName("provisioner").WithName("empty") - -// Provisioner implements the provisioning.Provisioner interface -type emptyProvisioner struct { -} - -// New returns a new Empty Provisioner -func New(host metal3v1alpha1.BareMetalHost, bmcCreds bmc.Credentials, publisher provisioner.EventPublisher) (provisioner.Provisioner, error) { - return &emptyProvisioner{}, nil -} - -// ValidateManagementAccess tests the connection information for the -// host to verify that the location and credentials work. -func (p *emptyProvisioner) ValidateManagementAccess(credentialsChanged, force bool) (provisioner.Result, string, error) { - return provisioner.Result{}, "", nil -} - -// InspectHardware updates the HardwareDetails field of the host with -// details of devices discovered on the hardware. It may be called -// multiple times, and should return true for its dirty flag until the -// inspection is completed. -func (p *emptyProvisioner) InspectHardware(force bool) (provisioner.Result, *metal3v1alpha1.HardwareDetails, error) { - return provisioner.Result{}, nil, nil -} - -// UpdateHardwareState fetches the latest hardware state of the server -// and updates the HardwareDetails field of the host with details. It -// is expected to do this in the least expensive way possible, such as -// reading from a cache. -func (p *emptyProvisioner) UpdateHardwareState() (provisioner.HardwareState, error) { - return provisioner.HardwareState{}, nil -} - -// Adopt allows an externally-provisioned server to be adopted. -func (p *emptyProvisioner) Adopt(force bool) (provisioner.Result, error) { - return provisioner.Result{}, nil -} - -// Prepare remove existing configuration and set new configuration -func (p *emptyProvisioner) Prepare(unprepared bool) (result provisioner.Result, started bool, err error) { - return provisioner.Result{}, false, nil -} - -// Provision writes the image from the host spec to the host. It may -// be called multiple times, and should return true for its dirty flag -// until the deprovisioning operation is completed. -func (p *emptyProvisioner) Provision(hostConf provisioner.HostConfigData) (provisioner.Result, error) { - return provisioner.Result{}, nil -} - -// Deprovision removes the host from the image. It may be called -// multiple times, and should return true for its dirty flag until the -// deprovisioning operation is completed. -func (p *emptyProvisioner) Deprovision(force bool) (provisioner.Result, error) { - return provisioner.Result{}, nil -} - -// Delete removes the host from the provisioning system. It may be -// called multiple times, and should return true for its dirty flag -// until the deprovisioning operation is completed. -func (p *emptyProvisioner) Delete() (provisioner.Result, error) { - return provisioner.Result{}, nil -} - -// PowerOn ensures the server is powered on independently of any image -// provisioning operation. -func (p *emptyProvisioner) PowerOn() (provisioner.Result, error) { - return provisioner.Result{}, nil -} - -// PowerOff ensures the server is powered off independently of any image -// provisioning operation. -func (p *emptyProvisioner) PowerOff(rebootMode metal3v1alpha1.RebootMode) (provisioner.Result, error) { - return provisioner.Result{}, nil -} - -// IsReady always returns true for the empty provisioner -func (p *emptyProvisioner) IsReady() (bool, error) { - return true, nil -} - -func (p *emptyProvisioner) HasProvisioningCapacity() (result bool, err error) { - return true, nil -} diff --git a/pkg/provisioner/fixture/fixture.go b/pkg/provisioner/fixture/fixture.go index 109150adff..cb1e0d570f 100644 --- a/pkg/provisioner/fixture/fixture.go +++ b/pkg/provisioner/fixture/fixture.go @@ -57,6 +57,7 @@ type fixtureProvisioner struct { state *Fixture } +// Fixture contains persistent state for a particular host type Fixture struct { // counter to set the provisioner as ready BecomeReadyCounter int diff --git a/pkg/provisioner/ironic/delete_test.go b/pkg/provisioner/ironic/delete_test.go index 00a1eb9a9f..6230964f44 100644 --- a/pkg/provisioner/ironic/delete_test.go +++ b/pkg/provisioner/ironic/delete_test.go @@ -86,7 +86,7 @@ func TestDelete(t *testing.T) { }, { name: "not-ironic-node", - ironic: testserver.NewIronic(t).Ready().NoNode(nodeUUID).NoNode("myhost"), + ironic: testserver.NewIronic(t).Ready().NoNode(nodeUUID).NoNode("myns" + nameSeparator + "myhost").NoNode("myhost"), }, { name: "available-node", diff --git a/pkg/provisioner/ironic/findhost_test.go b/pkg/provisioner/ironic/findhost_test.go index 79f95d074d..e8b21af67e 100644 --- a/pkg/provisioner/ironic/findhost_test.go +++ b/pkg/provisioner/ironic/findhost_test.go @@ -23,7 +23,7 @@ func TestFindExistingHost(t *testing.T) { name: "no-node", hostName: "name", provisioningID: "uuid", - ironic: testserver.NewIronic(t).NoNode("name").NoNode("uuid"), + ironic: testserver.NewIronic(t).NoNode("myns" + nameSeparator + "name").NoNode("name").NoNode("uuid"), }, { name: "by-name", @@ -31,21 +31,21 @@ func TestFindExistingHost(t *testing.T) { provisioningID: "uuid", ironic: testserver.NewIronic(t).NoNode("uuid"). Node(nodes.Node{ - Name: "name", + Name: "myns" + nameSeparator + "name", UUID: "different-uuid", }), - nodeName: "name", + nodeName: "myns" + nameSeparator + "name", }, { name: "by-uuid", hostName: "name", provisioningID: "uuid", - ironic: testserver.NewIronic(t).NoNode("name"). + ironic: testserver.NewIronic(t).NoNode("myns" + nameSeparator + "name").NoNode("name"). Node(nodes.Node{ - Name: "different-name", + Name: "myns" + nameSeparator + "different-name", UUID: "uuid", }), - nodeName: "different-name", + nodeName: "myns" + nameSeparator + "different-name", }, } diff --git a/pkg/provisioner/ironic/ironic.go b/pkg/provisioner/ironic/ironic.go index b2d0c5940c..0570fb18f0 100644 --- a/pkg/provisioner/ironic/ironic.go +++ b/pkg/provisioner/ironic/ironic.go @@ -51,10 +51,11 @@ var ( const ( // See nodes.Node.PowerState for details - powerOn = "power on" - powerOff = "power off" - softPowerOff = "soft power off" - powerNone = "None" + powerOn = "power on" + powerOff = "power off" + softPowerOff = "soft power off" + powerNone = "None" + nameSeparator = "~" ) var bootModeCapabilities = map[metal3v1alpha1.BootMode]string{ @@ -72,6 +73,7 @@ func (e macAddressConflictError) Error() string { return fmt.Sprintf("MAC address %s conflicts with existing node %s", e.Address, e.ExistingNode) } +// NewMacAddressConflictError is a wrap for macAddressConflictError error func NewMacAddressConflictError(address, node string) error { return macAddressConflictError{Address: address, ExistingNode: node} } @@ -133,8 +135,6 @@ type ironicProvisioner struct { host metal3v1alpha1.BareMetalHost // a shorter path to the provisioning status data structure status *metal3v1alpha1.ProvisionStatus - // access parameters for the BMC - bmcAccess bmc.AccessDetails // credentials to log in to the BMC bmcCreds bmc.Credentials // a client for talking to ironic @@ -143,6 +143,8 @@ type ironicProvisioner struct { inspector *gophercloud.ServiceClient // a logger configured for this host log logr.Logger + // a debug logger configured for this host + debugLog logr.Logger // an event publisher for recording significant events publisher provisioner.EventPublisher } @@ -182,25 +184,19 @@ func newProvisionerWithSettings(host metal3v1alpha1.BareMetalHost, bmcCreds bmc. } func newProvisionerWithIronicClients(host metal3v1alpha1.BareMetalHost, bmcCreds bmc.Credentials, publisher provisioner.EventPublisher, clientIronic *gophercloud.ServiceClient, clientInspector *gophercloud.ServiceClient) (*ironicProvisioner, error) { - - bmcAccess, err := bmc.NewAccessDetails(host.Spec.BMC.Address, host.Spec.BMC.DisableCertificateVerification) - if err != nil { - return nil, errors.Wrap(err, "failed to parse BMC address information") - } - // Ensure we have a microversion high enough to get the features // we need. clientIronic.Microversion = "1.56" p := &ironicProvisioner{ host: host, status: &(host.Status.Provisioning), - bmcAccess: bmcAccess, bmcCreds: bmcCreds, client: clientIronic, inspector: clientInspector, - log: log.WithValues("host", host.Name), publisher: publisher, } + p.log = log.WithValues("host", p.ironicNodeNameFromHost()) + p.debugLog = p.log.V(1) return p, nil } @@ -230,6 +226,14 @@ func New(host metal3v1alpha1.BareMetalHost, bmcCreds bmc.Credentials, publisher clientIronicSingleton, clientInspectorSingleton) } +func (p *ironicProvisioner) bmcAccess() (bmc.AccessDetails, error) { + bmcAccess, err := bmc.NewAccessDetails(p.host.Spec.BMC.Address, p.host.Spec.BMC.DisableCertificateVerification) + if err != nil { + return nil, errors.Wrap(err, "failed to parse BMC address information") + } + return bmcAccess, nil +} + func (p *ironicProvisioner) validateNode(ironicNode *nodes.Node) (errorMessage string, err error) { var validationErrors []string @@ -291,7 +295,7 @@ func (p *ironicProvisioner) findExistingHost() (ironicNode *nodes.Node, err erro ironicNode, err = nodes.Get(p.client, p.status.ID).Extract() switch err.(type) { case nil: - p.log.Info("found existing node by ID") + p.debugLog.Info("found existing node by ID") return ironicNode, nil case gophercloud.ErrDefault404: // Look by ID failed, trying to lookup by hostname in case it was @@ -303,18 +307,25 @@ func (p *ironicProvisioner) findExistingHost() (ironicNode *nodes.Node, err erro } // Try to load the node by name - p.log.Info("looking for existing node by name", "name", p.host.Name) - ironicNode, err = nodes.Get(p.client, p.host.Name).Extract() - switch err.(type) { - case nil: - p.log.Info("found existing node by name") - return ironicNode, nil - case gophercloud.ErrDefault404: - p.log.Info( - fmt.Sprintf("node with name %s doesn't exist", p.host.Name)) - default: - return nil, errors.Wrap(err, - fmt.Sprintf("failed to find node by name %s", p.host.Name)) + nodeSearchList := []string{p.ironicNodeNameFromHost()} + if !strings.Contains(p.host.Name, nameSeparator) { + nodeSearchList = append(nodeSearchList, p.host.Name) + } + + for _, nodeName := range nodeSearchList { + p.debugLog.Info("looking for existing node by name", "name", nodeName) + ironicNode, err = nodes.Get(p.client, nodeName).Extract() + switch err.(type) { + case nil: + p.debugLog.Info("found existing node by name") + return ironicNode, nil + case gophercloud.ErrDefault404: + p.log.Info( + fmt.Sprintf("node with name %s doesn't exist", nodeName)) + default: + return nil, errors.Wrap(err, + fmt.Sprintf("failed to find node by name %s", nodeName)) + } } // Try to load the node by port address @@ -331,7 +342,7 @@ func (p *ironicProvisioner) findExistingHost() (ironicNode *nodes.Node, err erro ironicNode, err = nodes.Get(p.client, nodeUUID).Extract() switch err.(type) { case nil: - p.log.Info("found existing node by ID") + p.debugLog.Info("found existing node by ID") // If the node has a name, this means we didn't find it above. if ironicNode.Name != "" { @@ -361,9 +372,15 @@ func (p *ironicProvisioner) findExistingHost() (ironicNode *nodes.Node, err erro // FIXME(dhellmann): We should rename this method to describe what it // actually does. func (p *ironicProvisioner) ValidateManagementAccess(credentialsChanged, force bool) (result provisioner.Result, provID string, err error) { + bmcAccess, err := p.bmcAccess() + if err != nil { + result, err = operationFailed(err.Error()) + return + } + var ironicNode *nodes.Node - p.log.Info("validating management access") + p.debugLog.Info("validating management access") ironicNode, err = p.findExistingHost() if err != nil { @@ -378,14 +395,14 @@ func (p *ironicProvisioner) ValidateManagementAccess(credentialsChanged, force b // Some BMC types require a MAC address, so ensure we have one // when we need it. If not, place the host in an error state. - if p.bmcAccess.NeedsMAC() && p.host.Spec.BootMACAddress == "" { - msg := fmt.Sprintf("BMC driver %s requires a BootMACAddress value", p.bmcAccess.Type()) + if bmcAccess.NeedsMAC() && p.host.Spec.BootMACAddress == "" { + msg := fmt.Sprintf("BMC driver %s requires a BootMACAddress value", bmcAccess.Type()) p.log.Info(msg) result, err = operationFailed(msg) return } - driverInfo := p.bmcAccess.DriverInfo(p.bmcCreds) + driverInfo := bmcAccess.DriverInfo(p.bmcCreds) // FIXME(dhellmann): We need to get our IP on the // provisioning network from somewhere. driverInfo["deploy_kernel"] = deployKernelURL @@ -397,8 +414,8 @@ func (p *ironicProvisioner) ValidateManagementAccess(credentialsChanged, force b if ironicNode == nil { p.log.Info("registering host in ironic") - if p.host.Spec.BootMode == metal3v1alpha1.UEFISecureBoot && !p.bmcAccess.SupportsSecureBoot() { - msg := fmt.Sprintf("BMC driver %s does not support secure boot", p.bmcAccess.Type()) + if p.host.Spec.BootMode == metal3v1alpha1.UEFISecureBoot && !bmcAccess.SupportsSecureBoot() { + msg := fmt.Sprintf("BMC driver %s does not support secure boot", bmcAccess.Type()) p.log.Info(msg) result, err = operationFailed(msg) return @@ -407,16 +424,16 @@ func (p *ironicProvisioner) ValidateManagementAccess(credentialsChanged, force b ironicNode, err = nodes.Create( p.client, nodes.CreateOpts{ - Driver: p.bmcAccess.Driver(), - BootInterface: p.bmcAccess.BootInterface(), + Driver: bmcAccess.Driver(), + BootInterface: bmcAccess.BootInterface(), Name: p.host.Name, DriverInfo: driverInfo, DeployInterface: p.deployInterface(), InspectInterface: "inspector", - ManagementInterface: p.bmcAccess.ManagementInterface(), - PowerInterface: p.bmcAccess.PowerInterface(), - RAIDInterface: p.bmcAccess.RAIDInterface(), - VendorInterface: p.bmcAccess.VendorInterface(), + ManagementInterface: bmcAccess.ManagementInterface(), + PowerInterface: bmcAccess.PowerInterface(), + RAIDInterface: bmcAccess.RAIDInterface(), + VendorInterface: bmcAccess.VendorInterface(), Properties: map[string]interface{}{ "capabilities": bootModeCapabilities[p.host.Status.Provisioning.BootMode], }, @@ -490,12 +507,12 @@ func (p *ironicProvisioner) ValidateManagementAccess(credentialsChanged, force b // if there are differences. provID = ironicNode.UUID - if ironicNode.Name == "" { + if ironicNode.Name != p.ironicNodeNameFromHost() { updates := nodes.UpdateOpts{ nodes.UpdateOperation{ Op: nodes.ReplaceOp, Path: "/name", - Value: p.host.Name, + Value: p.ironicNodeNameFromHost(), }, } ironicNode, err = nodes.Update(p.client, ironicNode.UUID, updates).Extract() @@ -584,18 +601,16 @@ func (p *ironicProvisioner) ValidateManagementAccess(credentialsChanged, force b return case nodes.Manageable: - p.log.Info("have manageable host") return case nodes.Available: // The host is fully registered (and probably wasn't cleanly // deleted previously) - p.log.Info("have available host") return case nodes.Active: // The host is already running, maybe it's a master? - p.log.Info("have active host", "image_source", ironicNode.InstanceInfo["image_source"]) + p.debugLog.Info("have active host", "image_source", ironicNode.InstanceInfo["image_source"]) return default: @@ -709,7 +724,7 @@ func (p *ironicProvisioner) InspectHardware(force bool) (result provisioner.Resu result, err = operationFailed(status.Error) return } - if !status.Finished || nodes.ProvisionState(ironicNode.ProvisionState) == nodes.InspectWait { + if !status.Finished || (nodes.ProvisionState(ironicNode.ProvisionState) == nodes.Inspecting || nodes.ProvisionState(ironicNode.ProvisionState) == nodes.InspectWait) { p.log.Info("inspection in progress", "started_at", status.StartedAt) result, err = operationContinuing(introspectionRequeueDelay) return @@ -736,7 +751,7 @@ func (p *ironicProvisioner) InspectHardware(force bool) (result provisioner.Resu // is expected to do this in the least expensive way possible, such as // reading from a cache. func (p *ironicProvisioner) UpdateHardwareState() (hwState provisioner.HardwareState, err error) { - p.log.Info("updating hardware state") + p.debugLog.Info("updating hardware state") ironicNode, err := p.findExistingHost() if err != nil { @@ -760,92 +775,60 @@ func (p *ironicProvisioner) UpdateHardwareState() (hwState provisioner.HardwareS return } -func (p *ironicProvisioner) getImageUpdateOptsForNode(ironicNode *nodes.Node, imageData *metal3v1alpha1.Image) (updates nodes.UpdateOpts, err error) { - checksum, checksumType, ok := imageData.GetChecksum() - if !ok { - p.log.Info("image/checksum not found for host") - return +func (p *ironicProvisioner) setLiveIsoUpdateOptsForNode(ironicNode *nodes.Node, imageData *metal3v1alpha1.Image, updates nodes.UpdateOpts) (nodes.UpdateOpts, error) { + var op nodes.UpdateOp + + if _, ok := ironicNode.InstanceInfo["boot_iso"]; !ok { + op = nodes.AddOp + p.log.Info("adding boot_iso") + } else { + op = nodes.ReplaceOp + p.log.Info("updating boot_iso") } - // instance_uuid - p.log.Info("setting instance_uuid") + updates = append( + updates, + nodes.UpdateOperation{ + Op: op, + Path: "/instance_info/boot_iso", + Value: imageData.URL, + }, + ) updates = append( updates, nodes.UpdateOperation{ Op: nodes.ReplaceOp, - Path: "/instance_uuid", - Value: string(p.host.ObjectMeta.UID), + Path: "/deploy_interface", + Value: "ramdisk", }, ) - - // Secure boot is a normal capability that goes into instance_info (we - // also put it to properties for consistency, although it's not - // strictly required in our case). - - if p.host.Spec.BootMode == metal3v1alpha1.UEFISecureBoot { - updates = append(updates, nodes.UpdateOperation{ - Op: nodes.AddOp, - Path: "/instance_info/capabilities", - // Instance info capabilities were invented later and - // use a normal JSON mapping instead of a custom - // string value. - Value: map[string]string{ - "secure_boot": "true", - }, - }) - } else { - updates = append(updates, nodes.UpdateOperation{ - Op: nodes.AddOp, - Path: "/instance_info/capabilities", - Value: map[string]string{}, - }) + // remove any image_source or checksum options + removals := []string{ + "image_source", "image_os_hash_value", "image_os_hash_algo", "image_checksum"} + op = nodes.RemoveOp + for _, item := range removals { + if _, ok := ironicNode.InstanceInfo[item]; ok { + p.log.Info("removing " + item) + updates = append( + updates, + nodes.UpdateOperation{ + Op: op, + Path: "/instance_info/" + item, + }, + ) + } } + return updates, nil +} - // live-iso format - var op nodes.UpdateOp - if imageData.DiskFormat != nil && *imageData.DiskFormat == "live-iso" { - if _, ok := ironicNode.InstanceInfo["boot_iso"]; !ok { - op = nodes.AddOp - p.log.Info("adding boot_iso") - } else { - op = nodes.ReplaceOp - p.log.Info("updating boot_iso") - } - updates = append( - updates, - nodes.UpdateOperation{ - Op: op, - Path: "/instance_info/boot_iso", - Value: imageData.URL, - }, - ) - updates = append( - updates, - nodes.UpdateOperation{ - Op: nodes.ReplaceOp, - Path: "/deploy_interface", - Value: "ramdisk", - }, - ) - // remove any image_source or checksum options - removals := []string{ - "image_source", "image_os_hash_value", "image_os_hash_algo", "image_checksum"} - op = nodes.RemoveOp - for _, item := range removals { - if _, ok := ironicNode.InstanceInfo[item]; ok { - p.log.Info("removing " + item) - updates = append( - updates, - nodes.UpdateOperation{ - Op: op, - Path: "/instance_info/" + item, - }, - ) - } - } +func (p *ironicProvisioner) setDirectDeployUpdateOptsForNode(ironicNode *nodes.Node, imageData *metal3v1alpha1.Image, updates nodes.UpdateOpts) (nodes.UpdateOpts, error) { + checksum, checksumType, ok := imageData.GetChecksum() + if !ok { + p.log.Info("image/checksum not found for host") return updates, nil } - // Set deploy_interface direct when not booting a live-iso + var op nodes.UpdateOp + updates = append( updates, nodes.UpdateOperation{ @@ -953,6 +936,50 @@ func (p *ironicProvisioner) getImageUpdateOptsForNode(ironicNode *nodes.Node, im return updates, nil } +func (p *ironicProvisioner) getImageUpdateOptsForNode(ironicNode *nodes.Node, imageData *metal3v1alpha1.Image) (updates nodes.UpdateOpts, err error) { + // instance_uuid + p.log.Info("setting instance_uuid") + updates = append( + updates, + nodes.UpdateOperation{ + Op: nodes.ReplaceOp, + Path: "/instance_uuid", + Value: string(p.host.ObjectMeta.UID), + }, + ) + + // Secure boot is a normal capability that goes into instance_info (we + // also put it to properties for consistency, although it's not + // strictly required in our case). + + if p.host.Spec.BootMode == metal3v1alpha1.UEFISecureBoot { + updates = append(updates, nodes.UpdateOperation{ + Op: nodes.AddOp, + Path: "/instance_info/capabilities", + // Instance info capabilities were invented later and + // use a normal JSON mapping instead of a custom + // string value. + Value: map[string]string{ + "secure_boot": "true", + }, + }) + } else { + updates = append(updates, nodes.UpdateOperation{ + Op: nodes.AddOp, + Path: "/instance_info/capabilities", + Value: map[string]string{}, + }) + } + + // Set live-iso format options + if imageData.DiskFormat != nil && *imageData.DiskFormat == "live-iso" { + return p.setLiveIsoUpdateOptsForNode(ironicNode, imageData, updates) + } + + // Set deploy_interface direct options when not booting a live-iso + return p.setDirectDeployUpdateOptsForNode(ironicNode, imageData, updates) +} + func (p *ironicProvisioner) getUpdateOptsForNode(ironicNode *nodes.Node) (updates nodes.UpdateOpts, err error) { hwProf, err := hardware.GetProfile(p.host.HardwareProfile()) @@ -1186,10 +1213,9 @@ func (p *ironicProvisioner) Adopt(force bool) (result provisioner.Result, err er Target: nodes.TargetAdopt, }, ) - } else { - return operationFailed(fmt.Sprintf("Host adoption failed: %s", - ironicNode.LastError)) } + return operationFailed(fmt.Sprintf("Host adoption failed: %s", + ironicNode.LastError)) case nodes.Active: default: } @@ -1220,12 +1246,12 @@ func (p *ironicProvisioner) ironicHasSameImage(ironicNode *nodes.Node) (sameImag return sameImage } -func (p *ironicProvisioner) buildManualCleaningSteps() (cleanSteps []nodes.CleanStep, err error) { +func (p *ironicProvisioner) buildManualCleaningSteps(bmcAccess bmc.AccessDetails) (cleanSteps []nodes.CleanStep, err error) { // Build raid clean steps - if p.bmcAccess.RAIDInterface() != "no-raid" { + if bmcAccess.RAIDInterface() != "no-raid" { cleanSteps = append(cleanSteps, BuildRAIDCleanSteps(p.host.Status.Provisioning.RAID)...) } else if p.host.Status.Provisioning.RAID != nil { - return nil, fmt.Errorf("RAID settings are defined, but the node's driver %s does not support RAID", p.bmcAccess.Driver()) + return nil, fmt.Errorf("RAID settings are defined, but the node's driver %s does not support RAID", bmcAccess.Driver()) } // TODO: Add manual cleaning steps for host configuration @@ -1233,8 +1259,8 @@ func (p *ironicProvisioner) buildManualCleaningSteps() (cleanSteps []nodes.Clean return } -func (p *ironicProvisioner) startManualCleaning(ironicNode *nodes.Node) (success bool, result provisioner.Result, err error) { - if p.bmcAccess.RAIDInterface() != "no-raid" { +func (p *ironicProvisioner) startManualCleaning(bmcAccess bmc.AccessDetails, ironicNode *nodes.Node) (success bool, result provisioner.Result, err error) { + if bmcAccess.RAIDInterface() != "no-raid" { // Set raid configuration err = setTargetRAIDCfg(p, ironicNode) if err != nil { @@ -1244,7 +1270,7 @@ func (p *ironicProvisioner) startManualCleaning(ironicNode *nodes.Node) (success } // Build manual clean steps - cleanSteps, err := p.buildManualCleaningSteps() + cleanSteps, err := p.buildManualCleaningSteps(bmcAccess) if err != nil { result, err = operationFailed(err.Error()) return @@ -1268,8 +1294,13 @@ func (p *ironicProvisioner) startManualCleaning(ironicNode *nodes.Node) (success // Prepare remove existing configuration and set new configuration. // If `started` is true, it means that we successfully executed `tryChangeNodeProvisionState`. func (p *ironicProvisioner) Prepare(unprepared bool) (result provisioner.Result, started bool, err error) { - var ironicNode *nodes.Node + bmcAccess, err := p.bmcAccess() + if err != nil { + result, err = transientError(err) + return + } + var ironicNode *nodes.Node if ironicNode, err = p.findExistingHost(); err != nil { result, err = transientError(errors.Wrap(err, "could not find host to clean")) return @@ -1282,7 +1313,7 @@ func (p *ironicProvisioner) Prepare(unprepared bool) (result provisioner.Result, switch nodes.ProvisionState(ironicNode.ProvisionState) { case nodes.Available: var cleanSteps []nodes.CleanStep - cleanSteps, err = p.buildManualCleaningSteps() + cleanSteps, err = p.buildManualCleaningSteps(bmcAccess) if err != nil { result, err = operationFailed(err.Error()) return @@ -1298,7 +1329,7 @@ func (p *ironicProvisioner) Prepare(unprepared bool) (result provisioner.Result, case nodes.Manageable: if unprepared { - started, result, err = p.startManualCleaning(ironicNode) + started, result, err = p.startManualCleaning(bmcAccess, ironicNode) return } // Manual clean finished @@ -1815,9 +1846,13 @@ func (p *ironicProvisioner) softPowerOff() (result provisioner.Result, err error return result, nil } +func (p *ironicProvisioner) ironicNodeNameFromHost() string { + return p.host.Namespace + nameSeparator + p.host.Name +} + // IsReady checks if the provisioning backend is available func (p *ironicProvisioner) IsReady() (result bool, err error) { - p.log.Info("verifying ironic provisioner dependencies") + p.debugLog.Info("verifying ironic provisioner dependencies") checker := newIronicDependenciesChecker(p.client, p.inspector, p.log) return checker.IsReady() @@ -1832,7 +1867,7 @@ func (p *ironicProvisioner) HasProvisioningCapacity() (result bool, err error) { } // If the current host is already under processing then let's skip the test - if _, ok := hosts[p.host.Name]; ok { + if _, ok := hosts[p.ironicNodeNameFromHost()]; ok { return true, nil } diff --git a/pkg/provisioner/ironic/ironic_test.go b/pkg/provisioner/ironic/ironic_test.go index 77f0d71303..a3561d3132 100644 --- a/pkg/provisioner/ironic/ironic_test.go +++ b/pkg/provisioner/ironic/ironic_test.go @@ -1,11 +1,15 @@ package ironic import ( + "testing" + + "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" logf "sigs.k8s.io/controller-runtime/pkg/log" logz "sigs.k8s.io/controller-runtime/pkg/log/zap" metal3v1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1" + "github.com/metal3-io/baremetal-operator/pkg/bmc" // We don't use this package directly here, but need it imported // so it registers its test fixture with the other BMC access @@ -81,3 +85,13 @@ func makeHostLiveIso() (host metal3v1alpha1.BareMetalHost) { // Implements provisioner.EventPublisher to swallow events for tests. func nullEventPublisher(reason, message string) {} + +func TestNewNoBMCDetails(t *testing.T) { + // Create a host without BMC details + host := makeHost() + host.Spec.BMC = metal3v1alpha1.BMCDetails{} + + prov, err := New(host, bmc.Credentials{}, nullEventPublisher) + assert.Equal(t, nil, err) + assert.NotEqual(t, nil, prov) +} diff --git a/pkg/provisioner/ironic/provisioncapacity_test.go b/pkg/provisioner/ironic/provisioncapacity_test.go index f37f0c2737..1b54c124ae 100644 --- a/pkg/provisioner/ironic/provisioncapacity_test.go +++ b/pkg/provisioner/ironic/provisioncapacity_test.go @@ -62,7 +62,7 @@ func TestHasProvisioningCapacity(t *testing.T) { allNodes := []nodes.Node{} for n, state := range tc.nodeStates { allNodes = append(allNodes, nodes.Node{ - Name: fmt.Sprintf("node-%d", n), + Name: fmt.Sprintf("myns%snode-%d", nameSeparator, n), ProvisionState: string(state), }) } diff --git a/pkg/provisioner/ironic/testserver/ironic.go b/pkg/provisioner/ironic/testserver/ironic.go index 56e7f2de76..8c8c3e94d5 100644 --- a/pkg/provisioner/ironic/testserver/ironic.go +++ b/pkg/provisioner/ironic/testserver/ironic.go @@ -168,6 +168,7 @@ func (m *IronicMock) NodeError(name string, errorCode int) *IronicMock { return m } +// NodeCreateCallback type is the callback mock for CreateNodes type NodeCreateCallback func(node nodes.Node) // CreateNodes configures the server so POSTing to /v1/nodes saves the data diff --git a/pkg/provisioner/ironic/updatehardwarestate_test.go b/pkg/provisioner/ironic/updatehardwarestate_test.go index e4e89b0b80..691a2f0a2e 100644 --- a/pkg/provisioner/ironic/updatehardwarestate_test.go +++ b/pkg/provisioner/ironic/updatehardwarestate_test.go @@ -90,15 +90,15 @@ func TestUpdateHardwareState(t *testing.T) { name: "node-not-found-by-name", hostName: "worker-0", - ironic: testserver.NewIronic(t).Ready().NoNode(nodeUUID).NodeError("myhost", http.StatusGatewayTimeout), + ironic: testserver.NewIronic(t).Ready().NoNode(nodeUUID).NodeError("myns.myhost", http.StatusGatewayTimeout), - expectedError: "failed to find existing host: failed to find node by name worker-0: EOF", + expectedError: "failed to find existing host: failed to find node by name myns.worker-0: EOF", expectUnreadablePower: true, }, { name: "not-ironic-node", - ironic: testserver.NewIronic(t).Ready().NoNode(nodeUUID).NoNode("myhost"), + ironic: testserver.NewIronic(t).Ready().NoNode(nodeUUID).NoNode("myns" + nameSeparator + "myhost").NoNode("myhost"), expectedError: "Host not registered", diff --git a/pkg/provisioner/ironic/validatemanagementaccess_test.go b/pkg/provisioner/ironic/validatemanagementaccess_test.go index 6ac15ca879..3f8b13aec9 100644 --- a/pkg/provisioner/ironic/validatemanagementaccess_test.go +++ b/pkg/provisioner/ironic/validatemanagementaccess_test.go @@ -22,7 +22,7 @@ func TestValidateManagementAccessNoMAC(t *testing.T) { host.Spec.BootMACAddress = "" host.Status.Provisioning.ID = "" // so we don't lookup by uuid - ironic := testserver.NewIronic(t).Ready().NoNode(host.Name) + ironic := testserver.NewIronic(t).Ready().NoNode(host.Namespace + nameSeparator + host.Name).NoNode(host.Name) ironic.Start() defer ironic.Stop() @@ -50,7 +50,7 @@ func TestValidateManagementAccessMACOptional(t *testing.T) { // Set up ironic server to return the node ironic := testserver.NewIronic(t).Ready(). Node(nodes.Node{ - Name: host.Name, + Name: host.Namespace + nameSeparator + host.Name, UUID: host.Status.Provisioning.ID, }) ironic.Start() @@ -85,7 +85,7 @@ func TestValidateManagementAccessCreateNodeNoImage(t *testing.T) { createdNode = &node } - ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).NoNode(host.Name) + ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).NoNode(host.Namespace + nameSeparator + host.Name).NoNode(host.Name) ironic.Start() defer ironic.Stop() @@ -120,7 +120,7 @@ func TestValidateManagementAccessCreateWithImage(t *testing.T) { createdNode = &node } - ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).NoNode(host.Name) + ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).NoNode(host.Namespace + nameSeparator + host.Name).NoNode(host.Name) ironic.AddDefaultResponse("/v1/nodes/node-0", "PATCH", http.StatusOK, "{}") ironic.Start() defer ironic.Stop() @@ -158,7 +158,7 @@ func TestValidateManagementAccessCreateWithLiveIso(t *testing.T) { createdNode = &node } - ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).NoNode(host.Name) + ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).NoNode(host.Namespace + nameSeparator + host.Name).NoNode(host.Name) ironic.AddDefaultResponse("/v1/nodes/node-0", "PATCH", http.StatusOK, "{}") ironic.Start() defer ironic.Stop() @@ -236,7 +236,7 @@ func TestValidateManagementAccessCreateNodeImageSpecOrStatus(t *testing.T) { createdNode = &node } - ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).NoNode(host.Name) + ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).NoNode(host.Namespace + nameSeparator + host.Name).NoNode(host.Name) ironic.NodeUpdate(nodes.Node{UUID: "node-0"}) ironic.Start() defer ironic.Stop() @@ -278,7 +278,7 @@ func TestValidateManagementAccessExistingNode(t *testing.T) { } ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).Node(nodes.Node{ - Name: host.Name, + Name: host.Namespace + nameSeparator + host.Name, UUID: "uuid", }) ironic.Start() @@ -300,6 +300,43 @@ func TestValidateManagementAccessExistingNode(t *testing.T) { assert.Equal(t, "uuid", provID) } +func TestValidateManagementAccessExistingNodeNameUpdate(t *testing.T) { + // Create a host without a bootMACAddress and with a BMC that + // does not require one. + host := makeHost() + host.Spec.BootMACAddress = "" + host.Status.Provisioning.ID = "uuid" + + ironic := testserver.NewIronic(t). + Node( + nodes.Node{ + Name: host.Name, + UUID: "uuid", + }). + NodeUpdate( + nodes.Node{ + Name: host.Namespace + nameSeparator + host.Name, + UUID: "uuid", + }) + ironic.Start() + defer ironic.Stop() + + auth := clients.AuthConfig{Type: clients.NoAuth} + prov, err := newProvisionerWithSettings(host, bmc.Credentials{}, nullEventPublisher, + ironic.Endpoint(), auth, testserver.NewInspector(t).Endpoint(), auth, + ) + if err != nil { + t.Fatalf("could not create provisioner: %s", err) + } + + result, _, err := prov.ValidateManagementAccess(false, false) + if err != nil { + t.Fatalf("error from ValidateManagementAccess: %s", err) + } + assert.Equal(t, "", result.ErrorMessage) + assert.Equal(t, false, result.Dirty) +} + func TestValidateManagementAccessExistingNodeContinue(t *testing.T) { statuses := []nodes.ProvisionState{ nodes.Manageable, @@ -340,7 +377,7 @@ func TestValidateManagementAccessExistingNodeContinue(t *testing.T) { } ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).Node(nodes.Node{ - Name: host.Name, + Name: host.Namespace + nameSeparator + host.Name, UUID: "", // to match status in host ProvisionState: string(status), }) @@ -384,7 +421,7 @@ func TestValidateManagementAccessExistingNodeWaiting(t *testing.T) { } node := nodes.Node{ - Name: host.Name, + Name: host.Namespace + nameSeparator + host.Name, UUID: "uuid", // to match status in host ProvisionState: string(status), } @@ -420,12 +457,12 @@ func TestValidateManagementAccessNewCredentials(t *testing.T) { ironic := testserver.NewIronic(t). Node( nodes.Node{ - Name: host.Name, + Name: host.Namespace + nameSeparator + host.Name, UUID: "uuid", }). NodeUpdate( nodes.Node{ - Name: host.Name, + Name: host.Namespace + nameSeparator + host.Name, UUID: "uuid", DriverInfo: map[string]interface{}{ "test_address": "test.bmc", @@ -472,6 +509,7 @@ func TestValidateManagementAccessLinkExistingIronicNodeByMAC(t *testing.T) { } ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).Node(existingNode).Port(existingNodePort) + ironic.AddDefaultResponse("/v1/nodes/myns"+nameSeparator+"myhost", "GET", http.StatusNotFound, "") ironic.AddDefaultResponse("/v1/nodes/myhost", "GET", http.StatusNotFound, "") ironic.AddDefaultResponse("/v1/nodes/"+existingNode.UUID, "PATCH", http.StatusOK, "{}") ironic.Start() @@ -515,6 +553,7 @@ func TestValidateManagementAccessExistingPortWithWrongUUID(t *testing.T) { } ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).Node(existingNode).Port(existingNodePort) + ironic.AddDefaultResponse("/v1/nodes/myns"+nameSeparator+"myhost", "GET", http.StatusNotFound, "") ironic.AddDefaultResponse("/v1/nodes/myhost", "GET", http.StatusNotFound, "") ironic.AddDefaultResponse("/v1/nodes/random-wrong-id", "GET", http.StatusNotFound, "") ironic.Start() @@ -558,6 +597,7 @@ func TestValidateManagementAccessExistingPortButHasName(t *testing.T) { } ironic := testserver.NewIronic(t).Ready().CreateNodes(createCallback).Node(existingNode).Port(existingNodePort) + ironic.AddDefaultResponse("/v1/nodes/myns"+nameSeparator+"myhost", "GET", http.StatusNotFound, "") ironic.AddDefaultResponse("/v1/nodes/myhost", "GET", http.StatusNotFound, "") ironic.Start() defer ironic.Stop() @@ -583,7 +623,7 @@ func TestValidateManagementAccessAddTwoHostsWithSameMAC(t *testing.T) { existingNode := nodes.Node{ UUID: "33ce8659-7400-4c68-9535-d10766f07a58", - Name: "myhost", + Name: "myns" + nameSeparator + "myhost", } existingNodePort := ports.Port{ @@ -620,3 +660,52 @@ func TestValidateManagementAccessAddTwoHostsWithSameMAC(t *testing.T) { assert.Equal(t, "", result.ErrorMessage) assert.NotEqual(t, "", provID) } + +func TestValidateManagementAccessUnsupportedSecureBoot(t *testing.T) { + // Create a host without a bootMACAddress and with a BMC that + // requires one. + host := makeHost() + host.Spec.BootMode = metal3v1alpha1.UEFISecureBoot + host.Status.Provisioning.ID = "" // so we don't lookup by uuid + + ironic := testserver.NewIronic(t).Ready().NoNode("myns" + nameSeparator + host.Name).NoNode(host.Name) + ironic.Start() + defer ironic.Stop() + + auth := clients.AuthConfig{Type: clients.NoAuth} + prov, err := newProvisionerWithSettings(host, bmc.Credentials{}, nil, + ironic.Endpoint(), auth, testserver.NewInspector(t).Endpoint(), auth, + ) + if err != nil { + t.Fatalf("could not create provisioner: %s", err) + } + + result, _, err := prov.ValidateManagementAccess(false, false) + if err != nil { + t.Fatalf("error from ValidateManagementAccess: %s", err) + } + assert.Contains(t, result.ErrorMessage, "does not support secure boot") +} + +func TestValidateManagementAccessNoBMCDetails(t *testing.T) { + ironic := testserver.NewIronic(t).Ready() + ironic.Start() + defer ironic.Stop() + + host := makeHost() + host.Spec.BMC = metal3v1alpha1.BMCDetails{} + + auth := clients.AuthConfig{Type: clients.NoAuth} + prov, err := newProvisionerWithSettings(host, bmc.Credentials{}, nullEventPublisher, + ironic.Endpoint(), auth, testserver.NewInspector(t).Endpoint(), auth, + ) + if err != nil { + t.Fatalf("could not create provisioner: %s", err) + } + + result, _, err := prov.ValidateManagementAccess(false, false) + if err != nil { + t.Fatalf("error from ValidateManagementAccess: %s", err) + } + assert.Equal(t, "failed to parse BMC address information: missing BMC address", result.ErrorMessage) +} diff --git a/tools/deploy.sh b/tools/deploy.sh index c1774469b2..bcb7be8286 100755 --- a/tools/deploy.sh +++ b/tools/deploy.sh @@ -18,12 +18,12 @@ DEPLOY_IRONIC="${2,,}" DEPLOY_TLS="${3,,}" DEPLOY_BASIC_AUTH="${4,,}" DEPLOY_KEEPALIVED="${5,,}" - IRONIC_HOST="${IRONIC_HOST}" IRONIC_HOST_IP="${IRONIC_HOST_IP}" MARIADB_HOST="${MARIADB_HOST:-"mariaDB"}" MARIADB_HOST_IP="${MARIADB_HOST_IP:-"127.0.0.1"}" KUBECTL_ARGS="${KUBECTL_ARGS:-""}" +KUSTOMIZE="go run sigs.k8s.io/kustomize/kustomize/v3" SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" @@ -201,18 +201,18 @@ if [ "${DEPLOY_TLS}" == "true" ]; then fi fi -pushd "${SCRIPTDIR}" -make bin/kustomize -popd - if [ "${DEPLOY_BMO}" == "true" ]; then + pushd "${SCRIPTDIR}" # shellcheck disable=SC2086 - "${SCRIPTDIR}/bin/kustomize" build "${BMO_SCENARIO}" | kubectl apply ${KUBECTL_ARGS} -f - + ${KUSTOMIZE} build "${BMO_SCENARIO}" | kubectl apply ${KUBECTL_ARGS} -f - + popd fi if [ "${DEPLOY_IRONIC}" == "true" ]; then + pushd "${SCRIPTDIR}" # shellcheck disable=SC2086 - "${SCRIPTDIR}/bin/kustomize" build "${IRONIC_SCENARIO}" | kubectl apply ${KUBECTL_ARGS} -f - + ${KUSTOMIZE} build "${IRONIC_SCENARIO}" | kubectl apply ${KUBECTL_ARGS} -f - + popd fi if [ "${DEPLOY_BASIC_AUTH}" == "true" ]; then diff --git a/tools/install_kustomize.sh b/tools/install_kustomize.sh deleted file mode 100755 index 2f1c7ed3ff..0000000000 --- a/tools/install_kustomize.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -set -ex - -if [ -z "${GOPATH:-}" ]; then - eval "$(go env | grep GOPATH)" -fi - -OUTPUT=bin/kustomize - -# Check for a vendor directory if any downstream forks use that dependency -# tracking method -if [ -d "vendor" ] -then - go build -o "${OUTPUT}" ./vendor/sigs.k8s.io/kustomize/kustomize/v3 - exit 0; -fi - -CMDPATH="$GOPATH/pkg/mod/sigs.k8s.io/kustomize/kustomize/v3@v3.8.5" - -if [ ! -f "$CMDPATH" ] -then - go mod download -fi - -go build -o "${OUTPUT}" "$CMDPATH" diff --git a/tools/remove_local_ironic.sh b/tools/remove_local_ironic.sh index 587fb6cbf5..b20ebed362 100755 --- a/tools/remove_local_ironic.sh +++ b/tools/remove_local_ironic.sh @@ -6,9 +6,9 @@ set -xe # It requires ${CONTAINER_RUNTIME} variable to be defined first for name in ironic ironic-api ironic-conductor ironic-inspector dnsmasq httpd mariadb ipa-downloader \ - ironic-endpoint-keepalived ironic-log-watch ironic-inspector-log-watch; do + ironic-endpoint-keepalived ironic-log-watch ironic-inspector-log-watch httpd-reverse-proxy ; do sudo "${CONTAINER_RUNTIME}" ps | grep -w "$name$" && sudo "${CONTAINER_RUNTIME}" kill "$name" sudo "${CONTAINER_RUNTIME}" ps --all | grep -w "$name$" && sudo "${CONTAINER_RUNTIME}" rm "$name" -f done -set +xe \ No newline at end of file +set +xe diff --git a/tools/run_local_ironic.sh b/tools/run_local_ironic.sh index da9ea3859d..cad3742306 100755 --- a/tools/run_local_ironic.sh +++ b/tools/run_local_ironic.sh @@ -17,9 +17,11 @@ PROVISIONING_INTERFACE="${PROVISIONING_INTERFACE:-"ironicendpoint"}" CLUSTER_DHCP_RANGE="${CLUSTER_DHCP_RANGE:-"172.22.0.10,172.22.0.100"}" IRONIC_KERNEL_PARAMS="${IRONIC_KERNEL_PARAMS:-"console=ttyS0"}" + IRONIC_CACERT_FILE="${IRONIC_CACERT_FILE:-}" IRONIC_CERT_FILE="${IRONIC_CERT_FILE:-}" IRONIC_KEY_FILE="${IRONIC_KEY_FILE:-}" +IRONIC_TLS_SETUP=${IRONIC_TLS_SETUP:-"true"} IRONIC_INSPECTOR_CACERT_FILE="${IRONIC_INSPECTOR_CACERT_FILE:-}" IRONIC_INSPECTOR_CERT_FILE="${IRONIC_INSPECTOR_CERT_FILE:-}" @@ -50,6 +52,11 @@ IRONIC_ENDPOINT="${IRONIC_ENDPOINT:-"${IRONIC_BASE_URL}:6385/v1/"}" IRONIC_INSPECTOR_ENDPOINT="${IRONIC_INSPECTOR_ENDPOINT:-"${IRONIC_BASE_URL}:5050/v1/"}" CACHEURL="${CACHEURL:-"http://${PROVISIONING_IP}/images"}" IRONIC_FAST_TRACK="${IRONIC_FAST_TRACK:-"true"}" +INSPECTOR_REVERSE_PROXY_SETUP=${INSPECTOR_REVERSE_PROXY_SETUP:-"true"} +if [[ $IRONIC_TLS_SETUP == *false* ]] +then + INSPECTOR_REVERSE_PROXY_SETUP="false" # No Revese proxy for Ironic inspector if TLS is not used +fi sudo mkdir -p "${IRONIC_DATA_DIR}" sudo mkdir -p "${IRONIC_DATA_DIR}/auth" @@ -66,6 +73,7 @@ IRONIC_INSPECTOR_ENDPOINT=${IRONIC_INSPECTOR_ENDPOINT} CACHEURL=${CACHEURL} IRONIC_FAST_TRACK=${IRONIC_FAST_TRACK} IRONIC_KERNEL_PARAMS=${IRONIC_KERNEL_PARAMS} +INSPECTOR_REVERSE_PROXY_SETUP=${INSPECTOR_REVERSE_PROXY_SETUP} EOF sudo "${CONTAINER_RUNTIME}" pull "$IRONIC_IMAGE" @@ -166,13 +174,6 @@ sudo "${CONTAINER_RUNTIME}" run -d --net host --privileged --name dnsmasq \ ${POD} --env-file "${IRONIC_DATA_DIR}/ironic-vars.env" \ -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 -# shellcheck disable=SC2086 -sudo "${CONTAINER_RUNTIME}" run -d --net host --privileged --name httpd \ - ${POD} --env-file "${IRONIC_DATA_DIR}/ironic-vars.env" \ - -v "$IRONIC_DATA_DIR:/shared" --entrypoint /bin/runhttpd "${IRONIC_IMAGE}" - # https://github.com/metal3-io/ironic/blob/master/runmariadb.sh # shellcheck disable=SC2086 sudo "${CONTAINER_RUNTIME}" run -d --net host --privileged --name mariadb \ @@ -219,6 +220,17 @@ sudo "${CONTAINER_RUNTIME}" run -d --net host --privileged --name ironic-inspect --env-file "${IRONIC_DATA_DIR}/ironic-vars.env" \ -v "$IRONIC_DATA_DIR:/shared" "${IRONIC_INSPECTOR_IMAGE}" +# Start httpd reverse proxy for Ironic Inspector +# shellcheck disable=SC2086 +if [[ $INSPECTOR_REVERSE_PROXY_SETUP == "true" ]] +then + sudo "${CONTAINER_RUNTIME}" run -d --net host --privileged --name httpd-reverse-proxy \ + ${POD} ${CERTS_MOUNTS} ${BASIC_AUTH_MOUNTS} ${IRONIC_INSPECTOR_HTPASSWD} \ + --env-file "${IRONIC_DATA_DIR}/ironic-vars.env" \ + --entrypoint /bin/runhttpd \ + -v "$IRONIC_DATA_DIR:/shared" "${IRONIC_INSPECTOR_IMAGE}" +fi + # Start ironic-inspector-log-watch # shellcheck disable=SC2086 sudo "${CONTAINER_RUNTIME}" run -d --net host --privileged --name ironic-inspector-log-watch \ diff --git a/vendor/modules.txt b/vendor/modules.txt index bfd2cd4553..dc3afc1e22 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -645,7 +645,7 @@ honnef.co/go/tools/staticcheck honnef.co/go/tools/stylecheck honnef.co/go/tools/unused honnef.co/go/tools/version -# k8s.io/api v0.20.0 +# k8s.io/api v0.20.1 ## explicit k8s.io/api/admission/v1 k8s.io/api/admission/v1beta1 @@ -696,7 +696,7 @@ k8s.io/api/storage/v1beta1 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1 -# k8s.io/apimachinery v0.20.0 +# k8s.io/apimachinery v0.20.1 ## explicit k8s.io/apimachinery/pkg/api/equality k8s.io/apimachinery/pkg/api/errors @@ -746,7 +746,7 @@ k8s.io/apimachinery/pkg/version k8s.io/apimachinery/pkg/watch k8s.io/apimachinery/third_party/forked/golang/json k8s.io/apimachinery/third_party/forked/golang/reflect -# k8s.io/client-go v0.20.0 +# k8s.io/client-go v0.20.1 ## explicit k8s.io/client-go/discovery k8s.io/client-go/dynamic