From 3dc335a36a95036176c6937aa4d932916e6771e1 Mon Sep 17 00:00:00 2001 From: Gaurav Mehta Date: Wed, 26 Oct 2022 12:49:58 +1100 Subject: [PATCH] minor fixes (#4) * fix failed cases * move to dapper and fixed integration tests * fixed test cases * v1.0.x and v1.1.x hardware generation * Additional hardware test cases * added port 443 to join url --- .gitignore | 1 + Dockerfile.dapper | 25 ++++ Makefile | 137 ++---------------- cover.out | 21 +++ package/Dockerfile | 26 ++++ pkg/api/v1alpha1/common.go | 9 +- pkg/controllers/cluster_events_controller.go | 6 +- .../cluster_events_controller_test.go | 24 ++- pkg/controllers/inventory_event_controller.go | 7 +- .../inventory_event_controller_test.go | 16 +- pkg/controllers/suite_test.go | 9 +- pkg/tink/tink.go | 65 ++++++++- pkg/tink/tink_test.go | 55 ++++++- scripts/build | 9 ++ scripts/ci | 7 + scripts/entry | 11 ++ scripts/generate | 0 scripts/package | 11 ++ scripts/test | 3 + scripts/version | 35 +++++ 20 files changed, 320 insertions(+), 157 deletions(-) create mode 100644 .gitignore create mode 100644 Dockerfile.dapper create mode 100644 cover.out create mode 100644 package/Dockerfile create mode 100755 scripts/build create mode 100755 scripts/ci create mode 100755 scripts/entry create mode 100755 scripts/generate create mode 100755 scripts/package create mode 100755 scripts/test create mode 100755 scripts/version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..36f971e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bin/* diff --git a/Dockerfile.dapper b/Dockerfile.dapper new file mode 100644 index 0000000..0d19a17 --- /dev/null +++ b/Dockerfile.dapper @@ -0,0 +1,25 @@ +FROM golang:1.18 + +ARG DAPPER_HOST_ARCH +ENV ARCH $DAPPER_HOST_ARCH + +RUN export K8S_VERSION=1.24.2 && \ + curl -sSLo envtest-bins.tar.gz "https://go.kubebuilder.io/test-tools/${K8S_VERSION}/$(go env GOOS)/$(go env GOARCH)" && \ + mkdir /usr/local/kubebuilder && \ + tar -C /usr/local/kubebuilder --strip-components=1 -zvxf envtest-bins.tar.gz && \ + curl -sSLo kustomize.tar.gz "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv4.5.7/kustomize_v4.5.7_linux_amd64.tar.gz" && \ + tar -C /usr/bin --strip-components=1 -zxvf kustomize.tar.gz && \ + go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0 + +RUN apt update && \ + apt install -y bash git gcc docker.io vim less file curl wget ca-certificates qemu-utils + +ENV DAPPER_ENV REPO TAG DRONE_TAG CROSS +ENV DAPPER_SOURCE /go/src/github.com/harvester/vm-import-controller +ENV DAPPER_OUTPUT ./bin ./pkg/api ./config +ENV DAPPER_DOCKER_SOCKET true +ENV DAPPER_RUN_ARGS "--network=host" +WORKDIR ${DAPPER_SOURCE} + +ENTRYPOINT ["./scripts/entry"] +CMD ["ci"] \ No newline at end of file diff --git a/Makefile b/Makefile index 9733149..119a5ee 100644 --- a/Makefile +++ b/Makefile @@ -1,130 +1,15 @@ +TARGETS := $(shell ls scripts) -# Image URL to use all building/pushing image targets -IMG ?= controller:latest -# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.23 +.dapper: + @echo Downloading dapper + @curl -sL https://releases.rancher.com/dapper/latest/dapper-$$(uname -s)-$$(uname -m) > .dapper.tmp + @@chmod +x .dapper.tmp + @./.dapper.tmp -v + @mv .dapper.tmp .dapper -# 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 +$(TARGETS): .dapper + ./.dapper $@ -# Setting SHELL to bash allows bash commands to be executed by recipes. -# This is a requirement for 'setup-envtest.sh' in the test target. -# Options are set to exit when a recipe line exits non-zero or a piped command fails. -SHELL = /usr/bin/env bash -o pipefail -.SHELLFLAGS = -ec +.DEFAULT_GOAL := ci -.PHONY: all -all: build - -##@ General - -# The help target prints out all targets with their descriptions organized -# beneath their categories. The categories are represented by '##@' and the -# target descriptions by '##'. The awk commands is responsible for reading the -# entire set of makefiles included in this invocation, looking for lines of the -# file as xyz: ## something, and then pretty-format the target and help. Then, -# if there's a line with ##@ something, that gets pretty-printed as a category. -# More info on the usage of ANSI control characters for terminal formatting: -# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters -# More info on the awk command: -# http://linuxcommand.org/lc3_adv_awk.php - -.PHONY: help -help: ## Display this help. - @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) - -##@ Development - -.PHONY: manifests -manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. - $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases - -.PHONY: generate -generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. - $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." - -.PHONY: fmt -fmt: ## Run go fmt against code. - go fmt ./... - -.PHONY: vet -vet: ## Run go vet against code. - go vet ./... - -.PHONY: test -test: manifests generate fmt vet envtest ## Run tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test -v -p 1 ./... -coverprofile cover.out - -##@ Build - -.PHONY: build -build: generate fmt vet ## Build manager binary. - go build -o bin/manager main.go - -.PHONY: run -run: manifests generate fmt vet ## Run a controller from your host. - go run ./main.go - -.PHONY: docker-build -docker-build: test ## Build docker image with the manager. - docker build -t ${IMG} . - -.PHONY: docker-push -docker-push: ## Push docker image with the manager. - docker push ${IMG} - -##@ Deployment - -ifndef ignore-not-found - ignore-not-found = false -endif - -.PHONY: install -install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | kubectl apply -f - - -.PHONY: uninstall -uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. - $(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f - - -.PHONY: deploy -deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. - cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} - $(KUSTOMIZE) build config/default | kubectl apply -f - - -.PHONY: undeploy -undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. - $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f - - -CONTROLLER_GEN = $(shell pwd)/bin/controller-gen -.PHONY: controller-gen -controller-gen: ## Download controller-gen locally if necessary. - $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0) - -KUSTOMIZE = $(shell pwd)/bin/kustomize -.PHONY: kustomize -kustomize: ## Download kustomize locally if necessary. - $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7) - -ENVTEST = $(shell pwd)/bin/setup-envtest -.PHONY: envtest -envtest: ## Download envtest-setup locally if necessary. - $(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) - -# go-get-tool will 'go get' any package $2 and install it to $1. -PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) -define go-get-tool -@[ -f $(1) ] || { \ -set -e ;\ -TMP_DIR=$$(mktemp -d) ;\ -cd $$TMP_DIR ;\ -go mod init tmp ;\ -echo "Downloading $(2)" ;\ -GOBIN=$(PROJECT_DIR)/bin go install $(2) ;\ -rm -rf $$TMP_DIR ;\ -} -endef +.PHONY: $(TARGETS) \ No newline at end of file diff --git a/cover.out b/cover.out new file mode 100644 index 0000000..7b737d1 --- /dev/null +++ b/cover.out @@ -0,0 +1,21 @@ +mode: set +github.com/harvester/seeder/pkg/events/events.go:16.103,25.16 3 1 +github.com/harvester/seeder/pkg/events/events.go:29.2,29.56 1 1 +github.com/harvester/seeder/pkg/events/events.go:25.16,27.3 1 0 +github.com/harvester/seeder/pkg/events/events.go:32.72,34.16 2 1 +github.com/harvester/seeder/pkg/events/events.go:38.2,40.28 3 1 +github.com/harvester/seeder/pkg/events/events.go:73.2,81.28 8 1 +github.com/harvester/seeder/pkg/events/events.go:34.16,36.3 1 0 +github.com/harvester/seeder/pkg/events/events.go:40.28,41.27 1 1 +github.com/harvester/seeder/pkg/events/events.go:44.3,49.17 6 1 +github.com/harvester/seeder/pkg/events/events.go:52.3,52.24 1 1 +github.com/harvester/seeder/pkg/events/events.go:41.27,42.12 1 1 +github.com/harvester/seeder/pkg/events/events.go:49.17,51.4 1 0 +github.com/harvester/seeder/pkg/events/events.go:52.24,54.18 2 1 +github.com/harvester/seeder/pkg/events/events.go:58.4,58.29 1 1 +github.com/harvester/seeder/pkg/events/events.go:62.4,63.18 2 1 +github.com/harvester/seeder/pkg/events/events.go:66.4,66.33 1 1 +github.com/harvester/seeder/pkg/events/events.go:54.18,56.5 1 0 +github.com/harvester/seeder/pkg/events/events.go:58.29,60.5 1 1 +github.com/harvester/seeder/pkg/events/events.go:63.18,65.5 1 0 +github.com/harvester/seeder/pkg/events/events.go:66.33,69.5 2 1 diff --git a/package/Dockerfile b/package/Dockerfile new file mode 100644 index 0000000..677de85 --- /dev/null +++ b/package/Dockerfile @@ -0,0 +1,26 @@ +# Build the manager binary +FROM golang:1.18 as builder + +WORKDIR /workspace +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer +RUN go mod download + +# Copy the go source +COPY main.go main.go +COPY pkg/ pkg/ + +# Build +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go + +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM gcr.io/distroless/static:nonroot +WORKDIR / +COPY --from=builder /workspace/manager . +USER 65532:65532 + +ENTRYPOINT ["/manager"] diff --git a/pkg/api/v1alpha1/common.go b/pkg/api/v1alpha1/common.go index 4ff1552..2bdb21f 100644 --- a/pkg/api/v1alpha1/common.go +++ b/pkg/api/v1alpha1/common.go @@ -2,10 +2,11 @@ package v1alpha1 // common constants not associated with a particular type const ( - DefaultNS = "metal-system" - TinkConfig = "tinkerbell" - DefaultAPIPort = "9345" - OverrideAPIPortLabel = "clusterPort.harvesterhci.io" + DefaultNS = "metal-system" + TinkConfig = "tinkerbell" + DefaultAPIPort = "9345" + OverrideAPIPortLabel = "clusterPort.harvesterhci.io" + OverrideRedfishPortLabel = "redfishPort.harvesterhci.io" ) var ( diff --git a/pkg/controllers/cluster_events_controller.go b/pkg/controllers/cluster_events_controller.go index 35f8b2e..c5d2bec 100644 --- a/pkg/controllers/cluster_events_controller.go +++ b/pkg/controllers/cluster_events_controller.go @@ -99,7 +99,11 @@ func (r *ClusterEventReconciler) updateNodes(ctx context.Context, c *seederv1alp } username := s.Data["username"] password := s.Data["password"] - e, err := events.NewEventFetcher(ctx, string(username), string(password), fmt.Sprintf("https://%s", i.Spec.Connection.Host)) + bmcendpoint := fmt.Sprintf("https://%s", i.Spec.BaseboardManagementSpec.Connection.Host) + if port, ok := i.Labels[seederv1alpha1.OverrideRedfishPortLabel]; ok { + bmcendpoint = fmt.Sprintf("https://%s:%s", i.Spec.BaseboardManagementSpec.Connection.Host, port) + } + e, err := events.NewEventFetcher(ctx, string(username), string(password), bmcendpoint) if err != nil { return err } diff --git a/pkg/controllers/cluster_events_controller_test.go b/pkg/controllers/cluster_events_controller_test.go index 11fe62a..d4f7d16 100644 --- a/pkg/controllers/cluster_events_controller_test.go +++ b/pkg/controllers/cluster_events_controller_test.go @@ -49,7 +49,7 @@ var _ = Describe("cluster events test", func() { i = &seederv1alpha1.Inventory{ ObjectMeta: metav1.ObjectMeta{ - Name: "event-test", + Name: "cluster-event-test", Namespace: "default", }, Spec: seederv1alpha1.InventorySpec{ @@ -61,7 +61,7 @@ var _ = Describe("cluster events test", func() { Port: 623, InsecureTLS: true, AuthSecretRef: corev1.SecretReference{ - Name: "event-test", + Name: "cluster-event-test", Namespace: "default", }, }, @@ -74,7 +74,7 @@ var _ = Describe("cluster events test", func() { s = &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: "event-test", + Name: "cluster-event-test", Namespace: "default", }, StringData: map[string]string{ @@ -213,6 +213,18 @@ var _ = Describe("cluster events test", func() { It("check for cluster event reconcilliation", func() { // poll for cluster to be ready for and for nodes to be patched with inventory info + Eventually(func() error { + iObj := &seederv1alpha1.Inventory{} + if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: i.Namespace, Name: i.Name}, iObj); err != nil { + return err + } + iObj.Labels = map[string]string{ + seederv1alpha1.OverrideRedfishPortLabel: redfishPort, + } + return k8sClient.Update(ctx, iObj) + + }, "30s", "5s").ShouldNot(HaveOccurred()) + Eventually(func() error { cObj := &seederv1alpha1.Cluster{} fmt.Println(cObj.Spec.Nodes) @@ -255,8 +267,8 @@ var _ = Describe("cluster events test", func() { for _, a := range v.Status.Addresses { if a.Address == iObj.Status.Address { found = true - if _, ok := v.Labels["totalCpuCores"]; !ok { - return fmt.Errorf("waiting for cpu cores to be populated") + if _, ok := v.Labels["manufacturer"]; !ok { + return fmt.Errorf("waiting for manufacturer to be populated") } } } @@ -266,7 +278,7 @@ var _ = Describe("cluster events test", func() { return fmt.Errorf("waiting to find node matching ip address allocated to inventory %s", iObj.Status.Address) } return nil - }, "60s", "5s").ShouldNot(HaveOccurred()) + }, "120s", "5s").ShouldNot(HaveOccurred()) }) AfterEach(func() { diff --git a/pkg/controllers/inventory_event_controller.go b/pkg/controllers/inventory_event_controller.go index 0b69d7d..3316470 100644 --- a/pkg/controllers/inventory_event_controller.go +++ b/pkg/controllers/inventory_event_controller.go @@ -121,8 +121,11 @@ func (r *InventoryEventReconciller) getInventoryInfo(ctx context.Context, i *see return fmt.Errorf("secret %s has no key password", s.Name) } - bmcendpoint := i.Spec.BaseboardManagementSpec.Connection.Host - rc, err := events.NewEventFetcher(ctx, string(username), string(password), fmt.Sprintf("https://%s", bmcendpoint)) + bmcendpoint := fmt.Sprintf("https://%s", i.Spec.BaseboardManagementSpec.Connection.Host) + if port, ok := i.Labels[seederv1alpha1.OverrideRedfishPortLabel]; ok { + bmcendpoint = fmt.Sprintf("https://%s:%s", i.Spec.BaseboardManagementSpec.Connection.Host, port) + } + rc, err := events.NewEventFetcher(ctx, string(username), string(password), bmcendpoint) if err != nil { return err } diff --git a/pkg/controllers/inventory_event_controller_test.go b/pkg/controllers/inventory_event_controller_test.go index 4ec2746..7e82cf5 100644 --- a/pkg/controllers/inventory_event_controller_test.go +++ b/pkg/controllers/inventory_event_controller_test.go @@ -76,6 +76,18 @@ var _ = Describe("Inventory event controller tests", func() { }) It("check inventory and baremetal reconcile", func() { + Eventually(func() error { + iObj := &seederv1alpha1.Inventory{} + if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: i.Namespace, Name: i.Name}, iObj); err != nil { + return err + } + iObj.Labels = map[string]string{ + seederv1alpha1.OverrideRedfishPortLabel: redfishPort, + } + return k8sClient.Update(ctx, iObj) + + }, "30s", "5s").ShouldNot(HaveOccurred()) + Eventually(func() error { iObj := &seederv1alpha1.Inventory{} err := k8sClient.Get(ctx, types.NamespacedName{Namespace: i.Namespace, Name: i.Name}, iObj) @@ -96,9 +108,9 @@ var _ = Describe("Inventory event controller tests", func() { return err } - _, ok := iObj.Labels["totalCpuCores"] + _, ok := iObj.Labels["manufacturer"] if !ok { - return fmt.Errorf("waiting for totalCpuCores to be populated") + return fmt.Errorf("waiting for manufacturer to be populated") } return nil }, "120s", "5s").ShouldNot(HaveOccurred()) diff --git a/pkg/controllers/suite_test.go b/pkg/controllers/suite_test.go index 818093f..07a89c5 100644 --- a/pkg/controllers/suite_test.go +++ b/pkg/controllers/suite_test.go @@ -29,7 +29,6 @@ import ( "github.com/harvester/seeder/pkg/mock" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/ory/dockertest/v3/docker" rufio "github.com/tinkerbell/rufio/api/v1alpha1" "k8s.io/apimachinery/pkg/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" @@ -54,8 +53,8 @@ var ( //eg *errgroup.Group //egctx context.Context //setupLog = ctrl.Log.WithName("setup") - pool *dockertest.Pool - //redfishPort string + pool *dockertest.Pool + redfishPort string redfishMock *dockertest.Resource ) @@ -178,14 +177,12 @@ var _ = BeforeSuite(func() { "--key", "/mockup/localhost.key", }, - PortBindings: map[docker.Port][]docker.PortBinding{ - "8000/tcp": {{HostPort: "443"}}, - }, } redfishMock, err = pool.BuildAndRunWithBuildOptions(redfishBuildOpts, redfishRunOpts) Expect(err).NotTo(HaveOccurred()) time.Sleep(30 * time.Second) + redfishPort = redfishMock.GetPort("8000/tcp") }) var _ = AfterSuite(func() { diff --git a/pkg/tink/tink.go b/pkg/tink/tink.go index 777d20a..0c776ac 100644 --- a/pkg/tink/tink.go +++ b/pkg/tink/tink.go @@ -3,11 +3,13 @@ package tink import ( "bytes" "fmt" + "html/template" + "strings" + seederv1alpha1 "github.com/harvester/seeder/pkg/api/v1alpha1" "github.com/harvester/seeder/pkg/util" "github.com/pkg/errors" tinkv1alpha1 "github.com/tinkerbell/tink/pkg/apis/core/v1alpha1" - "html/template" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -19,7 +21,7 @@ const ( defaultISOURL = "https://releases.rancher.com/harvester/" ) -//GenerateHWRequest will generate the tinkerbell Hardware type object +// GenerateHWRequest will generate the tinkerbell Hardware type object func GenerateHWRequest(i *seederv1alpha1.Inventory, c *seederv1alpha1.Cluster) (hw *tinkv1alpha1.Hardware, err error) { // generate metadata @@ -28,8 +30,14 @@ func GenerateHWRequest(i *seederv1alpha1.Inventory, c *seederv1alpha1.Cluster) ( mode = "create" } - m, err := generateMetaData(c.Spec.ConfigURL, c.Spec.HarvesterVersion, i.Spec.ManagementInterfaceMacAddress, mode, - i.Spec.PrimaryDisk, c.Status.ClusterAddress, c.Status.ClusterToken, i.Status.GeneratedPassword, c.Spec.ImageURL, c.Spec.ClusterConfig.Nameservers, c.Spec.ClusterConfig.SSHKeys) + var m string + if strings.Contains(c.Spec.HarvesterVersion, "v1.1") { + m, err = generateMetaDataV11(c.Spec.ConfigURL, c.Spec.HarvesterVersion, i.Spec.ManagementInterfaceMacAddress, mode, + i.Spec.PrimaryDisk, c.Status.ClusterAddress, c.Status.ClusterToken, i.Status.GeneratedPassword, c.Spec.ImageURL, c.Spec.ClusterConfig.Nameservers, c.Spec.ClusterConfig.SSHKeys) + } else { + m, err = generateMetaDataV10(c.Spec.ConfigURL, c.Spec.HarvesterVersion, i.Spec.ManagementInterfaceMacAddress, mode, + i.Spec.PrimaryDisk, c.Status.ClusterAddress, c.Status.ClusterToken, i.Status.GeneratedPassword, c.Spec.ImageURL, c.Spec.ClusterConfig.Nameservers, c.Spec.ClusterConfig.SSHKeys) + } if err != nil { return nil, errors.Wrap(err, "error during metadata generation") } @@ -85,8 +93,8 @@ func GenerateHWRequest(i *seederv1alpha1.Inventory, c *seederv1alpha1.Cluster) ( return hw, nil } -// generateMetaData is a wrapper to generate metadata for nodes to create or join a cluster -func generateMetaData(configURL, version, hwAddress, mode, disk, vip, token, password, imageurl string, Nameservers, SSHKeys []string) (metadata string, err error) { +// generateMetaDataV10 is a wrapper to generate metadata for nodes to create or join a cluster +func generateMetaDataV10(configURL, version, hwAddress, mode, disk, vip, token, password, imageurl string, Nameservers, SSHKeys []string) (metadata string, err error) { var tmpStruct struct { ConfigURL string @@ -130,3 +138,48 @@ func generateMetaData(configURL, version, hwAddress, mode, disk, vip, token, pas metadata = output.String() return metadata, nil } + +func generateMetaDataV11(configURL, version, hwAddress, mode, disk, vip, token, password, imageurl string, Nameservers, SSHKeys []string) (metadata string, err error) { + + var tmpStruct struct { + ConfigURL string + HWAddress string + Mode string + Disk string + VIP string + Token string + SSHKeys []string + Nameservers []string + Password string + IsoURL string + } + var output bytes.Buffer + tmpStruct.ConfigURL = configURL + tmpStruct.HWAddress = hwAddress + tmpStruct.Mode = mode + tmpStruct.Disk = disk + tmpStruct.VIP = vip + tmpStruct.Token = token + tmpStruct.Password = password + tmpStruct.SSHKeys = SSHKeys + tmpStruct.Nameservers = Nameservers + tmpStruct.Password = password + endpoint := defaultISOURL + if imageurl != "" { + endpoint = imageurl + } + tmpStruct.IsoURL = fmt.Sprintf("%s/%s/harvester-%s-amd64.iso", endpoint, version, version) + + var metaDataStruct = `{{ if ne .ConfigURL ""}}harvester.install.config_url={{ .ConfigURL }}{{end}} harvester.install.management_interface.interfaces="hwAddr:{{ .HWAddress }}" ip=dhcp harvester.install.management_interface.method=dhcp harvester.management_interface.bond_options.mode=balance-tlb harvester.install.management_interface.bond_options.miimon=100 console=ttyS1,115200 harvester.install.mode={{ .Mode }} harvester.token={{ .Token }} harvester.os.password={{ .Password }} {{ range $v := .SSHKeys}}harvester.os.ssh_authorized_keys=\"- {{ $v }} \ "{{ end }}{{range $v := .Nameservers}}harvester.os.dns_nameservers={{ $v }} {{end}} harvester.install.vip={{ .VIP }} harvester.install.vip_mode=static harvester.install.iso_url={{ .IsoURL }} harvester.install.device={{ .Disk }} {{if eq .Mode "join"}}harvester.server_url={{ printf "https://%s:443" .VIP }}{{end}} harvester.scheme_version=1` + + metadataTmpl := template.Must(template.New("MetaData").Parse(metaDataStruct)) + + err = metadataTmpl.Execute(&output, tmpStruct) + + if err != nil { + return metadata, err + } + + metadata = output.String() + return metadata, nil +} diff --git a/pkg/tink/tink_test.go b/pkg/tink/tink_test.go index 184391c..cea9d0a 100644 --- a/pkg/tink/tink_test.go +++ b/pkg/tink/tink_test.go @@ -11,13 +11,24 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func Test_generateMetaData(t *testing.T) { +func Test_generateMetaDataV10(t *testing.T) { assert := require.New(t) - m, err := generateMetaData("http://localhost", "v1.0.1", "xx:xx:xx:xx:xx", "create", + m, err := generateMetaDataV10("http://localhost", "v1.0.1", "xx:xx:xx:xx:xx", "create", "/dev/sda", "192.168.1.100", "token", "password", "v1.0.2", []string{"8.8.8.8"}, []string{"abc"}) assert.NoError(err, "no error should have occured") assert.Contains(m, "harvester.install.mode=create", "expected to find create mode in metadata") assert.Contains(m, "hwAddr:xx:xx:xx:xx:xx", "expected to find mac address in metadata") + assert.NotContains(m, "scheme_version", "expected to not find scheme_version") +} + +func Test_generateMetaDataV11(t *testing.T) { + assert := require.New(t) + m, err := generateMetaDataV11("http://localhost", "v1.0.1", "xx:xx:xx:xx:xx", "create", + "/dev/sda", "192.168.1.100", "token", "password", "v1.0.2", []string{"8.8.8.8"}, []string{"abc"}) + assert.NoError(err, "no error should have occured") + assert.Contains(m, "harvester.install.mode=create", "expected to find create mode in metadata") + assert.Contains(m, "hwAddr:xx:xx:xx:xx:xx", "expected to find mac address in metadata") + assert.Contains(m, "scheme_version", "expected to find scheme_version") } var ( @@ -109,7 +120,7 @@ var ( } ) -func Test_GenerateHWRequest(t *testing.T) { +func Test_GenerateHWRequestV10(t *testing.T) { assert := require.New(t) hw, err := GenerateHWRequest(i, c) assert.NoError(err, "no error should occur during hardware generation") @@ -125,12 +136,48 @@ func Test_GenerateHWRequest(t *testing.T) { assert.Equal(hw.Spec.Interfaces[0].DHCP.IP.Gateway, i.Status.Gateway, "expected to find correct gateway") assert.Equal(hw.Spec.Interfaces[0].DHCP.IP.Address, i.Status.Address, "expected to find correct address") assert.Equal(hw.Spec.Interfaces[0].DHCP.IP.Netmask, i.Status.Netmask, "expected to find correct netmask") + assert.NotContains(hw.Spec.Metadata.Instance.Userdata, "scheme_version") + assert.Contains(hw.Spec.Metadata.Instance.Userdata, "harvester.install.networks.harvester-mgmt", "expected to find harvester-mgmt in interfaces") + assert.NotContains(hw.Spec.Metadata.Instance.Userdata, "harvester.install.management_interface", "expected to not find management_interface") +} + +func Test_GenerateHWRequestV11(t *testing.T) { + assert := require.New(t) + clusterCopy := c.DeepCopy() + clusterCopy.Spec.HarvesterVersion = "v1.1.0" + hw, err := GenerateHWRequest(i, clusterCopy) + assert.NoError(err, "no error should occur during hardware generation") + assert.Contains(hw.Spec.Metadata.Instance.Userdata, "harvester.install.mode=create", "expected to find create mode in metadata") + assert.Contains(hw.Spec.Metadata.Instance.Userdata, "hwAddr:xx:xx:xx:xx:xx", "expected to find mac address in metadata") + assert.Contains(hw.Spec.Metadata.Instance.Userdata, "dns_nameservers=8.8.8.8", "expected to find correct nameserver") + assert.Contains(hw.Spec.Metadata.Instance.Userdata, "ssh_authorized_keys=\\\"- abc ", "expected to find ssh_keys") + assert.Contains(hw.Spec.Metadata.Instance.Userdata, "token=token", "expected to find token") + assert.Contains(hw.Spec.Metadata.Instance.Userdata, "password=password", "expected to find password") + assert.Contains(hw.Spec.Metadata.Instance.Userdata, "harvester.install.vip=192.168.1.100", "expected to find a vip") + assert.Contains(hw.Spec.Metadata.Instance.Userdata, "harvester.install.vip_mode=static", "expected to find vipMode static") + assert.Equal(hw.Spec.Interfaces[0].DHCP.MAC, i.Spec.ManagementInterfaceMacAddress, "expected to find correct hardware address") + assert.Equal(hw.Spec.Interfaces[0].DHCP.IP.Gateway, i.Status.Gateway, "expected to find correct gateway") + assert.Equal(hw.Spec.Interfaces[0].DHCP.IP.Address, i.Status.Address, "expected to find correct address") + assert.Equal(hw.Spec.Interfaces[0].DHCP.IP.Netmask, i.Status.Netmask, "expected to find correct netmask") + assert.Contains(hw.Spec.Metadata.Instance.Userdata, "scheme_version") + assert.NotContains(hw.Spec.Metadata.Instance.Userdata, "harvester.install.networks.harvester-mgmt", "expected to find harvester-mgmt in interfaces") + assert.Contains(hw.Spec.Metadata.Instance.Userdata, "harvester.install.management_interface", "expected to not find management_interface") } -func Test_GenerateHWRequestWithJoin(t *testing.T) { +func Test_GenerateHWRequestWithJoinV10(t *testing.T) { assert := require.New(t) i.Status.Conditions = util.RemoveCondition(i.Status.Conditions, seederv1alpha1.HarvesterCreateNode) hw, err := GenerateHWRequest(i, c) assert.NoError(err, "no error should occur during hardware generation") assert.Contains(hw.Spec.Metadata.Instance.Userdata, "harvester.server_url=https://192.168.1.100:8443", "expected to find join url") } + +func Test_GenerateHWRequestWithJoinV11(t *testing.T) { + assert := require.New(t) + i.Status.Conditions = util.RemoveCondition(i.Status.Conditions, seederv1alpha1.HarvesterCreateNode) + clusterCopy := c.DeepCopy() + clusterCopy.Spec.HarvesterVersion = "v1.1.0" + hw, err := GenerateHWRequest(i, clusterCopy) + assert.NoError(err, "no error should occur during hardware generation") + assert.Contains(hw.Spec.Metadata.Instance.Userdata, "harvester.server_url=https://192.168.1.100", "expected to find join url") +} diff --git a/scripts/build b/scripts/build new file mode 100755 index 0000000..a8a4ec0 --- /dev/null +++ b/scripts/build @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +cd $(dirname $0)/.. +go fmt ./... +go vet ./... +controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..." +CGO_ENABLED=0 go build -o bin/manager . +mkdir -p bin \ No newline at end of file diff --git a/scripts/ci b/scripts/ci new file mode 100755 index 0000000..9d40087 --- /dev/null +++ b/scripts/ci @@ -0,0 +1,7 @@ +#!/bin/bash +set -e + +cd $(dirname $0) + +./test +./build \ No newline at end of file diff --git a/scripts/entry b/scripts/entry new file mode 100755 index 0000000..6315671 --- /dev/null +++ b/scripts/entry @@ -0,0 +1,11 @@ +#!/bin/bash +set -e + +mkdir -p bin +if [ -e ./scripts/$1 ]; then + ./scripts/"$@" +else + exec "$@" +fi + +chown -R $DAPPER_UID:$DAPPER_GID . \ No newline at end of file diff --git a/scripts/generate b/scripts/generate new file mode 100755 index 0000000..e69de29 diff --git a/scripts/package b/scripts/package new file mode 100755 index 0000000..17a53d5 --- /dev/null +++ b/scripts/package @@ -0,0 +1,11 @@ +#!/bin/bash +set -e + +source $(dirname $0)/version +cd $(dirname $0)/.. + +IMAGE=${REPO}/harvester-seeder:${TAG} +DOCKERFILE=package/Dockerfile + +docker build -f ${DOCKERFILE} -t ${IMAGE} . +echo Built ${IMAGE} \ No newline at end of file diff --git a/scripts/test b/scripts/test new file mode 100755 index 0000000..c0022c6 --- /dev/null +++ b/scripts/test @@ -0,0 +1,3 @@ +set -e +cd $(dirname $0)/.. +go test -coverprofile /tmp/cover.out -timeout=20m -p 1 -v ./... \ No newline at end of file diff --git a/scripts/version b/scripts/version new file mode 100755 index 0000000..3d5038c --- /dev/null +++ b/scripts/version @@ -0,0 +1,35 @@ +#!/bin/bash + +if [ -n "$(git status --porcelain --untracked-files=no)" ]; then + DIRTY="-dirty" +fi + +COMMIT=$(git rev-parse --short HEAD) +GIT_TAG=${DRONE_TAG:-$(git tag -l --contains HEAD | head -n 1)} + +if [[ -z "$DIRTY" && -n "$GIT_TAG" ]]; then + VERSION=$GIT_TAG +else + VERSION="${COMMIT}${DIRTY}" +fi + +if [ -z "$ARCH" ]; then + ARCH=$(go env GOHOSTARCH) +fi + +if [ -z "$OS" ]; then + OS=$(go env GOHOSTOS) +fi + +SUFFIX="-${ARCH}" + +HELM_TAG=${TAG:-${VERSION}} +HELM_VERSION=${HELM_TAG/v/} +TAG=${TAG:-${VERSION}${SUFFIX}} +REPO=${REPO:-rancher} + +if echo $TAG | grep -q dirty; then + TAG=dev + HELM_TAG=dev + HELM_VERSION=0.0.0-dev +fi \ No newline at end of file