From a07fd9b0f72cedcd89ae3eda86ba9e3eb5651a89 Mon Sep 17 00:00:00 2001 From: Dmitri Fedotov Date: Thu, 12 Sep 2024 23:50:37 +0300 Subject: [PATCH] Add tilt for local development --- .golangci.yml | 2 +- Makefile | 35 +++- Tiltfile | 89 +++++++++++ api/v1alpha1/bmc_types.go | 1 + bmc/redfish_kube.go | 150 ++++++++++++++++++ config/dev/kustomization.yaml | 9 ++ config/dev/macdb.yaml | 12 ++ config/dev/manager_patch.yaml | 36 +++++ config/dev/registry_service.yaml | 12 ++ config/rbac/role.yaml | 12 ++ config/redfish-mockup/kustomization.yaml | 3 + .../redfish_mockup_endpoint.yaml | 13 ++ .../redfish_mockup_service.yaml | 11 ++ internal/controller/bmcutils.go | 15 +- internal/controller/endpoint_controller.go | 20 +++ internal/controller/server_controller.go | 2 + scripts/kind-with-registry.sh | 58 +++++++ 17 files changed, 476 insertions(+), 4 deletions(-) create mode 100644 Tiltfile create mode 100644 bmc/redfish_kube.go create mode 100644 config/dev/macdb.yaml create mode 100644 config/dev/manager_patch.yaml create mode 100644 config/dev/registry_service.yaml create mode 100644 config/redfish-mockup/kustomization.yaml create mode 100644 config/redfish-mockup/redfish_mockup_endpoint.yaml create mode 100644 config/redfish-mockup/redfish_mockup_service.yaml create mode 100755 scripts/kind-with-registry.sh diff --git a/.golangci.yml b/.golangci.yml index 2d181aa..a09b41d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -19,9 +19,9 @@ issues: linters: disable-all: true enable: + - copyloopvar - dupl - errcheck - - exportloopref - goconst - gocyclo - gofmt diff --git a/Makefile b/Makefile index c0ac157..814e5a5 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,9 @@ else GOBIN=$(shell go env GOBIN) endif +GOARCH := $(shell go env GOARCH) +GOOS := $(shell go env GOOS) + # CONTAINER_TOOL defines the container tool to be used for building images. # Be aware that the target commands are only tested with Docker which is # scaffolded by default. However, you might want to replace it to use other @@ -181,8 +184,12 @@ LOCALBIN ?= $(shell pwd)/bin $(LOCALBIN): mkdir -p $(LOCALBIN) +# curl retries +CURL_RETRIES=3 + ## Tool Binaries -KUBECTL ?= kubectl +KUBECTL ?= $(LOCALBIN)/kubectl-$(ENVTEST_K8S_VERSION) +KUBECTL_BIN ?= $(LOCALBIN)/kubectl KUSTOMIZE ?= $(LOCALBIN)/kustomize-$(KUSTOMIZE_VERSION) CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen-$(CONTROLLER_TOOLS_VERSION) ENVTEST ?= $(LOCALBIN)/setup-envtest-$(ENVTEST_VERSION) @@ -203,6 +210,13 @@ kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. $(KUSTOMIZE): $(LOCALBIN) $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) +.PHONY: kubectl +kubectl: $(KUBECTL) ## Download kubectl locally if necessary. +$(KUBECTL): $(LOCALBIN) + curl --retry $(CURL_RETRIES) -fsL https://dl.k8s.io/release/v$(ENVTEST_K8S_VERSION)/bin/$(GOOS)/$(GOARCH)/kubectl -o $(KUBECTL) + ln -sf "$(KUBECTL)" "$(KUBECTL_BIN)" + chmod +x "$(KUBECTL_BIN)" "$(KUBECTL)" + .PHONY: controller-gen controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. $(CONTROLLER_GEN): $(LOCALBIN) @@ -241,3 +255,22 @@ GOBIN=$(LOCALBIN) go install $${package} ;\ mv "$$(echo "$(1)" | sed "s/-$(3)$$//")" $(1) ;\ } endef + +## -------------------------------------- +## Tilt / Kind +## -------------------------------------- + +KIND_CLUSTER_NAME ?= metal + +.PHONY: kind-create +kind-create: $(ENVTEST) ## create metal kind cluster if needed + ./scripts/kind-with-registry.sh + +.PHONY: kind-delete +kind-delete: ## Destroys the "metal" kind cluster. + kind delete cluster --name=$(KIND_CLUSTER_NAME) + docker stop kind-registry && docker rm kind-registry + +.PHONY: tilt-up +tilt-up: $(ENVTEST) $(KUSTOMIZE) kind-create ## start tilt and build kind cluster if needed + EXP_CLUSTER_RESOURCE_SET=true tilt up diff --git a/Tiltfile b/Tiltfile new file mode 100644 index 0000000..92b51bc --- /dev/null +++ b/Tiltfile @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +#// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +#// SPDX-License-Identifier: Apache-2.0 + +update_settings(k8s_upsert_timeout_secs=60) # on first tilt up, often can take longer than 30 seconds + +settings = { + "allowed_contexts": [ + "kind-metal" + ], + "kubectl": "./bin/kubectl", + "boot_image": "ghcr.io/ironcore-dev/boot-operator:latest", + "cert_manager_version": "v1.15.3", + "new_args": { + "boot": [ + "--health-probe-bind-address=:8081", + "--metrics-bind-address=127.0.0.1:8085", + "--leader-elect", + "--default-ipxe-server-url=http://boot-service:30007", + "--default-kernel-url=http://boot-service:30007/image?imageName=ghcr.io/ironcore-dev/os-images/gardenlinux&version=1443.10&layerName=vmlinuz", + "--default-initrd-url=http://boot-service:30007/image?imageName=ghcr.io/ironcore-dev/os-images/gardenlinux&version=1443.10&layerName=initramfs", + "--default-squashfs-url=http://boot-service:30007/image?imageName=ghcr.io/ironcore-dev/os-images/gardenlinux&version=1443.10&layerName=squashfs", + "--ipxe-service-url=http://boot-service:30007", + "--ipxe-service-port=30007", + "--controllers=ipxebootconfig,serverbootconfigpxe,serverbootconfighttp,httpbootconfig", + ], + } +} + +kubectl = settings.get("kubectl") + +if "allowed_contexts" in settings: + allow_k8s_contexts(settings.get("allowed_contexts")) + +def deploy_cert_manager(): + version = settings.get("cert_manager_version") + print("Installing cert-manager") + local("{} apply -f https://github.com/cert-manager/cert-manager/releases/download/{}/cert-manager.yaml".format(kubectl, version), quiet=True, echo_off=True) + + print("Waiting for cert-manager to start") + local("{} wait --for=condition=Available --timeout=300s -n cert-manager deployment/cert-manager".format(kubectl), quiet=True, echo_off=True) + local("{} wait --for=condition=Available --timeout=300s -n cert-manager deployment/cert-manager-cainjector".format(kubectl), quiet=True, echo_off=True) + local("{} wait --for=condition=Available --timeout=300s -n cert-manager deployment/cert-manager-webhook".format(kubectl), quiet=True, echo_off=True) + +# deploy boot-operator +def deploy_boot(): + version = settings.get("boot_version") + image = settings.get("boot_image") + new_args = settings.get("new_args").get("boot") + boot_uri = "https://github.com/ironcore-dev/boot-operator//config/dev" + cmd = "{} apply -k {}".format(kubectl, boot_uri) + local(cmd, quiet=True) + + replace_args_with_new_args("boot-operator-system", "boot-operator-controller-manager", new_args) + patch_image("boot-operator-system", "boot-operator-controller-manager", image) + +def patch_image(namespace, name, image): + patch = [{ + "op": "replace", + "path": "/spec/template/spec/containers/0/image", + "value": image, + }] + local("{} patch deployment {} -n {} --type json -p='{}'".format(kubectl, name, namespace, str(encode_json(patch)).replace("\n", ""))) + +def replace_args_with_new_args(namespace, name, extra_args): + patch = [{ + "op": "replace", + "path": "/spec/template/spec/containers/0/args", + "value": extra_args, + }] + local("{} patch deployment {} -n {} --type json -p='{}'".format(kubectl, name, namespace, str(encode_json(patch)).replace("\n", ""))) + +def waitforsystem(): + print("Waiting for metal-operator to start") + local("{} wait --for=condition=ready --timeout=300s -n metal-operator-system pod --all".format(kubectl), quiet=False, echo_off=True) + +############################## +# Actual work happens here +############################## + +deploy_cert_manager() + +docker_build('controller', '.', target = 'manager') + +deploy_boot() + +yaml = kustomize('./config/dev') + +k8s_yaml(yaml) diff --git a/api/v1alpha1/bmc_types.go b/api/v1alpha1/bmc_types.go index 5b80015..fd3b6ec 100644 --- a/api/v1alpha1/bmc_types.go +++ b/api/v1alpha1/bmc_types.go @@ -12,6 +12,7 @@ const ( BMCType = "bmc" ProtocolRedfish = "Redfish" ProtocolRedfishLocal = "RedfishLocal" + ProtocolRedfishKube = "RedfishKube" ) // BMCSpec defines the desired state of BMC diff --git a/bmc/redfish_kube.go b/bmc/redfish_kube.go new file mode 100644 index 0000000..6f5a2ee --- /dev/null +++ b/bmc/redfish_kube.go @@ -0,0 +1,150 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package bmc + +import ( + "context" + "fmt" + + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/stmcginnis/gofish/redfish" + v1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" +) + +const ( + metalJobLabel = "kube.ironcore.dev/job" + devServerUUID = "38947555-7742-3448-3784-823347823834" + registryURL = "http://metal-registry.metal-operator-system.svc.cluster.local:30000/register" +) + +var _ BMC = (*RedfishKubeBMC)(nil) + +type KubeClient struct { + client client.Client + namespace string +} + +// RedfishKubeBMC is an implementation of the BMC interface for Redfish. +type RedfishKubeBMC struct { + *RedfishBMC + *KubeClient + poweredOn bool +} + +// NewRedfishKubeBMCClient creates a new RedfishKubeBMC with the given connection details. +func NewRedfishKubeBMCClient( + ctx context.Context, + endpoint, username, password string, + basicAuth bool, + c client.Client, + ns string, +) (BMC, error) { + bmc, err := NewRedfishBMCClient(ctx, endpoint, username, password, basicAuth) + if err != nil { + return nil, err + } + redfishKubeBMC := &RedfishKubeBMC{ + RedfishBMC: bmc, + KubeClient: &KubeClient{ + client: c, + namespace: ns, + }, + } + + // TODO find a general way to call PowerOn method for mockup servers + if !redfishKubeBMC.poweredOn { + if err = redfishKubeBMC.PowerOn(devServerUUID); err != nil { + return nil, fmt.Errorf("failed to power on system: %w", err) + } + redfishKubeBMC.poweredOn = true + } + + return redfishKubeBMC, nil +} + +func (r RedfishKubeBMC) PowerOn(systemUUID string) error { + system, err := r.getSystemByUUID(systemUUID) + if err != nil { + return fmt.Errorf("failed to get system: %w", err) + } + system.PowerState = redfish.OnPowerState + systemURI := fmt.Sprintf("/redfish/v1/Systems/%s", system.ID) + if err := system.Patch(systemURI, system); err != nil { + return fmt.Errorf("failed to set power state %s for system %s: %w", redfish.OnPowerState, systemUUID, err) + } + netData := `{"networkInterfaces":[{"name":"dummy0","ipAddress":"127.0.0.2","macAddress":"aa:bb:cc:dd:ee:ff"}]` + curlCmd := fmt.Sprintf( + `apk add curl && curl -H 'Content-Type: application/json' \ +-d '{"SystemUUID":"%s","data":%s}}' \ +-X POST %s`, + systemUUID, netData, registryURL) + cmd := []string{ + "/bin/sh", + "-c", + curlCmd, + } + if err := r.createJob(context.TODO(), r.KubeClient.client, cmd, r.KubeClient.namespace, systemUUID); err != nil { + return fmt.Errorf("failed to create job for system %s: %w", systemUUID, err) + } + return nil +} + +func (r RedfishKubeBMC) createJob( + ctx context.Context, + c client.Client, + cmd []string, + namespace, + systemUUID string, +) error { + // Check if a job with the same label already exists + jobList := &v1.JobList{} + listOpts := []client.ListOption{ + client.InNamespace(namespace), + client.MatchingLabels{metalJobLabel: systemUUID}, + } + if err := c.List(ctx, jobList, listOpts...); err != nil { + return fmt.Errorf("failed to list jobs: %w", err) + } + if len(jobList.Items) > 0 { + return nil // Job already exists, do not create a new one + } + + job := &v1.Job{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: fmt.Sprintf("register-%s-", systemUUID), + Namespace: namespace, + Labels: map[string]string{ + metalJobLabel: systemUUID, + }, + }, + Spec: v1.JobSpec{ + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + metalJobLabel: systemUUID, + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "registry-job", + Image: "alpine:latest", + Command: cmd, + }, + }, + RestartPolicy: corev1.RestartPolicyNever, + }, + }, + TTLSecondsAfterFinished: ptr.To(int32(30)), + }, + } + if err := c.Create(ctx, job); err != nil { + return err + } + return nil +} diff --git a/config/dev/kustomization.yaml b/config/dev/kustomization.yaml index 10972c7..cbcb96d 100644 --- a/config/dev/kustomization.yaml +++ b/config/dev/kustomization.yaml @@ -1,5 +1,14 @@ resources: - ../default +- ../redfish-mockup +- registry_service.yaml patches: - path: delete_manager_auth_proxy_patch.yaml +- path: manager_patch.yaml + +secretGenerator: +- name: macdb + namespace: metal-operator-system + files: + - macdb.yaml diff --git a/config/dev/macdb.yaml b/config/dev/macdb.yaml new file mode 100644 index 0000000..773634b --- /dev/null +++ b/config/dev/macdb.yaml @@ -0,0 +1,12 @@ +macPrefixes: +- macPrefix: 23 + manufacturer: Foo + protocol: RedfishKube + port: 8000 + type: bmc + defaultCredentials: + - username: foo + password: bar + console: + type: ssh + port: 22 diff --git a/config/dev/manager_patch.yaml b/config/dev/manager_patch.yaml new file mode 100644 index 0000000..55aff1a --- /dev/null +++ b/config/dev/manager_patch.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + - --mac-prefixes-file=/etc/macdb/macdb.yaml + - --probe-image=ghcr.io/ironcore-dev/metalprobe:latest + - --probe-os-image=ghcr.io/ironcore-dev/os-images/gardenlinux:1443.10 + - --registry-url=http://127.0.0.1:30000 + - --registry-port=30000 + - --enforce-first-boot + ports: + - containerPort: 30000 + volumeMounts: + - mountPath: /etc/macdb/ + name: macdb + - name: redfish + image: dmtf/redfish-mockup-server:latest + ports: + - containerPort: 8000 + securityContext: + runAsNonRoot: false + volumes: + - name: macdb + secret: + defaultMode: 420 + secretName: macdb diff --git a/config/dev/registry_service.yaml b/config/dev/registry_service.yaml new file mode 100644 index 0000000..57bc98e --- /dev/null +++ b/config/dev/registry_service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: metal-registry + namespace: metal-operator-system +spec: + ports: + - name: registry-server + port: 30000 + targetPort: 30000 + selector: + control-plane: controller-manager diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 29f055b..336042f 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -16,6 +16,18 @@ rules: - patch - update - watch +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - metal.ironcore.dev resources: diff --git a/config/redfish-mockup/kustomization.yaml b/config/redfish-mockup/kustomization.yaml new file mode 100644 index 0000000..c23b242 --- /dev/null +++ b/config/redfish-mockup/kustomization.yaml @@ -0,0 +1,3 @@ +resources: +- redfish_mockup_endpoint.yaml +- redfish_mockup_service.yaml diff --git a/config/redfish-mockup/redfish_mockup_endpoint.yaml b/config/redfish-mockup/redfish_mockup_endpoint.yaml new file mode 100644 index 0000000..0df99e9 --- /dev/null +++ b/config/redfish-mockup/redfish_mockup_endpoint.yaml @@ -0,0 +1,13 @@ +apiVersion: metal.ironcore.dev/v1alpha1 +kind: Endpoint +metadata: + labels: + app.kubernetes.io/name: endpoint + app.kubernetes.io/instance: endpoint-sample + app.kubernetes.io/part-of: metal-operator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: metal-operator + name: endpoint-sample +spec: + ip: "127.0.0.1" + macAddress: "23:11:8A:33:CF:EA" diff --git a/config/redfish-mockup/redfish_mockup_service.yaml b/config/redfish-mockup/redfish_mockup_service.yaml new file mode 100644 index 0000000..91129d7 --- /dev/null +++ b/config/redfish-mockup/redfish_mockup_service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: redfish + namespace: metal-operator-system +spec: + ports: + - port: 8000 + targetPort: 8000 + selector: + control-plane: controller-manager diff --git a/internal/controller/bmcutils.go b/internal/controller/bmcutils.go index cb87d49..bf82123 100644 --- a/internal/controller/bmcutils.go +++ b/internal/controller/bmcutils.go @@ -32,6 +32,7 @@ func GetBMCClientForServer(ctx context.Context, c client.Client, server *metalv1 return CreateBMCClient( ctx, + c, insecure, server.Spec.BMC.Protocol.Name, metalv1alpha1.MustParseIP(server.Spec.BMC.Endpoint), @@ -54,10 +55,10 @@ func GetBMCClientFromBMC(ctx context.Context, c client.Client, bmcObj *metalv1al return nil, fmt.Errorf("failed to get BMC secret: %w", err) } - return CreateBMCClient(ctx, insecure, bmcObj.Spec.Protocol.Name, endpoint.Spec.IP, bmcObj.Spec.Protocol.Port, bmcSecret) + return CreateBMCClient(ctx, c, insecure, bmcObj.Spec.Protocol.Name, endpoint.Spec.IP, bmcObj.Spec.Protocol.Port, bmcSecret) } -func CreateBMCClient(ctx context.Context, insecure bool, bmcProtocol metalv1alpha1.ProtocolName, endpoint metalv1alpha1.IP, port int32, bmcSecret *metalv1alpha1.BMCSecret) (bmc.BMC, error) { +func CreateBMCClient(ctx context.Context, c client.Client, insecure bool, bmcProtocol metalv1alpha1.ProtocolName, endpoint metalv1alpha1.IP, port int32, bmcSecret *metalv1alpha1.BMCSecret) (bmc.BMC, error) { protocol := "https" if insecure { protocol = "http" @@ -85,6 +86,16 @@ func CreateBMCClient(ctx context.Context, insecure bool, bmcProtocol metalv1alph if err != nil { return nil, fmt.Errorf("failed to create Redfish client: %w", err) } + case metalv1alpha1.ProtocolRedfishKube: + bmcAddress := fmt.Sprintf("%s://%s:%d", protocol, endpoint, port) + username, password, err := GetBMCCredentialsFromSecret(bmcSecret) + if err != nil { + return nil, fmt.Errorf("failed to get credentials from BMC secret: %w", err) + } + bmcClient, err = bmc.NewRedfishKubeBMCClient(ctx, bmcAddress, username, password, true, c, DefaultKubeNamespace) + if err != nil { + return nil, fmt.Errorf("failed to create Redfish client: %w", err) + } default: return nil, fmt.Errorf("unsupported BMC protocol %s", bmcProtocol) } diff --git a/internal/controller/endpoint_controller.go b/internal/controller/endpoint_controller.go index 78d237b..833c8ce 100644 --- a/internal/controller/endpoint_controller.go +++ b/internal/controller/endpoint_controller.go @@ -122,6 +122,26 @@ func (r *EndpointReconciler) reconcile(ctx context.Context, log logr.Logger, end return ctrl.Result{}, fmt.Errorf("failed to apply BMC object: %w", err) } log.V(1).Info("Applied BMC object for Endpoint") + case metalv1alpha1.ProtocolRedfishKube: + log.V(1).Info("Creating client for a kube test BMC") + bmcAddress := fmt.Sprintf("%s://%s:%d", r.getProtocol(), endpoint.Spec.IP, m.Port) + bmcClient, err := bmc.NewRedfishKubeBMCClient(ctx, bmcAddress, m.DefaultCredentials[0].Username, m.DefaultCredentials[0].Password, true, r.Client, DefaultKubeNamespace) + if err != nil { + return ctrl.Result{}, fmt.Errorf("failed to create BMC client: %w", err) + } + defer bmcClient.Logout() + + var bmcSecret *metalv1alpha1.BMCSecret + if bmcSecret, err = r.applyBMCSecret(ctx, log, endpoint, m); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to apply BMCSecret: %w", err) + } + log.V(1).Info("Applied kube test BMC secret for endpoint") + + if err := r.applyBMC(ctx, log, endpoint, bmcSecret, m); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to apply BMC object: %w", err) + } + log.V(1).Info("Applied BMC object for Endpoint") + } // TODO: other types like Switches can be handled here later } diff --git a/internal/controller/server_controller.go b/internal/controller/server_controller.go index 2e1d870..767db8f 100644 --- a/internal/controller/server_controller.go +++ b/internal/controller/server_controller.go @@ -35,6 +35,7 @@ const ( DefaultIgnitionSecretKeyName = "ignition" DefaultIgnitionFormatKey = "format" DefaultIgnitionFormatValue = "fcos" + DefaultKubeNamespace = "default" ServerFinalizer = "metal.ironcore.dev/server" InternalAnnotationTypeKeyName = "metal.ironcore.dev/type" InternalAnnotationTypeValue = "Internal" @@ -68,6 +69,7 @@ type ServerReconciler struct { //+kubebuilder:rbac:groups=metal.ironcore.dev,resources=servers/finalizers,verbs=update //+kubebuilder:rbac:groups=metal.ironcore.dev,resources=serverconfigurations,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups="batch",resources=jobs,verbs=get;list;watch;create;update;patch;delete // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. diff --git a/scripts/kind-with-registry.sh b/scripts/kind-with-registry.sh new file mode 100755 index 0000000..4b29ea9 --- /dev/null +++ b/scripts/kind-with-registry.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +#// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +#// SPDX-License-Identifier: Apache-2.0 + +set -o errexit +set -o nounset +set -o pipefail + + +REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +KUBECTL=$REPO_ROOT/bin/kubectl + +# desired kind cluster name; default is "metal" +KIND_CLUSTER_NAME="${KIND_CLUSTER_NAME:-metal}" + +if [[ "$(kind get clusters)" =~ .*"${KIND_CLUSTER_NAME}".* ]]; then + echo "cluster already exists, moving on" + exit 0 +fi + +reg_name='kind-registry' +reg_port="${KIND_REGISTRY_PORT:-5000}" + +# create registry container unless it already exists +running="$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" +if [ "${running}" != 'true' ]; then + docker run -d --restart=always -p "127.0.0.1:${reg_port}:5000" --name "${reg_name}" registry:2 +fi + +# create a cluster with the local registry enabled in containerd +cat <