diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..9bcfb6aa6b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +.git +.github +.vscode +bin/ +config/ +hack/ +docs/ +examples/ +**/.md diff --git a/.gitignore b/.gitignore index 79fcfb67a8..b8cc550408 100644 --- a/.gitignore +++ b/.gitignore @@ -96,6 +96,7 @@ network_closure.sh # User cluster configs .kubeconfig +kubeconfig .tags* diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000000..ffa54665b8 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,21 @@ +linters: + enable: + - golint + - govet + - gofmt + - structcheck + - varcheck + - interfacer + - unconvert + - ineffassign + - goconst + - misspell + - nakedret + - prealloc + - deadcode + disable-all: true + # Run with --fast=false for more extensive checks + fast: true +issue: + max-same-issues: 0 + max-per-linter: 0 diff --git a/Dockerfile b/Dockerfile index 5667750c49..17908b425d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -41,20 +41,19 @@ # Build the manager binary FROM golang:1.12.9 -# default the go proxy +# Run this with docker build --build_arg $(go env GOPROXY) to override the goproxy ARG goproxy=https://proxy.golang.org - -# run this with docker build --build_arg $(go env GOPROXY) to override the goproxy ENV GOPROXY=$goproxy 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 +# 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 the sources COPY main.go main.go COPY api/ api/ COPY controllers/ controllers/ diff --git a/Makefile b/Makefile index c30273b202..d2410ec299 100644 --- a/Makefile +++ b/Makefile @@ -1,136 +1,81 @@ +# Copyright 2019 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# If you update this file, please follow: +# https://suva.sh/posts/well-documented-makefiles/ + +# Ensure Make is run with bash shell as some syntax below is bash-specific +SHELL:=/usr/bin/env bash + +.DEFAULT_GOAL:=help + +# Use GOPROXY environment variable if set +GOPROXY := $(shell go env GOPROXY) +ifeq ($(GOPROXY),) +GOPROXY := https://proxy.golang.org +endif +export GOPROXY +# Activate module mode, as we use go modules to manage dependencies +export GO111MODULE=on -# Allow overriding manifest generation destination directory -MANIFEST_ROOT ?= "config" -CRD_ROOT ?= "$(MANIFEST_ROOT)/crd/bases" -WEBHOOK_ROOT ?= "$(MANIFEST_ROOT)/webhook" -RBAC_ROOT ?= "$(MANIFEST_ROOT)/rbac" +# Directories. +TOOLS_DIR := hack/tools +TOOLS_BIN_DIR := $(TOOLS_DIR)/bin +BIN_DIR := bin +# Binaries. +CLUSTERCTL := $(BIN_DIR)/clusterctl +CONTROLLER_GEN := $(TOOLS_BIN_DIR)/controller-gen +GOLANGCI_LINT := $(TOOLS_BIN_DIR)/golangci-lint +MOCKGEN := $(TOOLS_BIN_DIR)/mockgen +# Define Docker related variables. Releases should modify and double check these vars. +REGISTRY ?= k8scloudprovider +CONTROLLER_IMG ?= $(REGISTRY)/cluster-api-openstack-controller +TAG ?= dev +ARCH ?= amd64 +ALL_ARCH = amd64 arm arm64 ppc64le s390x -GIT_HOST = sigs.k8s.io -PWD := $(shell pwd) -BASE_DIR := $(shell basename $(PWD)) +# Allow overriding manifest generation destination directory +MANIFEST_ROOT ?= config +CRD_ROOT ?= $(MANIFEST_ROOT)/crd/bases +WEBHOOK_ROOT ?= $(MANIFEST_ROOT)/webhook +RBAC_ROOT ?= $(MANIFEST_ROOT)/rbac -HAS_LINT := $(shell command -v golint;) -HAS_GOX := $(shell command -v gox;) +# Check if binaries exist HAS_YQ := $(shell command -v yq;) HAS_KUSTOMIZE := $(shell command -v kustomize;) HAS_ENVSUBST := $(shell command -v envsubst;) -GOX_PARALLEL ?= 3 -TARGETS ?= darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le -DIST_DIRS = find * -type d -exec - -GOOS ?= $(shell go env GOOS) -VERSION ?= $(shell git describe --exact-match 2> /dev/null || \ - git describe --match=$(git rev-parse --short=8 HEAD) --always --dirty --abbrev=8) -GOFLAGS := -TAGS := -LDFLAGS := "-w -s -X 'main.version=${VERSION}'" -REGISTRY ?= k8scloudprovider - -MANAGER_IMAGE_NAME ?= cluster-api-provider-openstack -MANAGER_IMAGE_TAG ?= dev -PULL_POLICY ?= Always - -# Used in docker-* targets. -MANAGER_IMAGE ?= $(REGISTRY)/$(MANAGER_IMAGE_NAME):$(MANAGER_IMAGE_TAG) - - -build: binary images - -binary: manager clusterctl - -manager: - CGO_ENABLED=0 GOOS=$(GOOS) go build -v \ - -ldflags $(LDFLAGS) \ - -o bin/manager \ - cmd/manager/main.go - -clusterctl: - CGO_ENABLED=0 GOOS=$(GOOS) go build \ - -ldflags $(LDFLAGS) \ - -o bin/clusterctl \ - cmd/clusterctl/main.go - -check: vendor fmt vet lint - -fmt: - hack/verify-gofmt.sh - -lint: -ifndef HAS_LINT - go get -u golang.org/x/lint/golint - echo "installing golint" -endif - hack/verify-golint.sh - -vet: - go vet ./pkg/... ./cmd/... - -cover: generate vendor - go test -tags=unit ./pkg/... ./cmd/... -cover - -docs: - @echo "$@ not yet implemented" -godoc: - @echo "$@ not yet implemented" - -releasenotes: - @echo "Reno not yet implemented for this repo" - -translation: - @echo "$@ not yet implemented" - -# Do the work here - -# Set up the development environment -env: - @echo "PWD: $(PWD)" - @echo "BASE_DIR: $(BASE_DIR)" - go version - go env +## -------------------------------------- +## Help +## -------------------------------------- -shell: - $(SHELL) -i +help: ## Display this help + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) -images: docker-build +## -------------------------------------- +## Define targets for prow +## -------------------------------------- -# Build the docker image -.PHONY: docker-build -docker-build: - docker build . -t ${MANAGER_IMAGE} - -upload-images: images - @echo "push images to $(REGISTRY)" - docker login -u="$(DOCKER_USERNAME)" -p="$(DOCKER_PASSWORD)"; - docker push $(REGISTRY)/openstack-cluster-api-controller:$(VERSION) - docker push $(REGISTRY)/openstack-cluster-api-clusterctl:$(VERSION) - -version: - @echo ${VERSION} - -.PHONY: build-cross -build-cross: LDFLAGS += -extldflags "-static" -build-cross: vendor -ifndef HAS_GOX - go get -u github.com/mitchellh/gox -endif - CGO_ENABLED=0 gox -parallel=$(GOX_PARALLEL) -output="_dist/{{.OS}}-{{.Arch}}/{{.Dir}}" -osarch='$(TARGETS)' $(GOFLAGS) $(if $(TAGS),-tags '$(TAGS)',) -ldflags '$(LDFLAGS)' $(GIT_HOST)/$(BASE_DIR)/cmd/openstack-machine-controller/ -.PHONY: dist -dist: build-cross - ( \ - cd _dist && \ - $(DIST_DIRS) cp ../LICENSE {} \; && \ - $(DIST_DIRS) cp ../README.md {} \; && \ - $(DIST_DIRS) tar -zcf cluster-api-provider-openstack-$(VERSION)-{}.tar.gz {} \; && \ - $(DIST_DIRS) zip -r cluster-api-provider-openstack-$(VERSION)-{}.zip {} \; \ - ) +.PHONY: images +images: docker-build ## Build all images -# TODO(sbueringer) target below are already cleaned up after v1alpha2 refactoring -# targets above have to be cleaned up +.PHONY: check +check: modules generate lint-full test ## -------------------------------------- ## Testing @@ -142,71 +87,232 @@ test: generate lint ## Run tests $(MAKE) test-generate-examples .PHONY: test-go -test-go: ## Run tests - go test -v -tags=unit ./api/... ./pkg/... ./controllers/... +test-go: ## Run golang tests + go test -v ./api/... ./pkg/... ./controllers/... + # TODO change to ./... as soon as vendor is removed test-generate-examples: ifndef HAS_YQ - go get github.com/mikefarah/yq echo "installing yq" + GO111MODULE=off go get github.com/mikefarah/yq endif ifndef HAS_KUSTOMIZE - GO111MODULE=on go get sigs.k8s.io/kustomize/v3/cmd/kustomize echo "installing kustomize" + go get sigs.k8s.io/kustomize/v3/cmd/kustomize endif ifndef HAS_ENVSUBST - go get github.com/a8m/envsubst/cmd/envsubst echo "installing envsubst" + go get github.com/a8m/envsubst/cmd/envsubst endif # Create a dummy file for test only - mkdir tmp - echo 'clouds' > tmp/dummy-clouds-test.yaml - examples/generate.sh -f tmp/dummy-clouds-test.yaml openstack tmp/dummy-make-auto-test + mkdir -p tmp/dummy-make-auto-test + echo 'clouds' > tmp/dummy-make-auto-test/dummy-clouds-test.yaml + examples/generate.sh -f tmp/dummy-make-auto-test/dummy-clouds-test.yaml openstack tmp/dummy-make-auto-test/_out # the folder will be generated under same folder of examples rm -rf tmp/dummy-make-auto-test - rm tmp/dummy-clouds-test.yaml + +## -------------------------------------- +## Binaries +## -------------------------------------- + +.PHONY: binaries +binaries: manager ## Builds and installs all binaries + +.PHONY: manager +manager: ## Build manager binary. + go build -o $(BIN_DIR)/manager . + +## -------------------------------------- +## Tooling Binaries +## -------------------------------------- + +$(CLUSTERCTL): go.mod ## Build clusterctl binary. + go build -o $(BIN_DIR)/clusterctl sigs.k8s.io/cluster-api/cmd/clusterctl + +$(CONTROLLER_GEN): $(TOOLS_DIR)/go.mod # Build controller-gen from tools folder. + cd $(TOOLS_DIR); go build -tags=tools -o $(BIN_DIR)/controller-gen sigs.k8s.io/controller-tools/cmd/controller-gen + +$(GOLANGCI_LINT): $(TOOLS_DIR)/go.mod # Build golangci-lint from tools folder. + cd $(TOOLS_DIR); go build -tags=tools -o $(BIN_DIR)/golangci-lint github.com/golangci/golangci-lint/cmd/golangci-lint + +$(MOCKGEN): $(TOOLS_DIR)/go.mod # Build mockgen from tools folder. + cd $(TOOLS_DIR); go build -tags=tools -o $(BIN_DIR)/mockgen github.com/golang/mock/mockgen + +## -------------------------------------- +## Linting +## -------------------------------------- + +.PHONY: lint +lint: $(GOLANGCI_LINT) ## Lint codebase + $(GOLANGCI_LINT) run -v + +lint-full: $(GOLANGCI_LINT) ## Run slower linters to detect possible issues + $(GOLANGCI_LINT) run -v --fast=false ## -------------------------------------- ## Generate ## -------------------------------------- -.PHONY: vendor -vendor: ## Runs go mod to ensure proper vendoring. - ./hack/update-vendor.sh +.PHONY: modules +modules: ## Runs go mod to ensure proper vendoring. + go mod tidy + cd $(TOOLS_DIR); go mod tidy .PHONY: generate generate: ## Generate code $(MAKE) generate-go $(MAKE) generate-manifests - $(MAKE) generate-deepcopy .PHONY: generate-go -generate-go: ## Runs go generate +generate-go: $(CONTROLLER_GEN) $(MOCKGEN) ## Runs Go related generate targets go generate ./pkg/... ./cmd/... + # TODO change to ./.. as soon as vendor is removed + $(CONTROLLER_GEN) \ + paths=./api/... \ + object:headerFile=./hack/boilerplate.go.txt .PHONY: generate-manifests generate-manifests: ## Generate manifests e.g. CRD, RBAC etc. - go run vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go \ + $(CONTROLLER_GEN) \ paths=./api/... \ crd:trivialVersions=true \ output:crd:dir=$(CRD_ROOT) \ output:webhook:dir=$(WEBHOOK_ROOT) \ webhook - go run vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go \ + $(CONTROLLER_GEN) \ paths=./controllers/... \ output:rbac:dir=$(RBAC_ROOT) \ rbac:roleName=manager-role -.PHONY: generate-deepcopy -generate-deepcopy: ## Runs controller-gen to generate deepcopy files. - go run vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go \ - paths=./api/... \ - object:headerFile=./hack/boilerplate/boilerplate.generatego.txt - .PHONY: generate-examples generate-examples: clean-examples ## Generate examples configurations to run a cluster. ./examples/generate.sh +## -------------------------------------- +## Docker +## -------------------------------------- + +.PHONY: docker-build +docker-build: ## Build the docker image for controller-manager + docker build --pull --build-arg ARCH=$(ARCH) . -t $(CONTROLLER_IMG)-$(ARCH):$(TAG) + MANIFEST_IMG=$(CONTROLLER_IMG)-$(ARCH) MANIFEST_TAG=$(TAG) $(MAKE) set-manifest-image + +.PHONY: docker-push +docker-push: ## Push the docker image + docker push $(CONTROLLER_IMG)-$(ARCH):$(TAG) + +## -------------------------------------- +## Docker — All ARCH +## -------------------------------------- + +.PHONY: docker-build-all ## Build all the architecture docker images +docker-build-all: $(addprefix docker-build-,$(ALL_ARCH)) + +docker-build-%: + $(MAKE) ARCH=$* docker-build + +.PHONY: docker-push-all ## Push all the architecture docker images +docker-push-all: $(addprefix docker-push-,$(ALL_ARCH)) + $(MAKE) docker-push-manifest + +docker-push-%: + $(MAKE) ARCH=$* docker-push + +.PHONY: docker-push-manifest +docker-push-manifest: ## Push the fat manifest docker image. + ## Minimum docker version 18.06.0 is required for creating and pushing manifest images. + docker manifest create --amend $(CONTROLLER_IMG):$(TAG) $(shell echo $(ALL_ARCH) | sed -e "s~[^ ]*~$(CONTROLLER_IMG)\-&:$(TAG)~g") + @for arch in $(ALL_ARCH); do docker manifest annotate --arch $${arch} ${CONTROLLER_IMG}:${TAG} ${CONTROLLER_IMG}-$${arch}:${TAG}; done + MANIFEST_IMG=$(CONTROLLER_IMG) MANIFEST_TAG=$(TAG) $(MAKE) set-manifest-image + +.PHONY: set-manifest-image +set-manifest-image: + $(info Updating kustomize image patch file for manager resource) + sed -i'' -e 's@image: .*@image: '"${MANIFEST_IMG}:$(MANIFEST_TAG)"'@' ./config/default/manager_image_patch.yaml + +## -------------------------------------- +## Release TODO(sbueringer): just copied over from CAPA right now, have to implement that for OpenStack +## -------------------------------------- + +RELEASE_TAG := $(shell git describe --abbrev=0 2>/dev/null) + +.PHONY: release +release: ## Builds and push container images using the latest git tag for the commit. + @if [ -z "${RELEASE_TAG}" ]; then echo "RELEASE_TAG is not set"; exit 1; fi + # Push the release image to the staging bucket first. + REGISTRY=gcr.io/k8s-staging-cluster-api-openstack TAG=$(RELEASE_TAG) \ + $(MAKE) docker-build-all docker-push-all + # Set the manifest image to the production bucket. + REGISTRY=us.gcr.io/k8s-artifacts-prod/cluster-api-openstack TAG=$(RELEASE_TAG) \ + set-manifest-image + # Generate release artifacts. + mkdir -p out/ + kustomize build config/default > out/infrastructure-components.yaml + +.PHONY: release-staging-latest +release-staging-latest: ## Builds and push container images to the staging bucket using "latest" tag. + REGISTRY=gcr.io/k8s-staging-cluster-api-openstack TAG=latest \ + $(MAKE) docker-build-all docker-push-all + +## -------------------------------------- +## Development +## -------------------------------------- + +.PHONY: create-cluster +create-cluster: $(CLUSTERCTL) ## Create a development Kubernetes cluster on OpenStack using examples + $(CLUSTERCTL) \ + create cluster -v 4 \ + --bootstrap-flags="name=clusterapi" \ + --bootstrap-type kind \ + -m ./examples/_out/controlplane.yaml \ + -c ./examples/_out/cluster.yaml \ + -p ./examples/_out/provider-components.yaml \ + -a ./examples/addons.yaml + +.PHONY: create-cluster-management +create-cluster-management: $(CLUSTERCTL) ## Create a development Kubernetes cluster on OpenStack in a KIND management cluster. + kind create cluster --name=clusterapi + # Apply provider-components. + kubectl \ + --kubeconfig=$$(kind get kubeconfig-path --name="clusterapi") \ + create -f examples/_out/provider-components.yaml + # Create Cluster. + kubectl \ + --kubeconfig=$$(kind get kubeconfig-path --name="clusterapi") \ + create -f examples/_out/cluster.yaml + # Create control plane machine. + kubectl \ + --kubeconfig=$$(kind get kubeconfig-path --name="clusterapi") \ + create -f examples/_out/controlplane.yaml + # Get KubeConfig using clusterctl. + $(CLUSTERCTL) \ + alpha phases get-kubeconfig -v=3 \ + --kubeconfig=$$(kind get kubeconfig-path --name="clusterapi") \ + --namespace=default \ + --cluster-name=test1 + # Apply addons on the target cluster, waiting for the control-plane to become available. + $(CLUSTERCTL) \ + alpha phases apply-addons -v=3 \ + --kubeconfig=./kubeconfig \ + -a examples/addons.yaml + # Create a worker node with MachineDeployment. + kubectl \ + --kubeconfig=$$(kind get kubeconfig-path --name="clusterapi") \ + create -f examples/_out/worker.yaml + +.PHONY: delete-cluster +delete-cluster: $(CLUSTERCTL) ## Deletes the development Kubernetes Cluster "test1" + $(CLUSTERCTL) \ + delete cluster -v 4 \ + --bootstrap-type kind \ + --bootstrap-flags="name=clusterapi" \ + --cluster test1 \ + --kubeconfig ./kubeconfig \ + -p ./examples/out/provider-components.yaml \ + +kind-reset: ## Destroys the "clusterapi" kind cluster. + kind delete cluster --name=clusterapi || true + ## -------------------------------------- ## Cleanup / Verification ## -------------------------------------- @@ -219,6 +325,7 @@ clean: ## Remove all generated files .PHONY: clean-bin clean-bin: ## Remove all generated binaries rm -rf bin + rm -rf hack/tools/bin .PHONY: clean-temporary clean-temporary: ## Remove all temporary files and folders @@ -231,6 +338,6 @@ clean-examples: ## Remove all the temporary files generated in the examples fold rm -rf examples/_out/ rm -f examples/provider-components/provider-components-*.yaml - -.PHONY: build clean cover vendor docs fmt functional lint \ - translation version build-cross dist manifests +.PHONY: verify-install +verify-install: ## Checks that you've installed this repository correctly + ./hack/verify-install.sh diff --git a/PROJECT b/PROJECT index 7cfe30b9ba..824f3d401d 100644 --- a/PROJECT +++ b/PROJECT @@ -8,3 +8,6 @@ resources: - group: infrastructure version: v1alpha2 kind: OpenStackMachine +- group: infrastructure + version: v1alpha2 + kind: OpenStackMachineTemplate diff --git a/api/v1alpha2/openstackcluster_types.go b/api/v1alpha2/openstackcluster_types.go index 02e9891a3a..7442cb253d 100644 --- a/api/v1alpha2/openstackcluster_types.go +++ b/api/v1alpha2/openstackcluster_types.go @@ -1,4 +1,5 @@ /* +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,9 +27,6 @@ const ( ClusterFinalizer = "openstackcluster.infrastructure.cluster.x-k8s.io" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - // OpenStackClusterSpec defines the desired state of OpenStackCluster type OpenStackClusterSpec struct { @@ -55,7 +53,7 @@ type OpenStackClusterSpec struct { // UseOctavia is weather LoadBalancer Service is Octavia or not // +optional - UseOctavia bool `json:"useOctavia, omitempty"` + UseOctavia bool `json:"useOctavia,omitempty"` // ManagedAPIServerLoadBalancer defines whether a LoadBalancer for the // APIServer should be created. If set to true the following properties are diff --git a/api/v1alpha2/openstackmachine_types.go b/api/v1alpha2/openstackmachine_types.go index 2b58b7f0f7..210d929455 100644 --- a/api/v1alpha2/openstackmachine_types.go +++ b/api/v1alpha2/openstackmachine_types.go @@ -1,4 +1,5 @@ /* +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -27,9 +28,6 @@ const ( MachineFinalizer = "openstackmachine.infrastructure.cluster.x-k8s.io" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - // OpenStackMachineSpec defines the desired state of OpenStackMachine type OpenStackMachineSpec struct { @@ -52,7 +50,7 @@ type OpenStackMachineSpec struct { Image string `json:"image"` // The ssh key to inject in the instance - KeyName string `json:"keyName,omitempty"` + SSHKeyName string `json:"sshKeyName,omitempty"` // A networks object. Required parameter when there are multiple networks defined for the tenant. // When you do not specify the networks parameter, the server attaches to the only network created for the current tenant. diff --git a/api/v1alpha2/openstackmachinetemplate_types.go b/api/v1alpha2/openstackmachinetemplate_types.go new file mode 100644 index 0000000000..1436f6a051 --- /dev/null +++ b/api/v1alpha2/openstackmachinetemplate_types.go @@ -0,0 +1,51 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// OpenStackMachineTemplateSpec defines the desired state of OpenStackMachineTemplate +type OpenStackMachineTemplateSpec struct { + Template OpenStackMachineTemplateResource `json:"template"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=openstackmachinetemplates,scope=Namespaced,categories=cluster-api +// +kubebuilder:storageversion + +// OpenStackMachineTemplate is the Schema for the openstackmachinetemplates API +type OpenStackMachineTemplate struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec OpenStackMachineTemplateSpec `json:"spec,omitempty"` +} + +// +kubebuilder:object:root=true + +// OpenStackMachineTemplateList contains a list of OpenStackMachineTemplate +type OpenStackMachineTemplateList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []OpenStackMachineTemplate `json:"items"` +} + +func init() { + SchemeBuilder.Register(&OpenStackMachineTemplate{}, &OpenStackMachineTemplateList{}) +} diff --git a/api/v1alpha2/types.go b/api/v1alpha2/types.go index a9fadcd522..74b8f4a986 100644 --- a/api/v1alpha2/types.go +++ b/api/v1alpha2/types.go @@ -1,5 +1,11 @@ package v1alpha2 +// OpenStackMachineTemplateResource describes the data needed to create a OpenStackMachine from a template +type OpenStackMachineTemplateResource struct { + // Spec is the specification of the desired behavior of the machine. + Spec OpenStackMachineSpec `json:"spec"` +} + type ExternalRouterIPParam struct { // The FixedIP in the corresponding subnet FixedIP string `json:"fixedIP,omitempty"` @@ -36,7 +42,7 @@ type NetworkParam struct { // The UUID of the network. Required if you omit the port attribute. UUID string `json:"uuid,omitempty"` // A fixed IPv4 address for the NIC. - FixedIp string `json:"fixedIp,omitempty"` + FixedIP string `json:"fixedIp,omitempty"` // Filters for optional network query Filter Filter `json:"filter,omitempty"` // Subnet within a network to use diff --git a/api/v1alpha2/zz_generated.deepcopy.go b/api/v1alpha2/zz_generated.deepcopy.go index 74562735b8..82410986ab 100644 --- a/api/v1alpha2/zz_generated.deepcopy.go +++ b/api/v1alpha2/zz_generated.deepcopy.go @@ -1,7 +1,7 @@ // +build !ignore_autogenerated /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -473,6 +473,96 @@ func (in *OpenStackMachineStatus) DeepCopy() *OpenStackMachineStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenStackMachineTemplate) DeepCopyInto(out *OpenStackMachineTemplate) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenStackMachineTemplate. +func (in *OpenStackMachineTemplate) DeepCopy() *OpenStackMachineTemplate { + if in == nil { + return nil + } + out := new(OpenStackMachineTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OpenStackMachineTemplate) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenStackMachineTemplateList) DeepCopyInto(out *OpenStackMachineTemplateList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]OpenStackMachineTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenStackMachineTemplateList. +func (in *OpenStackMachineTemplateList) DeepCopy() *OpenStackMachineTemplateList { + if in == nil { + return nil + } + out := new(OpenStackMachineTemplateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OpenStackMachineTemplateList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenStackMachineTemplateResource) DeepCopyInto(out *OpenStackMachineTemplateResource) { + *out = *in + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenStackMachineTemplateResource. +func (in *OpenStackMachineTemplateResource) DeepCopy() *OpenStackMachineTemplateResource { + if in == nil { + return nil + } + out := new(OpenStackMachineTemplateResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenStackMachineTemplateSpec) DeepCopyInto(out *OpenStackMachineTemplateSpec) { + *out = *in + in.Template.DeepCopyInto(&out.Template) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenStackMachineTemplateSpec. +func (in *OpenStackMachineTemplateSpec) DeepCopy() *OpenStackMachineTemplateSpec { + if in == nil { + return nil + } + out := new(OpenStackMachineTemplateSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RootVolume) DeepCopyInto(out *RootVolume) { *out = *in diff --git a/config/certmanager/certificate.yaml b/config/certmanager/certificate.yaml index 02bdc1bfff..7013bd4235 100644 --- a/config/certmanager/certificate.yaml +++ b/config/certmanager/certificate.yaml @@ -1,6 +1,6 @@ # The following manifests contain a self-signed issuer CR and a certificate CR. # More document can be found at https://docs.cert-manager.io -apiVersion: certmanager.k8s.io/v1alpha1 +apiVersion: certmanager.k8s.io/v1alpha2 kind: Issuer metadata: name: selfsigned-issuer @@ -8,16 +8,16 @@ metadata: spec: selfSigned: {} --- -apiVersion: certmanager.k8s.io/v1alpha1 +apiVersion: certmanager.k8s.io/v1alpha2 kind: Certificate metadata: name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml namespace: system spec: - # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize - commonName: $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc + # $(SERVICENAME) and $(NAMESPACE) will be substituted by kustomize + commonName: $(SERVICENAME).$(NAMESPACE).svc dnsNames: - - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local + - $(SERVICENAME).$(NAMESPACE).svc.cluster.local issuerRef: kind: Issuer name: selfsigned-issuer diff --git a/config/certmanager/kustomization.yaml b/config/certmanager/kustomization.yaml index bebea5a595..35b0a864cb 100644 --- a/config/certmanager/kustomization.yaml +++ b/config/certmanager/kustomization.yaml @@ -1,5 +1,26 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - certificate.yaml - +# the following config is for teaching kustomize how to do var substitution +vars: +- name: NAMESPACE # namespace of the service and the certificate CR + objref: + kind: Service + version: v1 + name: webhook-service + fieldref: + fieldpath: metadata.namespace +- name: CERTIFICATENAME + objref: + kind: Certificate + group: certmanager.k8s.io + version: v1alpha2 + name: serving-cert # this name should match the one in certificate.yaml +- name: SERVICENAME + objref: + kind: Service + version: v1 + name: webhook-service configurations: - kustomizeconfig.yaml diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml index 9334a785ba..763d909cff 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml @@ -66,9 +66,6 @@ spec: If the RootVolume is specified, this will be ignored and use rootVolume directly. type: string - keyName: - description: The ssh key to inject in the instance - type: string networks: description: A networks object. Required parameter when there are multiple networks defined for the tenant. When you do not specify the networks @@ -241,6 +238,9 @@ spec: description: Metadata mapping. Allows you to create a map of key value pairs to add to the server instance. type: object + sshKeyName: + description: The ssh key to inject in the instance + type: string tags: description: Machine tags Requires Nova api 2.52 minimum! items: diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml new file mode 100644 index 0000000000..211c772699 --- /dev/null +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml @@ -0,0 +1,301 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + creationTimestamp: null + name: openstackmachinetemplates.infrastructure.cluster.x-k8s.io +spec: + group: infrastructure.cluster.x-k8s.io + names: + categories: + - cluster-api + kind: OpenStackMachineTemplate + plural: openstackmachinetemplates + scope: Namespaced + validation: + openAPIV3Schema: + description: OpenStackMachineTemplate is the Schema for the openstackmachinetemplates + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: OpenStackMachineTemplateSpec defines the desired state of OpenStackMachineTemplate + properties: + template: + description: OpenStackMachineTemplateResource describes the data needed + to create a OpenStackMachine from a template + properties: + spec: + description: Spec is the specification of the desired behavior of + the machine. + properties: + availabilityZone: + description: The availability zone from which to launch the + server. + type: string + cloudName: + description: The name of the cloud to use from the clouds secret + type: string + cloudsSecret: + description: The name of the secret containing the openstack + credentials + properties: + name: + description: Name is unique within a namespace to reference + a secret resource. + type: string + namespace: + description: Namespace defines the space within which the + secret name must be unique. + type: string + type: object + configDrive: + description: Config Drive support + type: boolean + flavor: + description: The flavor reference for the flavor for your server + instance. + type: string + floatingIP: + description: The floatingIP which will be associated to the + machine, only used for master. The floatingIP should have + been created and haven't been associated. + type: string + image: + description: The name of the image to use for your server instance. + If the RootVolume is specified, this will be ignored and use + rootVolume directly. + type: string + networks: + description: A networks object. Required parameter when there + are multiple networks defined for the tenant. When you do + not specify the networks parameter, the server attaches to + the only network created for the current tenant. + items: + properties: + filter: + description: Filters for optional network query + properties: + adminStateUp: + type: boolean + description: + type: string + id: + type: string + limit: + type: integer + marker: + type: string + name: + type: string + notTags: + type: string + notTagsAny: + type: string + projectId: + type: string + shared: + type: boolean + sortDir: + type: string + sortKey: + type: string + status: + type: string + tags: + type: string + tagsAny: + type: string + tenantId: + type: string + type: object + fixedIp: + description: A fixed IPv4 address for the NIC. + type: string + subnets: + description: Subnet within a network to use + items: + properties: + filter: + description: Filters for optional network query + properties: + cidr: + type: string + description: + type: string + enableDhcp: + type: boolean + gateway_ip: + type: string + id: + type: string + ipVersion: + type: integer + ipv6AddressMode: + type: string + ipv6RaMode: + type: string + limit: + type: integer + marker: + type: string + name: + type: string + networkId: + type: string + notTags: + type: string + notTagsAny: + type: string + projectId: + type: string + sortDir: + type: string + sortKey: + type: string + subnetpoolId: + type: string + tags: + type: string + tagsAny: + type: string + tenantId: + type: string + type: object + uuid: + description: The UUID of the network. Required if + you omit the port attribute. + type: string + type: object + type: array + uuid: + description: The UUID of the network. Required if you + omit the port attribute. + type: string + type: object + type: array + providerID: + description: ProviderID is the unique identifier as specified + by the cloud provider. + type: string + rootVolume: + description: The volume metadata to boot from + properties: + deviceType: + type: string + diskSize: + type: integer + sourceType: + type: string + sourceUUID: + type: string + type: object + securityGroups: + description: The names of the security groups to assign to the + instance + items: + properties: + filter: + description: Filters used to query security groups in + openstack + properties: + description: + type: string + id: + type: string + limit: + type: integer + marker: + type: string + name: + type: string + notTags: + type: string + notTagsAny: + type: string + projectId: + type: string + sortDir: + type: string + sortKey: + type: string + tags: + type: string + tagsAny: + type: string + tenantId: + type: string + type: object + name: + description: Security Group name + type: string + uuid: + description: Security Group UID + type: string + type: object + type: array + serverMetadata: + additionalProperties: + type: string + description: Metadata mapping. Allows you to create a map of + key value pairs to add to the server instance. + type: object + sshKeyName: + description: The ssh key to inject in the instance + type: string + tags: + description: Machine tags Requires Nova api 2.52 minimum! + items: + type: string + type: array + trunk: + description: Whether the server instance is created on a trunk + port or not. + type: boolean + userDataSecret: + description: The name of the secret containing the user data + (startup script in most cases) + properties: + name: + description: Name is unique within a namespace to reference + a secret resource. + type: string + namespace: + description: Namespace defines the space within which the + secret name must be unique. + type: string + type: object + required: + - flavor + - image + type: object + required: + - spec + type: object + required: + - template + type: object + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index f74481cf3c..f260f88e62 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -1,22 +1,27 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization # This kustomization.yaml is not intended to be run by itself, # since it depends on service name and namespace that are out of this kustomize package. # It should be run by config/default resources: - bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml - bases/infrastructure.cluster.x-k8s.io_openstackmachines.yaml +- bases/infrastructure.cluster.x-k8s.io_openstackmachinetemplates.yaml # +kubebuilder:scaffold:crdkustomizeresource -patchesStrategicMerge: +#patches: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD #- patches/webhook_in_openstackclusters.yaml #- patches/webhook_in_openstackmachines.yaml +#- patches/webhook_in_openstackmachinetemplates.yaml # +kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD #- patches/cainjection_in_openstackclusters.yaml #- patches/cainjection_in_openstackmachines.yaml +#- patches/cainjection_in_openstackmachinetemplates.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_openstackmachinetemplates.yaml b/config/crd/patches/cainjection_in_openstackmachinetemplates.yaml new file mode 100644 index 0000000000..a152ce157d --- /dev/null +++ b/config/crd/patches/cainjection_in_openstackmachinetemplates.yaml @@ -0,0 +1,8 @@ +# 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 +kind: CustomResourceDefinition +metadata: + annotations: + certmanager.k8s.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: openstackmachinetemplates.infrastructure.cluster.x-k8s.io diff --git a/config/crd/patches/webhook_in_openstackmachinetemplates.yaml b/config/crd/patches/webhook_in_openstackmachinetemplates.yaml new file mode 100644 index 0000000000..994aca79a3 --- /dev/null +++ b/config/crd/patches/webhook_in_openstackmachinetemplates.yaml @@ -0,0 +1,17 @@ +# The following patch enables conversion webhook for CRD +# CRD conversion requires k8s 1.13 or later. +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: openstackmachinetemplates.infrastructure.cluster.x-k8s.io +spec: + conversion: + strategy: Webhook + webhookClientConfig: + # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, + # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) + caBundle: Cg== + service: + namespace: system + name: webhook-service + path: /convert diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 342c2eb56e..b8ca8ba504 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -1,3 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + # Adds namespace to all resources. namespace: capo-system @@ -18,7 +21,7 @@ resources: - ../manager # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in crd/kustomization.yaml #- ../webhook -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. +# [CERTMANAGER] To enable cert-manager, uncomment next line. 'WEBHOOK' components are required. #- ../certmanager patchesStrategicMerge: @@ -26,7 +29,7 @@ patchesStrategicMerge: # Protect the /metrics endpoint by putting it behind auth. # Only one of manager_auth_proxy_patch.yaml and # manager_prometheus_metrics_patch.yaml should be enabled. -- manager_auth_proxy_patch.yaml +# - manager_auth_proxy_patch.yaml # If you want your controller-manager to expose the /metrics # endpoint w/o any authn/z, uncomment the following line and # comment manager_auth_proxy_patch.yaml. @@ -37,37 +40,7 @@ patchesStrategicMerge: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in crd/kustomization.yaml #- manager_webhook_patch.yaml -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. -# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. +# [CAINJECTION] Uncomment next line to enable the CA injection in the admission webhooks. +# Uncomment 'CAINJECTION' in crd/kustomization.yaml to enable the CA injection in the admission webhooks. # 'CERTMANAGER' needs to be enabled to use ca injection #- webhookcainjection_patch.yaml - -# the following config is for teaching kustomize how to do var substitution -vars: -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR -# objref: -# kind: Certificate -# group: certmanager.k8s.io -# version: v1alpha1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldref: -# fieldpath: metadata.namespace -#- name: CERTIFICATE_NAME -# objref: -# kind: Certificate -# group: certmanager.k8s.io -# version: v1alpha1 -# name: serving-cert # this name should match the one in certificate.yaml -#- name: SERVICE_NAMESPACE # namespace of the service -# objref: -# kind: Service -# version: v1 -# name: webhook-service -# fieldref: -# fieldpath: metadata.namespace -#- name: SERVICE_NAME -# objref: -# kind: Service -# version: v1 -# name: webhook-service diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml index 989d698878..d3994fb918 100644 --- a/config/default/manager_auth_proxy_patch.yaml +++ b/config/default/manager_auth_proxy_patch.yaml @@ -22,4 +22,3 @@ spec: - name: manager args: - "--metrics-addr=127.0.0.1:8080" - - "--enable-leader-election" diff --git a/config/default/manager_image_patch.yaml b/config/default/manager_image_patch.yaml index 166251ee08..6be964d4ca 100644 --- a/config/default/manager_image_patch.yaml +++ b/config/default/manager_image_patch.yaml @@ -8,5 +8,5 @@ spec: spec: containers: # Change the value of image field below to your controller image URL - - image: docker.io/sbueringer/cluster-api-provider-openstack:dev + - image: k8scloudprovider/openstack-cluster-api-controller:latest name: manager diff --git a/config/default/webhookcainjection_patch.yaml b/config/default/webhookcainjection_patch.yaml index 5e3e077df9..02025d2f7b 100644 --- a/config/default/webhookcainjection_patch.yaml +++ b/config/default/webhookcainjection_patch.yaml @@ -1,15 +1,15 @@ # This patch add annotation to admission webhook config and -# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. +# the variables $(NAMESPACE) and $(CERTIFICATENAME) will be substituted by kustomize. apiVersion: admissionregistration.k8s.io/v1beta1 kind: MutatingWebhookConfiguration metadata: name: mutating-webhook-configuration annotations: - certmanager.k8s.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + certmanager.k8s.io/inject-ca-from: $(NAMESPACE)/$(CERTIFICATENAME) --- apiVersion: admissionregistration.k8s.io/v1beta1 kind: ValidatingWebhookConfiguration metadata: name: validating-webhook-configuration annotations: - certmanager.k8s.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + certmanager.k8s.io/inject-ca-from: $(NAMESPACE)/$(CERTIFICATENAME) diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 5c5f0b84cb..99cf0f7457 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -1,2 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - manager.yaml diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 5b1258581e..c06a9e9c3f 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -16,13 +16,16 @@ spec: selector: matchLabels: control-plane: capo-controller-manager + replicas: 1 template: metadata: labels: control-plane: capo-controller-manager spec: containers: - - name: manager + - args: + - --enable-leader-election image: controller:latest imagePullPolicy: Always + name: manager terminationGracePeriodSeconds: 10 diff --git a/config/rbac/auth_proxy_service.yaml b/config/rbac/auth_proxy_service.yaml index d61e5469fb..0277d13dd2 100644 --- a/config/rbac/auth_proxy_service.yaml +++ b/config/rbac/auth_proxy_service.yaml @@ -6,7 +6,7 @@ metadata: prometheus.io/scheme: https prometheus.io/scrape: "true" labels: - control-plane: controller-manager + control-plane: capo-controller-manager name: controller-manager-metrics-service namespace: system spec: @@ -15,4 +15,4 @@ spec: port: 8443 targetPort: https selector: - control-plane: controller-manager + control-plane: capo-controller-manager diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 817f1fe613..ac51278beb 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -1,11 +1,10 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization resources: - role.yaml - role_binding.yaml - leader_election_role.yaml - leader_election_role_binding.yaml -# Comment the following 3 lines if you want to disable -# the auth proxy (https://github.com/brancz/kube-rbac-proxy) -# which protects your /metrics endpoint. - auth_proxy_service.yaml - auth_proxy_role.yaml - auth_proxy_role_binding.yaml diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml index eaa79158fb..85093a8c24 100644 --- a/config/rbac/leader_election_role.yaml +++ b/config/rbac/leader_election_role.yaml @@ -24,9 +24,3 @@ rules: - get - update - patch -- apiGroups: - - "" - resources: - - events - verbs: - - create diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index e68b0a20e3..eb15bd98f8 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -9,6 +9,7 @@ rules: - apiGroups: - "" resources: + - events - secrets verbs: - create @@ -21,6 +22,7 @@ rules: - cluster.x-k8s.io resources: - clusters + - clusters/status verbs: - get - list @@ -30,6 +32,7 @@ rules: resources: - clusters - machines + - machines/status verbs: - get - list diff --git a/config/samples/infrastructure_v1alpha2_openstackcluster.yaml b/config/samples/infrastructure_v1alpha2_openstackcluster.yaml deleted file mode 100644 index 602a7e5739..0000000000 --- a/config/samples/infrastructure_v1alpha2_openstackcluster.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 -kind: OpenStackCluster -metadata: - name: openstackcluster-sample -spec: - # Add fields here - foo: bar diff --git a/config/samples/infrastructure_v1alpha2_openstackmachine.yaml b/config/samples/infrastructure_v1alpha2_openstackmachine.yaml deleted file mode 100644 index 617f7a40eb..0000000000 --- a/config/samples/infrastructure_v1alpha2_openstackmachine.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 -kind: OpenStackMachine -metadata: - name: openstackmachine-sample -spec: - # Add fields here - foo: bar diff --git a/config/webhook/kustomization.yaml b/config/webhook/kustomization.yaml deleted file mode 100644 index 9cf26134e4..0000000000 --- a/config/webhook/kustomization.yaml +++ /dev/null @@ -1,6 +0,0 @@ -resources: -- manifests.yaml -- service.yaml - -configurations: -- kustomizeconfig.yaml diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml deleted file mode 100644 index 25e21e3c96..0000000000 --- a/config/webhook/kustomizeconfig.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# the following config is for teaching kustomize where to look at when substituting vars. -# It requires kustomize v2.1.0 or newer to work properly. -nameReference: -- kind: Service - version: v1 - fieldSpecs: - - kind: MutatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/name - - kind: ValidatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/name - -namespace: -- kind: MutatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/namespace - create: true -- kind: ValidatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/namespace - create: true - -varReference: -- path: metadata/annotations diff --git a/config/webhook/service.yaml b/config/webhook/service.yaml deleted file mode 100644 index b4861025ab..0000000000 --- a/config/webhook/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ - -apiVersion: v1 -kind: Service -metadata: - name: webhook-service - namespace: system -spec: - ports: - - port: 443 - targetPort: 443 - selector: - control-plane: controller-manager diff --git a/controllers/openstackcluster_controller.go b/controllers/openstackcluster_controller.go index 1bc68828c8..dd275fe489 100644 --- a/controllers/openstackcluster_controller.go +++ b/controllers/openstackcluster_controller.go @@ -18,20 +18,21 @@ package controllers import ( "context" "fmt" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/controller" + "github.com/go-logr/logr" "github.com/pkg/errors" apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/klog" infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha2" "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/loadbalancer" "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking" "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/provider" - "sigs.k8s.io/cluster-api/api/v1alpha2" + clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha2" "sigs.k8s.io/cluster-api/util" "sigs.k8s.io/cluster-api/util/patch" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) @@ -42,16 +43,17 @@ const ( // OpenStackClusterReconciler reconciles a OpenStackCluster object type OpenStackClusterReconciler struct { client.Client - Log logr.Logger + Log logr.Logger + Recorder record.EventRecorder } // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=openstackclusters,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=openstackclusters/status,verbs=get;update;patch -// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters,verbs=get;list;watch +// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters;clusters/status,verbs=get;list;watch func (r *OpenStackClusterReconciler) Reconcile(request ctrl.Request) (_ ctrl.Result, reterr error) { ctx := context.TODO() - logger := log.Log.WithName(clusterControllerName). + logger := r.Log.WithName(clusterControllerName). WithName(fmt.Sprintf("namespace=%s", request.Namespace)). WithName(fmt.Sprintf("openStackCluster=%s", request.Name)) @@ -100,8 +102,8 @@ func (r *OpenStackClusterReconciler) Reconcile(request ctrl.Request) (_ ctrl.Res return r.reconcileCluster(logger, cluster, openStackCluster) } -func (r *OpenStackClusterReconciler) reconcileCluster(logger logr.Logger, cluster *v1alpha2.Cluster, openStackCluster *infrav1.OpenStackCluster) (_ ctrl.Result, reterr error) { - klog.Infof("Reconciling Cluster %s/%s", cluster.Namespace, cluster.Name) +func (r *OpenStackClusterReconciler) reconcileCluster(logger logr.Logger, cluster *clusterv1.Cluster, openStackCluster *infrav1.OpenStackCluster) (_ ctrl.Result, reterr error) { + logger.Info("Reconciling Cluster") clusterName := fmt.Sprintf("%s-%s", cluster.Namespace, cluster.Name) @@ -115,19 +117,19 @@ func (r *OpenStackClusterReconciler) reconcileCluster(logger logr.Logger, cluste return reconcile.Result{}, err } - networkingService, err := networking.NewService(osProviderClient, clientOpts) + networkingService, err := networking.NewService(osProviderClient, clientOpts, logger) if err != nil { return reconcile.Result{}, err } - loadbalancerService, err := loadbalancer.NewService(osProviderClient, clientOpts, openStackCluster.Spec.UseOctavia) + loadbalancerService, err := loadbalancer.NewService(osProviderClient, clientOpts, logger, openStackCluster.Spec.UseOctavia) if err != nil { return reconcile.Result{}, err } - klog.Infof("Reconciling network components for cluster %s", clusterName) + logger.Info("Reconciling network components") if openStackCluster.Spec.NodeCIDR == "" { - klog.V(4).Infof("No need to reconcile network for cluster %s", clusterName) + logger.V(4).Info("No need to reconcile network") } else { err := networkingService.ReconcileNetwork(clusterName, openStackCluster) if err != nil { @@ -175,32 +177,32 @@ func (r *OpenStackClusterReconciler) reconcileCluster(logger logr.Logger, cluste }, } } else { - klog.Info("No control plane node found yet, could not write OpenStackCluster.Status.APIEndpoints") + logger.Info("No control plane node found yet, could not write OpenStackCluster.Status.APIEndpoints") } } // No errors, so mark us ready so the Cluster API Cluster Controller can pull it openStackCluster.Status.Ready = true - klog.Infof("Reconciled Cluster create %s/%s successfully", cluster.Namespace, cluster.Name) + logger.Info("Reconciled Cluster create successfully") return reconcile.Result{}, nil } -func (r *OpenStackClusterReconciler) reconcileClusterDelete(logger logr.Logger, cluster *v1alpha2.Cluster, openStackCluster *infrav1.OpenStackCluster) (ctrl.Result, error) { +func (r *OpenStackClusterReconciler) reconcileClusterDelete(logger logr.Logger, cluster *clusterv1.Cluster, openStackCluster *infrav1.OpenStackCluster) (ctrl.Result, error) { - klog.Infof("Reconcile Cluster delete %s/%s", cluster.Namespace, cluster.Name) + logger.Info("Reconcile Cluster delete") clusterName := fmt.Sprintf("%s-%s", cluster.Namespace, cluster.Name) osProviderClient, clientOpts, err := provider.NewClientFromCluster(r.Client, openStackCluster) if err != nil { return reconcile.Result{}, err } - networkingService, err := networking.NewService(osProviderClient, clientOpts) + networkingService, err := networking.NewService(osProviderClient, clientOpts, logger) if err != nil { return reconcile.Result{}, err } - loadbalancerService, err := loadbalancer.NewService(osProviderClient, clientOpts, openStackCluster.Spec.UseOctavia) + loadbalancerService, err := loadbalancer.NewService(osProviderClient, clientOpts, logger, openStackCluster.Spec.UseOctavia) if err != nil { return reconcile.Result{}, err } @@ -214,7 +216,7 @@ func (r *OpenStackClusterReconciler) reconcileClusterDelete(logger logr.Logger, // Delete other things if openStackCluster.Status.GlobalSecurityGroup != nil { - klog.Infof("Deleting global security group %q", openStackCluster.Status.GlobalSecurityGroup.Name) + logger.Info("Deleting global security group", "name", openStackCluster.Status.GlobalSecurityGroup.Name) err := networkingService.DeleteSecurityGroups(openStackCluster.Status.GlobalSecurityGroup) if err != nil { return reconcile.Result{}, errors.Errorf("failed to delete security group: %v", err) @@ -222,7 +224,7 @@ func (r *OpenStackClusterReconciler) reconcileClusterDelete(logger logr.Logger, } if openStackCluster.Status.ControlPlaneSecurityGroup != nil { - klog.Infof("Deleting control plane security group %q", openStackCluster.Status.ControlPlaneSecurityGroup.Name) + logger.Info("Deleting control plane security group", "name", openStackCluster.Status.ControlPlaneSecurityGroup.Name) err := networkingService.DeleteSecurityGroups(openStackCluster.Status.ControlPlaneSecurityGroup) if err != nil { return reconcile.Result{}, errors.Errorf("failed to delete security group: %v", err) @@ -231,20 +233,21 @@ func (r *OpenStackClusterReconciler) reconcileClusterDelete(logger logr.Logger, // TODO(sbueringer) Delete network/subnet/router/... if created by CAPO - klog.Infof("Reconciled Cluster delete %s/%s successfully", cluster.Namespace, cluster.Name) + logger.Info("Reconciled Cluster delete successfully") // Cluster is deleted so remove the finalizer. openStackCluster.Finalizers = util.Filter(openStackCluster.Finalizers, infrav1.ClusterFinalizer) return reconcile.Result{}, nil } -func (r *OpenStackClusterReconciler) SetupWithManager(mgr ctrl.Manager) error { +func (r *OpenStackClusterReconciler) SetupWithManager(mgr ctrl.Manager, options controller.Options) error { return ctrl.NewControllerManagedBy(mgr). + WithOptions(options). For(&infrav1.OpenStackCluster{}). Complete(r) } func (r *OpenStackClusterReconciler) getControlPlaneMachine() (*infrav1.OpenStackMachine, error) { - machines := &v1alpha2.MachineList{} + machines := &clusterv1.MachineList{} if err := r.Client.List(context.Background(), machines); err != nil { return nil, err } @@ -253,7 +256,7 @@ func (r *OpenStackClusterReconciler) getControlPlaneMachine() (*infrav1.OpenStac return nil, err } - var controlPlaneMachine *v1alpha2.Machine + var controlPlaneMachine *clusterv1.Machine for _, machine := range machines.Items { if util.IsControlPlaneMachine(&machine) { controlPlaneMachine = &machine diff --git a/controllers/openstackmachine_controller.go b/controllers/openstackmachine_controller.go index 145712f81e..cfdc9cca37 100644 --- a/controllers/openstackmachine_controller.go +++ b/controllers/openstackmachine_controller.go @@ -19,15 +19,19 @@ import ( "context" "encoding/json" "fmt" + "k8s.io/client-go/tools/record" + "net" + "os" + "sigs.k8s.io/controller-runtime/pkg/controller" + "strconv" + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/utils/openstack/clientconfig" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" - "k8s.io/klog" "k8s.io/utils/pointer" - "net" - "os" "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/compute" "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/loadbalancer" "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking" @@ -35,11 +39,8 @@ import ( clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha2" "sigs.k8s.io/cluster-api/util/patch" "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - "strconv" - "time" "github.com/go-logr/logr" ctrl "sigs.k8s.io/controller-runtime" @@ -62,17 +63,18 @@ const ( // OpenStackMachineReconciler reconciles a OpenStackMachine object type OpenStackMachineReconciler struct { client.Client - Log logr.Logger + Log logr.Logger + Recorder record.EventRecorder } // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=openstackmachines,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=openstackmachines/status,verbs=get;update;patch -// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters;machines,verbs=get;list;watch -// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch +// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters;machines;machines/status,verbs=get;list;watch +// +kubebuilder:rbac:groups="",resources=events;secrets,verbs=get;list;watch;create;update;patch func (r *OpenStackMachineReconciler) Reconcile(request ctrl.Request) (_ ctrl.Result, reterr error) { ctx := context.TODO() - logger := log.Log. + logger := r.Log. WithName(machineControllerName). WithName(fmt.Sprintf("namespace=%s", request.Namespace)). WithName(fmt.Sprintf("openStackMachine=%s", request.Name)) @@ -168,7 +170,7 @@ func (r *OpenStackMachineReconciler) reconcileMachine(logger logr.Logger, machin return reconcile.Result{RequeueAfter: 10 * time.Second}, nil } - klog.Infof("Creating Machine %s/%s: %s", cluster.Namespace, cluster.Name, machine.Name) + logger.Info("Creating Machine") clusterName := fmt.Sprintf("%s-%s", cluster.ObjectMeta.Namespace, cluster.Name) @@ -177,25 +179,25 @@ func (r *OpenStackMachineReconciler) reconcileMachine(logger logr.Logger, machin return reconcile.Result{}, err } - computeService, err := compute.NewService(osProviderClient, clientOpts) + computeService, err := compute.NewService(osProviderClient, clientOpts, logger) if err != nil { return reconcile.Result{}, err } - networkingService, err := networking.NewService(osProviderClient, clientOpts) + networkingService, err := networking.NewService(osProviderClient, clientOpts, logger) if err != nil { return reconcile.Result{}, err } instance, err := r.getOrCreate(computeService, machine, openStackMachine, cluster, openStackCluster) if err != nil { - handleMachineError(openStackMachine, capierrors.UpdateMachineError, errors.Errorf("OpenStack instance cannot be created: %v", err)) + handleMachineError(logger, openStackMachine, capierrors.UpdateMachineError, errors.Errorf("OpenStack instance cannot be created: %v", err)) return reconcile.Result{}, err } // Set an error message if we couldn't find the instance. if instance == nil { - handleMachineError(openStackMachine, capierrors.UpdateMachineError, errors.New("OpenStack instance cannot be found")) + handleMachineError(logger, openStackMachine, capierrors.UpdateMachineError, errors.New("OpenStack instance cannot be found")) return reconcile.Result{}, nil } @@ -218,33 +220,33 @@ func (r *OpenStackMachineReconciler) reconcileMachine(logger logr.Logger, machin case infrav1.InstanceStateBuilding: logger.Info("Machine instance is BUILDING", "instance-id", instance.ID) default: - handleMachineError(openStackMachine, capierrors.UpdateMachineError, errors.Errorf("OpenStack instance state %q is unexpected", instance.State)) + handleMachineError(logger, openStackMachine, capierrors.UpdateMachineError, errors.Errorf("OpenStack instance state %q is unexpected", instance.State)) return reconcile.Result{}, nil } if openStackMachine.Spec.FloatingIP != "" { err = r.reconcileFloatingIP(computeService, networkingService, instance, openStackMachine, openStackCluster) if err != nil { - handleMachineError(openStackMachine, capierrors.UpdateMachineError, errors.Errorf("FloatingIP cannot be reconciled: %v", err)) + handleMachineError(logger, openStackMachine, capierrors.UpdateMachineError, errors.Errorf("FloatingIP cannot be reconciled: %v", err)) return reconcile.Result{}, nil } } if openStackCluster.Spec.ManagedAPIServerLoadBalancer { - err = r.reconcileLoadBalancerMember(osProviderClient, clientOpts, instance, clusterName, machine, openStackMachine, openStackCluster) + err = r.reconcileLoadBalancerMember(logger, osProviderClient, clientOpts, instance, clusterName, machine, openStackMachine, openStackCluster) if err != nil { - handleMachineError(openStackMachine, capierrors.UpdateMachineError, errors.Errorf("LoadBalancerMember cannot be reconciled: %v", err)) + handleMachineError(logger, openStackMachine, capierrors.UpdateMachineError, errors.Errorf("LoadBalancerMember cannot be reconciled: %v", err)) return reconcile.Result{}, nil } } - klog.Infof("Reconciled Machine create %s/%s: %s successfully", cluster.Namespace, cluster.Name, machine.Name) + logger.Info("Reconciled Machine create successfully") return reconcile.Result{}, nil } func (r *OpenStackMachineReconciler) reconcileMachineDelete(logger logr.Logger, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine, cluster *clusterv1.Cluster, openStackCluster *infrav1.OpenStackCluster) (ctrl.Result, error) { - klog.Infof("Deleting Machine %s/%s: %s", cluster.Namespace, cluster.Name, machine.Name) + logger.Info("Deleting Machine") clusterName := fmt.Sprintf("%s-%s", cluster.ObjectMeta.Namespace, cluster.Name) @@ -253,12 +255,12 @@ func (r *OpenStackMachineReconciler) reconcileMachineDelete(logger logr.Logger, return reconcile.Result{}, err } - computeService, err := compute.NewService(osProviderClient, clientOpts) + computeService, err := compute.NewService(osProviderClient, clientOpts, logger) if err != nil { return reconcile.Result{}, err } - loadbalancerService, err := loadbalancer.NewService(osProviderClient, clientOpts, openStackCluster.Spec.UseOctavia) + loadbalancerService, err := loadbalancer.NewService(osProviderClient, clientOpts, logger, openStackCluster.Spec.UseOctavia) if err != nil { return reconcile.Result{}, err } @@ -275,7 +277,7 @@ func (r *OpenStackMachineReconciler) reconcileMachineDelete(logger logr.Logger, } if instance == nil { - klog.Infof("Skipped deleting %s that is already deleted.\n", machine.Name) + logger.Info("Skipped deleting machine that is already deleted") openStackMachine.Finalizers = util.Filter(openStackMachine.Finalizers, infrav1.MachineFinalizer) return reconcile.Result{}, nil } @@ -283,11 +285,11 @@ func (r *OpenStackMachineReconciler) reconcileMachineDelete(logger logr.Logger, // TODO(sbueringer) wait for instance deleted err = computeService.InstanceDelete(machine) if err != nil { - handleMachineError(openStackMachine, capierrors.UpdateMachineError, errors.Errorf("error deleting Openstack instance: %v", err)) + handleMachineError(logger, openStackMachine, capierrors.UpdateMachineError, errors.Errorf("error deleting Openstack instance: %v", err)) return reconcile.Result{}, nil } - klog.Infof("Reconciled Machine delete %s/%s: %s successfully", cluster.Namespace, cluster.Name, machine.Name) + logger.Info("Reconciled Machine delete successfully") // Instance is deleted so remove the finalizer. openStackMachine.Finalizers = util.Filter(openStackMachine.Finalizers, infrav1.MachineFinalizer) return reconcile.Result{}, nil @@ -324,11 +326,11 @@ func (r *OpenStackMachineReconciler) getOrCreate(computeService *compute.Service return instance, nil } -func handleMachineError(openstackMachine *infrav1.OpenStackMachine, reason capierrors.MachineStatusError, message error) { +func handleMachineError(logger logr.Logger, openstackMachine *infrav1.OpenStackMachine, reason capierrors.MachineStatusError, message error) { openstackMachine.Status.ErrorReason = &reason openstackMachine.Status.ErrorMessage = pointer.StringPtr(message.Error()) // TODO remove if this error is logged redundantly - klog.Errorf("Machine error %s: %v", openstackMachine.Name, message.Error()) + logger.Error(fmt.Errorf(string(reason)), message.Error()) } func getTimeout(name string, timeout int) time.Duration { @@ -341,8 +343,9 @@ func getTimeout(name string, timeout int) time.Duration { return time.Duration(timeout) } -func (r *OpenStackMachineReconciler) SetupWithManager(mgr ctrl.Manager) error { +func (r *OpenStackMachineReconciler) SetupWithManager(mgr ctrl.Manager, options controller.Options) error { return ctrl.NewControllerManagedBy(mgr). + WithOptions(options). For(&infrav1.OpenStackMachine{}).Watches( &source.Kind{Type: &clusterv1.Machine{}}, &handler.EnqueueRequestsFromMapFunc{ @@ -359,24 +362,24 @@ func (r *OpenStackMachineReconciler) SetupWithManager(mgr ctrl.Manager) error { } func (r *OpenStackMachineReconciler) reconcileFloatingIP(computeService *compute.Service, networkingService *networking.Service, instance *compute.Instance, openStackMachine *infrav1.OpenStackMachine, openStackCluster *infrav1.OpenStackCluster) error { - err := networkingService.GetOrCreateFloatingIP(openStackCluster, openStackMachine.Spec.FloatingIP) + err := networkingService.CreateFloatingIPIfNecessary(openStackCluster, openStackMachine.Spec.FloatingIP) if err != nil { return fmt.Errorf("error creating floatingIP: %v", err) } err = computeService.AssociateFloatingIP(instance.ID, openStackMachine.Spec.FloatingIP) if err != nil { - return fmt.Errorf("error associationg floatingIP: %v", err) + return fmt.Errorf("error associating floatingIP: %v", err) } return nil } -func (r *OpenStackMachineReconciler) reconcileLoadBalancerMember(osProviderClient *gophercloud.ProviderClient, clientOpts *clientconfig.ClientOpts, instance *compute.Instance, clusterName string, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine, openStackCluster *infrav1.OpenStackCluster) error { +func (r *OpenStackMachineReconciler) reconcileLoadBalancerMember(logger logr.Logger, osProviderClient *gophercloud.ProviderClient, clientOpts *clientconfig.ClientOpts, instance *compute.Instance, clusterName string, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine, openStackCluster *infrav1.OpenStackCluster) error { ip, err := getIPFromInstance(instance) if err != nil { return err } - loadbalancerService, err := loadbalancer.NewService(osProviderClient, clientOpts, openStackCluster.Spec.UseOctavia) + loadbalancerService, err := loadbalancer.NewService(osProviderClient, clientOpts, logger, openStackCluster.Spec.UseOctavia) if err != nil { return err } @@ -432,7 +435,7 @@ func getIPFromInstance(instance *compute.Instance) (string, error) { // OpenStackClusterToOpenStackMachine is a handler.ToRequestsFunc to be used to enqeue requests for reconciliation // of OpenStackMachines. func (r *OpenStackMachineReconciler) OpenStackClusterToOpenStackMachines(o handler.MapObject) []ctrl.Request { - var result []ctrl.Request + result := []ctrl.Request{} c, ok := o.Object.(*infrav1.OpenStackCluster) if !ok { diff --git a/examples/cluster/cluster.yaml b/examples/cluster/cluster.yaml index 25975655a4..f68056a557 100644 --- a/examples/cluster/cluster.yaml +++ b/examples/cluster/cluster.yaml @@ -17,8 +17,8 @@ spec: # * Enable the apiServerPort property #apiServerPort: 6443 infrastructureRef: - kind: OpenStackCluster apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 + kind: OpenStackCluster name: ${CLUSTER_NAME} namespace: ${CLUSTER_NAME} --- diff --git a/examples/controlplane/controlplane.yaml b/examples/controlplane/controlplane.yaml index 35303d3949..c6b7ac7119 100644 --- a/examples/controlplane/controlplane.yaml +++ b/examples/controlplane/controlplane.yaml @@ -31,7 +31,7 @@ metadata: spec: flavor: m1.medium image: - keyName: cluster-api-provider-openstack + sshKeyName: cluster-api-provider-openstack availabilityZone: nova networks: - filter: @@ -147,7 +147,7 @@ metadata: spec: flavor: m1.medium image: - keyName: cluster-api-provider-openstack + sshKeyName: cluster-api-provider-openstack availabilityZone: nova networks: - filter: @@ -229,7 +229,7 @@ metadata: spec: flavor: m1.medium image: - keyName: cluster-api-provider-openstack + sshKeyName: cluster-api-provider-openstack availabilityZone: nova networks: - filter: diff --git a/examples/generate.sh b/examples/generate.sh index 2af3977937..7f26822ee6 100755 --- a/examples/generate.sh +++ b/examples/generate.sh @@ -93,8 +93,8 @@ if [[ ${OVERWRITE} -ne 1 ]] && [[ -d "$OUTPUT_DIR" ]]; then exit 1 fi -yq_type=$(file $(which yq)) -if [[ $yq_type == *"Python script"* ]]; then +yq_type=$(file "$(which yq)") +if [[ ${yq_type} == *"Python script"* ]]; then echo "Wrong version of 'yq' installed, please install the one from https://github.com/mikefarah/yq" echo "" exit 1 @@ -113,9 +113,10 @@ PROVIDER_COMPONENTS_GENERATED_FILE=${OUTPUT_DIR}/provider-components.yaml CLUSTER_GENERATED_FILE=${OUTPUT_DIR}/cluster.yaml CONTROLPLANE_GENERATED_FILE=${OUTPUT_DIR}/controlplane.yaml MACHINEDEPLOYMENT_GENERATED_FILE=${OUTPUT_DIR}/machinedeployment.yaml -WORKER_GENERATED_FILE=${OUTPUT_DIR}/worker.yaml MACHINES_GENERATED_FILE=${OUTPUT_DIR}/machines.yaml +rm -rf "${OUTPUT_DIR}" +rm -rf "${CLOUDS_SECRETS_CONFIG_DIR}" mkdir -p "${OUTPUT_DIR}" mkdir -p "${CLOUDS_SECRETS_CONFIG_DIR}" @@ -125,7 +126,8 @@ if [[ ! -f ${MACHINE_CONTROLLER_SSH_PRIVATE_FILE} ]]; then # This is needed because GetKubeConfig assumes the key in the home .ssh dir. ssh-keygen -t rsa -f ${MACHINE_CONTROLLER_SSH_PRIVATE_FILE} -N "" fi -export MACHINE_CONTROLLER_SSH_PUBLIC_FILE_CONTENT=$(cat ${MACHINE_CONTROLLER_SSH_PRIVATE_FILE}.pub) +export MACHINE_CONTROLLER_SSH_PUBLIC_FILE_CONTENT +MACHINE_CONTROLLER_SSH_PUBLIC_FILE_CONTENT=$(cat ${MACHINE_CONTROLLER_SSH_PRIVATE_FILE}.pub) CLOUDS_PATH=${CLOUDS_PATH:-""} OPENSTACK_CLOUD_CONFIG_PLAIN=$(cat "$CLOUDS_PATH") @@ -144,32 +146,34 @@ CACERT_ORIGINAL=$(echo "$OPENSTACK_CLOUD_CONFIG_PLAIN" | yq r - clouds.${CLOUD}. # Basic cloud.conf, no LB configuration as that data is not known yet. export OPENSTACK_CLOUD_PROVIDER_CONF="[Global] - auth-url=$AUTH_URL - username=\"$USERNAME\" - password=\"$PASSWORD\" - tenant-id=\"$PROJECT_ID\" - domain-name=\"$DOMAIN_NAME\" + auth-url=$AUTH_URL + username=\"$USERNAME\" + password=\"$PASSWORD\" + tenant-id=\"$PROJECT_ID\" + domain-name=\"$DOMAIN_NAME\" " if [[ "$CACERT_ORIGINAL" != "null" ]]; then OPENSTACK_CLOUD_PROVIDER_CONF="$OPENSTACK_CLOUD_PROVIDER_CONF - ca-file=\"${CACERT_ORIGINAL}\" + ca-file=\"${CACERT_ORIGINAL}\" " fi if [[ "$REGION" != "null" ]]; then OPENSTACK_CLOUD_PROVIDER_CONF="$OPENSTACK_CLOUD_PROVIDER_CONF - region=\"${REGION}\" + region=\"${REGION}\" " fi OS=$(uname) if [[ "$OS" =~ "Linux" ]]; then # export OPENSTACK_CLOUD_PROVIDER_CONF=$(echo "$OPENSTACK_CLOUD_PROVIDER_CONF_PLAIN"|base64 -w0) if [[ "$CACERT_ORIGINAL" != "null" ]]; then - export OPENSTACK_CLOUD_CACERT_CONFIG=$(cat "$CACERT_ORIGINAL"|base64 -w0) + export OPENSTACK_CLOUD_CACERT_CONFIG + OPENSTACK_CLOUD_CACERT_CONFIG=$(cat "$CACERT_ORIGINAL"|base64 -w0) fi elif [[ "$OS" =~ "Darwin" ]]; then # export OPENSTACK_CLOUD_PROVIDER_CONF=$(echo "$OPENSTACK_CLOUD_PROVIDER_CONF_PLAIN"|base64) if [[ "$CACERT_ORIGINAL" != "null" ]]; then - export OPENSTACK_CLOUD_CACERT_CONFIG=$(cat "$CACERT_ORIGINAL"|base64) + export OPENSTACK_CLOUD_CACERT_CONFIG + OPENSTACK_CLOUD_CACERT_CONFIG=$(cat "$CACERT_ORIGINAL"|base64) fi else echo "Unrecognized OS : $OS" @@ -191,19 +195,16 @@ echo "Generated ${CLUSTER_GENERATED_FILE}" kustomize build "${SOURCE_DIR}/controlplane" --reorder=none | envsubst > "${CONTROLPLANE_GENERATED_FILE}" echo "Generated ${CONTROLPLANE_GENERATED_FILE}" -# Generate machinedeployment resources. (TODO(sbueringer) Have to implement OpenStackMachineTemplate first) -#kustomize build "${SOURCE_DIR}/machinedeployment" --reorder=none | envsubst >> "${MACHINEDEPLOYMENT_GENERATED_FILE}" -#echo "Generated ${MACHINEDEPLOYMENT_GENERATED_FILE}" - -# Generate machines resources. -kustomize build "${SOURCE_DIR}/machines" --reorder=none | envsubst > "${WORKER_GENERATED_FILE}" -echo "Generated ${WORKER_GENERATED_FILE}" +# Generate machinedeployment resources. +kustomize build "${SOURCE_DIR}/machinedeployment" --reorder=none | envsubst >> "${MACHINEDEPLOYMENT_GENERATED_FILE}" +echo "Generated ${MACHINEDEPLOYMENT_GENERATED_FILE}" +# combine control plane and regular machines in ${MACHINES_GENERATED_FILE} cat ${CONTROLPLANE_GENERATED_FILE} > ${MACHINES_GENERATED_FILE} echo "---" >> ${MACHINES_GENERATED_FILE} #cat ${MACHINEDEPLOYMENT_GENERATED_FILE} >> ${MACHINES_GENERATED_FILE} echo "---" >> ${MACHINES_GENERATED_FILE} -cat ${WORKER_GENERATED_FILE} >> ${MACHINES_GENERATED_FILE} +cat ${MACHINEDEPLOYMENT_GENERATED_FILE} >> ${MACHINES_GENERATED_FILE} echo "---" >> ${MACHINES_GENERATED_FILE} echo "Generated ${MACHINES_GENERATED_FILE}" @@ -222,6 +223,7 @@ echo "Generated ${COMPONENTS_OPENSTACK_GENERATED_FILE}" # Generate OpenStack Infrastructure Provider cloud-secrets file. kustomize build "${SOURCE_DIR}/clouds-secrets" --reorder=none | envsubst > "${COMPONENTS_OPENSTACK_CLOUDS_SECRETS_GENERATED_FILE}" echo "Generated ${COMPONENTS_OPENSTACK_CLOUDS_SECRETS_GENERATED_FILE}" +echo "WARNING: ${COMPONENTS_OPENSTACK_CLOUDS_SECRETS_GENERATED_FILE} includes OpenStack credentials" # Generate a single provider components file. kustomize build "${SOURCE_DIR}/provider-components"| envsubst > "${PROVIDER_COMPONENTS_GENERATED_FILE}" diff --git a/examples/machines/kustomization.yaml b/examples/machinedeployment/kustomization.yaml similarity index 84% rename from examples/machines/kustomization.yaml rename to examples/machinedeployment/kustomization.yaml index 3235178372..cc68c00a09 100644 --- a/examples/machines/kustomization.yaml +++ b/examples/machinedeployment/kustomization.yaml @@ -2,6 +2,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: ${CLUSTER_NAME} resources: -- machines.yaml +- machinedeployment.yaml configurations: - kustomizeconfig.yaml diff --git a/examples/machinedeployment/kustomizeconfig.yaml b/examples/machinedeployment/kustomizeconfig.yaml new file mode 100644 index 0000000000..eb6d8a9651 --- /dev/null +++ b/examples/machinedeployment/kustomizeconfig.yaml @@ -0,0 +1,11 @@ +namespace: +- kind: MachineDeployment + group: cluster.x-k8s.io + version: v1alpha2 + path: spec/template/spec/infrastructureRef/namespace + create: true +- kind: MachineDeployment + group: cluster.x-k8s.io + version: v1alpha2 + path: spec/template/spec/bootstrap/configRef/namespace + create: true diff --git a/examples/machinedeployment/machinedeployment.yaml b/examples/machinedeployment/machinedeployment.yaml new file mode 100644 index 0000000000..ea1099b326 --- /dev/null +++ b/examples/machinedeployment/machinedeployment.yaml @@ -0,0 +1,92 @@ +##################################################### +# ${CLUSTER_NAME}-md-0 +##################################################### +apiVersion: cluster.x-k8s.io/v1alpha2 +kind: MachineDeployment +metadata: + name: ${CLUSTER_NAME}-md-0 + namespace: ${CLUSTER_NAME} + labels: + cluster.x-k8s.io/cluster-name: ${CLUSTER_NAME} + nodepool: nodepool-0 +spec: + replicas: 1 + selector: + matchLabels: + cluster.x-k8s.io/cluster-name: ${CLUSTER_NAME} + nodepool: nodepool-0 + template: + metadata: + labels: + cluster.x-k8s.io/cluster-name: ${CLUSTER_NAME} + nodepool: nodepool-0 + spec: + bootstrap: + configRef: + apiVersion: bootstrap.cluster.x-k8s.io/v1alpha2 + kind: KubeadmConfigTemplate + name: ${CLUSTER_NAME}-md-0 + namespace: ${CLUSTER_NAME} + infrastructureRef: + apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 + kind: OpenStackMachineTemplate + name: ${CLUSTER_NAME}-md-0 + namespace: ${CLUSTER_NAME} + version: ${KUBERNETES_VERSION} +--- +apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 +kind: OpenStackMachineTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 + namespace: ${CLUSTER_NAME} +spec: + template: + spec: + availabilityZone: nova + cloudName: $CLOUD + cloudsSecret: + name: cloud-config + namespace: ${CLUSTER_NAME} + flavor: m1.medium + image: + networks: + - filter: + name: k8s-clusterapi-cluster-${CLUSTER_NAME}-${CLUSTER_NAME} + subnets: + - filter: + name: k8s-clusterapi-cluster-${CLUSTER_NAME}-${CLUSTER_NAME} + sshKeyName: cluster-api-provider-openstack +--- +apiVersion: bootstrap.cluster.x-k8s.io/v1alpha2 +kind: KubeadmConfigTemplate +metadata: + name: ${CLUSTER_NAME}-md-0 + namespace: ${CLUSTER_NAME} +spec: + template: + spec: + files: + - content: |- + # cloud.conf to communicate with OpenStack + $OPENSTACK_CLOUD_PROVIDER_CONF + owner: root + path: /etc/kubernetes/cloud.conf + permissions: "0600" + - content: |- + # cacert to communicate with OpenStack + $OPENSTACK_CLOUD_CACERT_CONFIG + owner: root + path: /etc/certs/cacert + permissions: "0600" + joinConfiguration: + nodeRegistration: + kubeletExtraArgs: + cloud-config: /etc/kubernetes/cloud.conf + cloud-provider: openstack + name: '{{ local_hostname }}' + ntp: + servers: [] + users: + - name: ubuntu + sshAuthorizedKeys: + - "$MACHINE_CONTROLLER_SSH_PUBLIC_FILE_CONTENT" diff --git a/examples/machines/kustomizeconfig.yaml b/examples/machines/kustomizeconfig.yaml deleted file mode 100644 index fa95d11679..0000000000 --- a/examples/machines/kustomizeconfig.yaml +++ /dev/null @@ -1,15 +0,0 @@ -namespace: -- kind: Machine - group: cluster.x-k8s.io - version: v1alpha2 - path: spec/infrastructureRef/namespace - create: true -- kind: Machine - group: cluster.x-k8s.io - version: v1alpha2 - path: spec/bootstrap/configRef/namespace - create: true - -commonLabels: -- path: metadata/labels - create: true diff --git a/examples/machines/machines.yaml b/examples/machines/machines.yaml deleted file mode 100644 index 1e9997535e..0000000000 --- a/examples/machines/machines.yaml +++ /dev/null @@ -1,75 +0,0 @@ -##################################################### -# ${CLUSTER_NAME}-node-01 -##################################################### -apiVersion: cluster.x-k8s.io/v1alpha2 -kind: Machine -metadata: - name: ${CLUSTER_NAME}-node-01 - namespace: ${CLUSTER_NAME} - labels: - cluster.x-k8s.io/cluster-name: ${CLUSTER_NAME} -spec: - version: ${KUBERNETES_VERSION} - bootstrap: - configRef: - apiVersion: bootstrap.cluster.x-k8s.io/v1alpha2 - kind: KubeadmConfig - name: ${CLUSTER_NAME}-node-01 - namespace: ${CLUSTER_NAME} - infrastructureRef: - apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 - kind: OpenStackMachine - name: ${CLUSTER_NAME}-node-01 - namespace: ${CLUSTER_NAME} ---- -apiVersion: infrastructure.cluster.x-k8s.io/v1alpha2 -kind: OpenStackMachine -metadata: - name: ${CLUSTER_NAME}-node-01 - namespace: ${CLUSTER_NAME} -spec: - flavor: m1.medium - image: - keyName: cluster-api-provider-openstack - availabilityZone: nova - networks: - - filter: - name: k8s-clusterapi-cluster-${CLUSTER_NAME}-${CLUSTER_NAME} - subnets: - - filter: - name: k8s-clusterapi-cluster-${CLUSTER_NAME}-${CLUSTER_NAME} - cloudName: $CLOUD - cloudsSecret: - name: cloud-config - namespace: ${CLUSTER_NAME} ---- -apiVersion: bootstrap.cluster.x-k8s.io/v1alpha2 -kind: KubeadmConfig -metadata: - name: ${CLUSTER_NAME}-node-01 -spec: - files: - - path: /etc/kubernetes/cloud.conf - owner: root - permissions: "0600" - content: |- - # cloud.conf to communicate with OpenStack - $OPENSTACK_CLOUD_PROVIDER_CONF - - path: /etc/certs/cacert - owner: root - permissions: "0600" - content: |- - # cacert to communicate with OpenStack - $OPENSTACK_CLOUD_CACERT_CONFIG - ntp: - servers: [] - users: - - name: ubuntu - sshAuthorizedKeys: - - "$MACHINE_CONTROLLER_SSH_PUBLIC_FILE_CONTENT" - joinConfiguration: - nodeRegistration: - name: '{{ local_hostname }}' - kubeletExtraArgs: - cloud-provider: openstack - cloud-config: /etc/kubernetes/cloud.conf diff --git a/examples/provider-components/manager_tolerations_patch.yaml b/examples/provider-components/manager_tolerations_patch.yaml index 56772217ae..4f4f5fcf19 100644 --- a/examples/provider-components/manager_tolerations_patch.yaml +++ b/examples/provider-components/manager_tolerations_patch.yaml @@ -12,12 +12,6 @@ spec: key: node-role.kubernetes.io/master - key: CriticalAddonsOnly operator: Exists - - effect: NoExecute - key: node.alpha.kubernetes.io/notReady - operator: Exists - - effect: NoExecute - key: node.alpha.kubernetes.io/unreachable - operator: Exists --- apiVersion: apps/v1 kind: Deployment @@ -32,18 +26,12 @@ spec: key: node-role.kubernetes.io/master - key: CriticalAddonsOnly operator: Exists - - effect: NoExecute - key: node.alpha.kubernetes.io/notReady - operator: Exists - - effect: NoExecute - key: node.alpha.kubernetes.io/unreachable - operator: Exists --- apiVersion: apps/v1 kind: Deployment metadata: - name: cluster-api-controller-manager - namespace: cluster-api-system + name: capi-controller-manager + namespace: capi-system spec: template: spec: @@ -52,9 +40,3 @@ spec: key: node-role.kubernetes.io/master - key: CriticalAddonsOnly operator: Exists - - effect: NoExecute - key: node.alpha.kubernetes.io/notReady - operator: Exists - - effect: NoExecute - key: node.alpha.kubernetes.io/unreachable - operator: Exists diff --git a/go.mod b/go.mod index 68d22e18c9..2a59d40201 100644 --- a/go.mod +++ b/go.mod @@ -13,14 +13,10 @@ require ( k8s.io/api v0.0.0-20190711103429-37c3b8b1ca65 k8s.io/apimachinery v0.0.0-20190711103026-7bf792636534 k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible - k8s.io/code-generator v0.0.0-20190311093542-50b561225d70 - k8s.io/gengo v0.0.0-20190813173942-955ffa8fcfc9 // indirect k8s.io/klog v0.4.0 k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5 sigs.k8s.io/cluster-api v0.0.0-20190826164421-9a520827870a sigs.k8s.io/controller-runtime v0.2.0 - sigs.k8s.io/controller-tools v0.2.0 - sigs.k8s.io/testing_frameworks v0.1.2-0.20190130140139-57f07443c2d4 ) replace ( diff --git a/go.sum b/go.sum index 592d135c14..99f21ae118 100644 --- a/go.sum +++ b/go.sum @@ -281,12 +281,8 @@ k8s.io/apimachinery v0.0.0-20190704094733-8f6ac2502e51/go.mod h1:ccL7Eh7zubPUSh9 k8s.io/apiserver v0.0.0-20190409021813-1ec86e4da56c/go.mod h1:6bqaTSOSJavUIXUtfaR9Os9JtTCm8ZqH2SUl2S60C4w= k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible h1:U5Bt+dab9K8qaUmXINrkXO135kA11/i5Kg1RUydgaMQ= k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= -k8s.io/code-generator v0.0.0-20190311093542-50b561225d70 h1:lgPp615xLHxN84RBd+viA/oHzJfI0miFYFH4T9wpPQ4= -k8s.io/code-generator v0.0.0-20190311093542-50b561225d70/go.mod h1:MYiN+ZJZ9HkETbgVZdWw2AsuAi9PZ4V80cwfuf2axe8= k8s.io/component-base v0.0.0-20190409021516-bd2732e5c3f7 h1:f+AySqWvoqyCD7aArN3EZ+g2boKIS52pcSo6zdZkc+4= k8s.io/component-base v0.0.0-20190409021516-bd2732e5c3f7/go.mod h1:DMaomcf3j3MM2j1FsvlLVVlc7wA2jPytEur3cP9zRxQ= -k8s.io/gengo v0.0.0-20190813173942-955ffa8fcfc9 h1:/gJp8cw8+k4AxBNGZ5u5eRoCOzH3WVpY02K654HNiyU= -k8s.io/gengo v0.0.0-20190813173942-955ffa8fcfc9/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.2.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.4.0 h1:lCJCxf/LIowc2IGS9TPjWDyXY4nOmdGdfcwwDQCOURQ= diff --git a/go.vendor b/go.vendor deleted file mode 100644 index 2686febc4f..0000000000 --- a/go.vendor +++ /dev/null @@ -1 +0,0 @@ -sigs.k8s.io/cluster-api diff --git a/hack/boilerplate/boilerplate.generatego.txt b/hack/boilerplate.go.txt similarity index 93% rename from hack/boilerplate/boilerplate.generatego.txt rename to hack/boilerplate.go.txt index 8a0dbd5501..c55ac26013 100644 --- a/hack/boilerplate/boilerplate.generatego.txt +++ b/hack/boilerplate.go.txt @@ -1,5 +1,5 @@ /* -Copyright 2018 The Kubernetes Authors. +Copyright 2019 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/hack/tools/go.mod b/hack/tools/go.mod new file mode 100644 index 0000000000..2eeadd4758 --- /dev/null +++ b/hack/tools/go.mod @@ -0,0 +1,24 @@ +module sigs.k8s.io/cluster-api/hack/tools + +go 1.12 + +require ( + github.com/golang/mock v1.3.1 + github.com/golangci/golangci-lint v1.17.1 + sigs.k8s.io/controller-tools v0.2.0 + sigs.k8s.io/testing_frameworks v0.1.1 +) + +replace ( + // TODO(vincepri) Remove the following replace directives, needed for golangci-lint until + // https://github.com/golangci/golangci-lint/issues/595 is fixed. + github.com/go-critic/go-critic v0.0.0-20181204210945-1df300866540 => github.com/go-critic/go-critic v0.0.0-20190526074819-1df300866540 + github.com/golangci/errcheck v0.0.0-20181003203344-ef45e06d44b6 => github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 + github.com/golangci/go-tools v0.0.0-20180109140146-af6baa5dc196 => github.com/golangci/go-tools v0.0.0-20190318060251-af6baa5dc196 + github.com/golangci/gofmt v0.0.0-20181105071733-0b8337e80d98 => github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98 + github.com/golangci/gosec v0.0.0-20180901114220-66fb7fc33547 => github.com/golangci/gosec v0.0.0-20190211064107-66fb7fc33547 + github.com/golangci/ineffassign v0.0.0-20180808204949-42439a7714cc => github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc + github.com/golangci/lint-1 v0.0.0-20180610141402-ee948d087217 => github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217 + github.com/timakin/bodyclose => github.com/golangci/bodyclose v0.0.0-20190714144026-65da19158fa2 + mvdan.cc/unparam v0.0.0-20190124213536-fbb59629db34 => mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34 +) diff --git a/hack/tools/go.sum b/hack/tools/go.sum new file mode 100644 index 0000000000..83070d4e19 --- /dev/null +++ b/hack/tools/go.sum @@ -0,0 +1,265 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OpenPeeDeeP/depguard v0.0.0-20180806142446-a69c782687b2 h1:HTOmFEEYrWi4MW5ZKUx6xfeyM10Sx3kQF65xiQJMPYA= +github.com/OpenPeeDeeP/depguard v0.0.0-20180806142446-a69c782687b2/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-critic/go-critic v0.0.0-20190526074819-1df300866540 h1:aTxtKFxZ1TqCCYkrQE6Si8qIm/2+58VSJWurTgEPVHE= +github.com/go-critic/go-critic v0.0.0-20190526074819-1df300866540/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA= +github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0TuRN0= +github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g= +github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= +github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8= +github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= +github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= +github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ= +github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= +github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg= +github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k= +github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= +github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= +github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk= +github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg= +github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= +github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks= +github.com/go-toolsmith/pkgload v1.0.0 h1:4DFWWMXVfbcN5So1sBNW9+yeiMqLFGl1wFLTL5R0Tgg= +github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= +github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4= +github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= +github.com/go-toolsmith/typep v1.0.0 h1:zKymWyA1TRYvqYrYDrfEMZULyrhcnGY3x7LDKU2XQaA= +github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= +github.com/gobuffalo/flect v0.1.5 h1:xpKq9ap8MbYfhuPCF0dBH854Gp9CxZjr/IocxELFflo= +github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golangci/bodyclose v0.0.0-20190714144026-65da19158fa2 h1:nh/PMhIaxu+h8NOuhOwT2el9Ed08166oitASyNYqQzs= +github.com/golangci/bodyclose v0.0.0-20190714144026-65da19158fa2/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= +github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= +github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= +github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= +github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= +github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 h1:YYWNAGTKWhKpcLLt7aSj/odlKrSrelQwlovBpDuf19w= +github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0= +github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw= +github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= +github.com/golangci/go-tools v0.0.0-20190318060251-af6baa5dc196 h1:Y8tnIoL0ZQEnVn+SedetVzw1JRsGvjnOemI+oTFCpow= +github.com/golangci/go-tools v0.0.0-20190318060251-af6baa5dc196/go.mod h1:unzUULGw35sjyOYjUt0jMTXqHlZPpPc6e+xfO4cd6mM= +github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3 h1:pe9JHs3cHHDQgOFXJJdYkK6fLz2PWyYtP4hthoCMvs8= +github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o= +github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee h1:J2XAy40+7yz70uaOiMbNnluTg7gyQhtGqLQncQh+4J8= +github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= +github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98 h1:0OkFarm1Zy2CjCiDKfK9XHgmc2wbDlRMD2hD8anAJHU= +github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= +github.com/golangci/golangci-lint v1.17.1 h1:lc8Hf9GPCjIr0hg3S/xhvFT1+Hydass8F1xchr8jkME= +github.com/golangci/golangci-lint v1.17.1/go.mod h1:+5sJSl2h3aly+fpmL2meSP8CaSKua2E4Twi9LPy7b1g= +github.com/golangci/gosec v0.0.0-20190211064107-66fb7fc33547 h1:fUdgm/BdKvwOHxg5AhNbkNRp2mSy8sxTXyBVs/laQHo= +github.com/golangci/gosec v0.0.0-20190211064107-66fb7fc33547/go.mod h1:0qUabqiIQgfmlAmulqxyiGkkyF6/tOGSnY2cnPVwrzU= +github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc h1:gLLhTLMk2/SutryVJ6D4VZCU3CUqr8YloG7FPIBWFpI= +github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU= +github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217 h1:En/tZdwhAn0JNwLuXzP3k2RVtMqMmOEK7Yu/g3tmtJE= +github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= +github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= +github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= +github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770 h1:EL/O5HGrF7Jaq0yNhBLucz9hTuRzj2LdwGBOaENgxIk= +github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= +github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 h1:leSNB7iYzLYSSx3J/s5sVf4Drkc68W2wm4Ixh/mr0us= +github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI= +github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0 h1:HVfrLniijszjS1aiNg8JbBMO2+E1WIQ+j/gL4SQqGPg= +github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= +github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= +github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:JVnpOZS+qxli+rgVl98ILOXVNbW+kb5wcxeGx8ShUIw= +github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= +github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno= +github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/magiconair/properties v1.7.6 h1:U+1DqNen04MdEPgFiIwdOUiqZ8qPa37xgogX/sd3+54= +github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= +github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238 h1:+MZW2uvHgN8kYvksEN3f7eFL2wpzk0GxmlFsMybWc7E= +github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= +github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= +github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663 h1:Ri1EhipkbhWsffPJ3IPlrb4SkTOPa2PfRXp3jchBczw= +github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pelletier/go-toml v1.1.0 h1:cmiOvKzEunMsAxyhXSzpL5Q1CRKpVv0KQsnAIcSEVYM= +github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e h1:MZM7FHLqUHYI0Y/mQAt3d2aYa0SiNms/hFqC9qJYolM= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041 h1:llrF3Fs4018ePo4+G/HV/uQUqEI1HMDjCeOf2V6puPc= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/sirupsen/logrus v1.0.5 h1:8c8b5uO0zS4X6RPl/sd1ENwSkIc0/H2PaHxE3udaE8I= +github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sourcegraph/go-diff v0.5.1 h1:gO6i5zugwzo1RVTvgvfwCOSVegNuvnNi6bAD1QCmkHs= +github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE= +github.com/spf13/afero v1.1.0 h1:bopulORc2JeYaxfHLvJa5NzxviA9PoWhpiiJkru7Ji4= +github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg= +github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= +github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec h1:2ZXvIUGghLpdTVHR1UfvfrzoVlZaE/yOWC5LueIHZig= +github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.0.2 h1:Ncr3ZIuJn322w2k1qmzXDnkLAdQMlJqBa9kfAH+irso= +github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= +github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09 h1:KaQtG+aDELoNmXYas3TVkGNYRuq8JQ1aa7LJt8EXVyo= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872 h1:cGjJzUd8RgBw428LXP65YXni0aiGNA4Bl+ls8SmLOm8= +golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190501045030-23463209683d h1:D7DVZUZEUgsSIDTivnUtVeGfN5AvhDIKtdIZAqx0ieE= +golang.org/x/tools v0.0.0-20190501045030-23463209683d/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd h1:7E3PabyysDSEjnaANKBgums/hyvMI/HoHQ50qZEzTrg= +golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b h1:aBGgKJUM9Hk/3AE8WaZIApnTxG35kbuQba2w+SXqezo= +k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= +k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8 h1:q1Qvjzs/iEdXF6A1a8H3AKVFDzJNcJn3nXMs6R6qFtA= +k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE= +k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d h1:Jmdtdt1ZnoGfWWIIik61Z7nKYgO3J+swQJtPYsP9wHA= +k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= +k8s.io/klog v0.2.0 h1:0ElL0OHzF3N+OhoJTL0uca20SxtYt4X4+bzHeqrB83c= +k8s.io/klog v0.2.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= +mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= +mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= +mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= +mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34 h1:duVSyluuJA+u0BnkcLR01smoLrGgDTfWt5c8ODYG8fU= +mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY= +sigs.k8s.io/controller-tools v0.2.0 h1:AmQ/0JKBJAjyAiPAkrAf9QW06jkx2lc5hpxMjamsFpw= +sigs.k8s.io/controller-tools v0.2.0/go.mod h1:8t/X+FVWvk6TaBcsa+UKUBbn7GMtvyBKX30SGl4em6Y= +sigs.k8s.io/testing_frameworks v0.1.1 h1:cP2l8fkA3O9vekpy5Ks8mmA0NW/F7yBdXf8brkWhVrs= +sigs.k8s.io/testing_frameworks v0.1.1/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 h1:JPJh2pk3+X4lXAkZIk2RuE/7/FoK9maXw+TNPJhVS/c= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/hack/tools.go b/hack/tools/tools.go similarity index 69% rename from hack/tools.go rename to hack/tools/tools.go index c8fb0d3280..aa84ddfa6a 100644 --- a/hack/tools.go +++ b/hack/tools/tools.go @@ -1,3 +1,5 @@ +// +build tools + /* Copyright 2019 The Kubernetes Authors. @@ -18,14 +20,8 @@ limitations under the License. package tools import ( - _ "k8s.io/code-generator/cmd/client-gen" - _ "k8s.io/code-generator/cmd/conversion-gen" - _ "k8s.io/code-generator/cmd/deepcopy-gen" - _ "k8s.io/code-generator/cmd/defaulter-gen" - _ "k8s.io/code-generator/cmd/informer-gen" - _ "k8s.io/code-generator/cmd/lister-gen" - _ "k8s.io/code-generator/cmd/register-gen" - _ "k8s.io/code-generator/cmd/set-gen" + _ "github.com/golang/mock/mockgen" + _ "github.com/golangci/golangci-lint/cmd/golangci-lint" _ "sigs.k8s.io/controller-tools/cmd/controller-gen" _ "sigs.k8s.io/testing_frameworks/integration" ) diff --git a/hack/verify-install.sh b/hack/verify-install.sh new file mode 100755 index 0000000000..5842e10fb9 --- /dev/null +++ b/hack/verify-install.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# Copyright 2018 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e +set -o errexit +set -o nounset +set -o pipefail + +GOPATH="$(go env GOPATH)" +WORKINGDIR="$(pwd)" +EXPECTEDDIR="${GOPATH}/src/sigs.k8s.io/cluster-api-provider-openstack" + +if [ "${WORKINGDIR}" != "${EXPECTEDDIR}" ]; then + echo "------------------------------------------------------------------------------------------------------------------" + echo "Invalid checkout directory!" + echo + echo "Expected: ${EXPECTEDDIR}" + echo "Actual: ${WORKINGDIR}" + echo "" + echo "This project assumes that the repository has been checked out to \$GOPATH/src/sigs.k8s.io/cluster-api-provider-openstack" + echo "------------------------------------------------------------------------------------------------------------------" + exit 1 +fi + +echo "Repository is set up correctly" + +exit 0 diff --git a/main.go b/main.go index 0d1a613806..da49167123 100644 --- a/main.go +++ b/main.go @@ -20,9 +20,12 @@ import ( "flag" "net/http" "os" + "time" + + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/record" "sigs.k8s.io/controller-runtime/pkg/client/config" - "time" _ "net/http/pprof" @@ -52,30 +55,75 @@ func init() { func main() { klog.InitFlags(nil) - var metricsAddr string - var enableLeaderElection bool + var ( + metricsAddr string + enableLeaderElection bool + watchNamespace string + profilerAddress string + openStackClusterConcurrency int + openStackMachineConcurrency int + syncPeriod time.Duration + ) + + flag.StringVar( + &metricsAddr, + "metrics-addr", + ":8080", + "The address the metric endpoint binds to.", + ) + + flag.BoolVar( + &enableLeaderElection, + "enable-leader-election", + false, + "Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.", + ) + + flag.StringVar( + &watchNamespace, + "namespace", + "", + "Namespace that the controller watches to reconcile cluster-api objects. If unspecified, the controller watches for cluster-api objects across all namespaces.", + ) + + flag.StringVar( + &profilerAddress, + "profiler-address", + "", + "Bind address to expose the pprof profiler (e.g. localhost:6060)", + ) + + flag.IntVar(&openStackClusterConcurrency, + "openstackcluster-concurrency", + 1, + "Number of OpenStackClusters to process simultaneously", + ) + + flag.IntVar(&openStackMachineConcurrency, + "openstackmachine-concurrency", + 1, + "Number of OpenStackMachines to process simultaneously", + ) + + flag.DurationVar(&syncPeriod, + "sync-period", + 10*time.Minute, + "The minimum interval at which watched resources are reconciled (e.g. 15m)", + ) - flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") - flag.BoolVar(&enableLeaderElection, "enable-leader-election", false, - "Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.") - watchNamespace := flag.String("namespace", "", - "Namespace that the controller watches to reconcile cluster-api objects. If unspecified, the controller watches for cluster-api objects across all namespaces.") - profilerAddress := flag.String("profiler-address", "", "Bind address to expose the pprof profiler (e.g. localhost:6060)") flag.Parse() - if *watchNamespace != "" { - setupLog.Info("Watching cluster-api objects only in namespace for reconciliation", "namespace", *watchNamespace) + if watchNamespace != "" { + setupLog.Info("Watching cluster-api objects only in namespace for reconciliation", "namespace", watchNamespace) } - if *profilerAddress != "" { - setupLog.Info("Profiler listening for requests", "profiler-address", *profilerAddress) + if profilerAddress != "" { + setupLog.Info("Profiler listening for requests", "profiler-address", profilerAddress) go func() { - setupLog.Error(http.ListenAndServe(*profilerAddress, nil), "listen and serve error") + setupLog.Error(http.ListenAndServe(profilerAddress, nil), "listen and serve error") }() } - syncPeriod := 10 * time.Minute - ctrl.SetLogger(klogr.New()) cfg, err := config.GetConfigWithContext(os.Getenv("KUBECONTEXT")) @@ -88,7 +136,7 @@ func main() { MetricsBindAddress: metricsAddr, LeaderElection: enableLeaderElection, SyncPeriod: &syncPeriod, - Namespace: *watchNamespace, + Namespace: watchNamespace, }) if err != nil { setupLog.Error(err, "unable to start manager") @@ -99,18 +147,18 @@ func main() { record.InitFromRecorder(mgr.GetEventRecorderFor("openstack-controller")) if err = (&controllers.OpenStackMachineReconciler{ - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("OpenStackMachine"), - //Recorder: mgr.GetEventRecorderFor("openstackmachine-controller"), - }).SetupWithManager(mgr); err != nil { + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("OpenStackMachine"), + Recorder: mgr.GetEventRecorderFor("openstackmachine-controller"), + }).SetupWithManager(mgr, controller.Options{MaxConcurrentReconciles: openStackMachineConcurrency}); err != nil { setupLog.Error(err, "unable to create controller", "controller", "OpenStackMachine") os.Exit(1) } if err = (&controllers.OpenStackClusterReconciler{ - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("OpenStackCluster"), - //Recorder: mgr.GetEventRecorderFor("openstackcluster-controller"), - }).SetupWithManager(mgr); err != nil { + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("OpenStackCluster"), + Recorder: mgr.GetEventRecorderFor("openstackcluster-controller"), + }).SetupWithManager(mgr, controller.Options{MaxConcurrentReconciles: openStackClusterConcurrency}); err != nil { setupLog.Error(err, "unable to create controller", "controller", "OpenStackCluster") os.Exit(1) } diff --git a/pkg/cloud/services/compute/instance.go b/pkg/cloud/services/compute/instance.go index 64d9a7d5cf..87853b3556 100644 --- a/pkg/cloud/services/compute/instance.go +++ b/pkg/cloud/services/compute/instance.go @@ -18,12 +18,12 @@ package compute import ( "fmt" - "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - "k8s.io/klog" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/record" + "time" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/services/networking" - "sigs.k8s.io/cluster-api/api/v1alpha2" + clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha2" "sigs.k8s.io/cluster-api/controllers/noderefutil" - "time" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" @@ -66,17 +66,16 @@ type ServerNetwork struct { } // InstanceCreate creates a compute instance -func (is *Service) InstanceCreate(clusterName string, machine *v1alpha2.Machine, openStackMachine *infrav1.OpenStackMachine, openStackCluster *infrav1.OpenStackCluster) (instance *Instance, err error) { - var createOpts servers.CreateOptsBuilder +func (s *Service) InstanceCreate(clusterName string, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine, openStackCluster *infrav1.OpenStackCluster) (instance *Instance, err error) { if openStackMachine == nil { return nil, fmt.Errorf("create Options need be specified to create instace") } - if openStackMachine.Spec.Trunk == true { - trunkSupport, err := getTrunkSupport(is) + if openStackMachine.Spec.Trunk { + trunkSupport, err := getTrunkSupport(s) if err != nil { return nil, fmt.Errorf("there was an issue verifying whether trunk support is available, please disable it: %v", err) } - if trunkSupport == false { + if !trunkSupport { return nil, fmt.Errorf("there is no trunk support. Please disable it") } } @@ -94,7 +93,7 @@ func (is *Service) InstanceCreate(clusterName string, machine *v1alpha2.Machine, machineTags = append(machineTags, openStackCluster.Spec.Tags...) // Get security groups - securityGroups, err := getSecurityGroups(is, openStackMachine.Spec.SecurityGroups) + securityGroups, err := getSecurityGroups(s, openStackMachine.Spec.SecurityGroups) if err != nil { return nil, err } @@ -103,7 +102,7 @@ func (is *Service) InstanceCreate(clusterName string, machine *v1alpha2.Machine, for _, net := range openStackMachine.Spec.Networks { opts := networks.ListOpts(net.Filter) opts.ID = net.UUID - ids, err := getNetworkIDsByFilter(is.networkClient, &opts) + ids, err := getNetworkIDsByFilter(s.networkClient, &opts) if err != nil { return nil, err } @@ -118,7 +117,7 @@ func (is *Service) InstanceCreate(clusterName string, machine *v1alpha2.Machine, subnetOpts := subnets.ListOpts(subnet.Filter) subnetOpts.ID = subnet.UUID subnetOpts.NetworkID = netID - subnetsByFilter, err := networking.GetSubnetsByFilter(is.networkClient, &subnetOpts) + subnetsByFilter, err := networking.GetSubnetsByFilter(s.networkClient, &subnetOpts) if err != nil { return nil, err } @@ -131,12 +130,12 @@ func (is *Service) InstanceCreate(clusterName string, machine *v1alpha2.Machine, } } } - var portsList []servers.Network + portsList := []servers.Network{} for _, net := range nets { if net.networkID == "" { return nil, fmt.Errorf("no network was found or provided. Please check your machine configuration and try again") } - allPages, err := ports.List(is.networkClient, ports.ListOpts{ + allPages, err := ports.List(s.networkClient, ports.ListOpts{ Name: openStackMachine.Name, NetworkID: net.networkID, }).AllPages() @@ -150,7 +149,7 @@ func (is *Service) InstanceCreate(clusterName string, machine *v1alpha2.Machine, var port ports.Port if len(portList) == 0 { // create server port - port, err = createPort(is, openStackMachine.Name, net, &securityGroups) + port, err = createPort(s, openStackMachine.Name, net, &securityGroups) if err != nil { return nil, fmt.Errorf("failed to create port err: %v", err) } @@ -158,7 +157,7 @@ func (is *Service) InstanceCreate(clusterName string, machine *v1alpha2.Machine, port = portList[0] } - _, err = attributestags.ReplaceAll(is.networkClient, "ports", port.ID, attributestags.ReplaceAllOpts{ + _, err = attributestags.ReplaceAll(s.networkClient, "ports", port.ID, attributestags.ReplaceAllOpts{ Tags: machineTags}).Extract() if err != nil { return nil, fmt.Errorf("tagging port for server err: %v", err) @@ -167,8 +166,8 @@ func (is *Service) InstanceCreate(clusterName string, machine *v1alpha2.Machine, Port: port.ID, }) - if openStackMachine.Spec.Trunk == true { - allPages, err := trunks.List(is.networkClient, trunks.ListOpts{ + if openStackMachine.Spec.Trunk { + allPages, err := trunks.List(s.networkClient, trunks.ListOpts{ Name: openStackMachine.Name, PortID: port.ID, }).AllPages() @@ -186,7 +185,7 @@ func (is *Service) InstanceCreate(clusterName string, machine *v1alpha2.Machine, Name: openStackMachine.Name, PortID: port.ID, } - newTrunk, err := trunks.Create(is.networkClient, trunkCreateOpts).Extract() + newTrunk, err := trunks.Create(s.networkClient, trunkCreateOpts).Extract() if err != nil { return nil, fmt.Errorf("create trunk for server err: %v", err) } @@ -195,7 +194,7 @@ func (is *Service) InstanceCreate(clusterName string, machine *v1alpha2.Machine, trunk = trunkList[0] } - _, err = attributestags.ReplaceAll(is.networkClient, "trunks", trunk.ID, attributestags.ReplaceAllOpts{ + _, err = attributestags.ReplaceAll(s.networkClient, "trunks", trunk.ID, attributestags.ReplaceAllOpts{ Tags: machineTags}).Extract() if err != nil { return nil, fmt.Errorf("tagging trunk for server err: %v", err) @@ -204,20 +203,23 @@ func (is *Service) InstanceCreate(clusterName string, machine *v1alpha2.Machine, } var serverTags []string - if openStackCluster.Spec.DisableServerTags == false { + if !openStackCluster.Spec.DisableServerTags { serverTags = machineTags // NOTE(flaper87): This is the minimum required version // to use tags. - is.computeClient.Microversion = "2.52" + s.computeClient.Microversion = "2.52" + defer func(s *Service) { + s.computeClient.Microversion = "" + }(s) } // Get image ID - imageID, err := getImageID(is, openStackMachine.Spec.Image) + imageID, err := getImageID(s, openStackMachine.Spec.Image) if err != nil { return nil, fmt.Errorf("create new server err: %v", err) } - serverCreateOpts := servers.CreateOpts{ + var serverCreateOpts servers.CreateOptsBuilder = servers.CreateOpts{ Name: openStackMachine.Name, ImageRef: imageID, FlavorName: openStackMachine.Spec.Flavor, @@ -225,7 +227,7 @@ func (is *Service) InstanceCreate(clusterName string, machine *v1alpha2.Machine, Networks: portsList, UserData: []byte(*machine.Spec.Bootstrap.Data), SecurityGroups: securityGroups, - ServiceClient: is.computeClient, + ServiceClient: s.computeClient, Tags: serverTags, Metadata: openStackMachine.Spec.ServerMetadata, ConfigDrive: openStackMachine.Spec.ConfigDrive, @@ -233,8 +235,6 @@ func (is *Service) InstanceCreate(clusterName string, machine *v1alpha2.Machine, // If the root volume Size is not 0, means boot from volume if openStackMachine.Spec.RootVolume != nil && openStackMachine.Spec.RootVolume.Size != 0 { - var blocks []bootfromvolume.BlockDevice - block := bootfromvolume.BlockDevice{ SourceType: bootfromvolume.SourceType(openStackMachine.Spec.RootVolume.SourceType), BootIndex: 0, @@ -244,22 +244,22 @@ func (is *Service) InstanceCreate(clusterName string, machine *v1alpha2.Machine, VolumeSize: openStackMachine.Spec.RootVolume.Size, DeviceType: openStackMachine.Spec.RootVolume.DeviceType, } - blocks = append(blocks, block) - - createOpts = bootfromvolume.CreateOptsExt{ - CreateOptsBuilder: createOpts, - BlockDevice: blocks, + serverCreateOpts = bootfromvolume.CreateOptsExt{ + CreateOptsBuilder: serverCreateOpts, + BlockDevice: []bootfromvolume.BlockDevice{block}, } } - server, err := servers.Create(is.computeClient, keypairs.CreateOptsExt{ + server, err := servers.Create(s.computeClient, keypairs.CreateOptsExt{ CreateOptsBuilder: serverCreateOpts, - KeyName: openStackMachine.Spec.KeyName, + KeyName: openStackMachine.Spec.SSHKeyName, }).Extract() if err != nil { + record.Warnf(openStackMachine, "FailedCreateServer", "Failed to create server: %v", err) return nil, fmt.Errorf("create new server err: %v", err) } - is.computeClient.Microversion = "" + record.Eventf(openStackMachine, "SuccessfulCreateServer", "Created server %s with id %s", openStackMachine.Name, server.ID) + return &Instance{Server: *server, State: infrav1.InstanceState(server.Status)}, nil } @@ -309,7 +309,7 @@ func getSecurityGroups(is *Service, securityGroupParams []infrav1.SecurityGroupP } func isDuplicate(list []string, name string) bool { - if list == nil || len(list) == 0 { + if len(list) == 0 { return false } for _, element := range list { @@ -321,7 +321,7 @@ func isDuplicate(list []string, name string) bool { } // A function for getting the id of a network by querying openstack with filters -func getNetworkIDsByFilter(networkClient *gophercloud.ServiceClient, opts *networks.ListOpts) ([]string, error) { +func getNetworkIDsByFilter(networkClient *gophercloud.ServiceClient, opts networks.ListOptsBuilder) ([]string, error) { if opts == nil { return []string{}, fmt.Errorf("no Filters were passed") } @@ -391,14 +391,19 @@ func getImageID(is *Service, imageName string) (string, error) { } } -func (is *Service) AssociateFloatingIP(instanceID, floatingIP string) error { +func (s *Service) AssociateFloatingIP(instanceID, floatingIP string) error { opts := floatingips.AssociateOpts{ FloatingIP: floatingIP, } - return floatingips.AssociateInstance(is.computeClient, instanceID, opts).ExtractErr() + return floatingips.AssociateInstance(s.computeClient, instanceID, opts).ExtractErr() } -func (is *Service) InstanceDelete(machine *v1alpha2.Machine) error { +func (s *Service) InstanceDelete(machine *clusterv1.Machine) error { + + if machine.Spec.ProviderID == nil { + // nothing to do + return nil + } parsed, err := noderefutil.NewProviderID(*machine.Spec.ProviderID) if err != nil { @@ -406,7 +411,7 @@ func (is *Service) InstanceDelete(machine *v1alpha2.Machine) error { } // get instance port id - allInterfaces, err := attachinterfaces.List(is.computeClient, parsed.ID()).AllPages() + allInterfaces, err := attachinterfaces.List(s.computeClient, parsed.ID()).AllPages() if err != nil { return err } @@ -415,16 +420,16 @@ func (is *Service) InstanceDelete(machine *v1alpha2.Machine) error { return err } if len(instanceInterfaces) < 1 { - return servers.Delete(is.computeClient, parsed.ID()).ExtractErr() + return servers.Delete(s.computeClient, parsed.ID()).ExtractErr() } - trunkSupport, err := getTrunkSupport(is) + trunkSupport, err := getTrunkSupport(s) if err != nil { return fmt.Errorf("obtaining network extensions: %v", err) } // get and delete trunks for _, port := range instanceInterfaces { - err := attachinterfaces.Delete(is.computeClient, parsed.ID(), port.PortID).ExtractErr() + err := attachinterfaces.Delete(s.computeClient, parsed.ID(), port.PortID).ExtractErr() if err != nil { return err } @@ -432,7 +437,7 @@ func (is *Service) InstanceDelete(machine *v1alpha2.Machine) error { listOpts := trunks.ListOpts{ PortID: port.PortID, } - allTrunks, err := trunks.List(is.networkClient, listOpts).AllPages() + allTrunks, err := trunks.List(s.networkClient, listOpts).AllPages() if err != nil { return err } @@ -442,7 +447,7 @@ func (is *Service) InstanceDelete(machine *v1alpha2.Machine) error { } if len(trunkInfo) == 1 { err = util.PollImmediate(RetryIntervalTrunkDelete, TimeoutTrunkDelete, func() (bool, error) { - err := trunks.Delete(is.networkClient, trunkInfo[0].ID).ExtractErr() + err := trunks.Delete(s.networkClient, trunkInfo[0].ID).ExtractErr() if err != nil { return false, nil } @@ -456,7 +461,7 @@ func (is *Service) InstanceDelete(machine *v1alpha2.Machine) error { // delete port err = util.PollImmediate(RetryIntervalPortDelete, TimeoutPortDelete, func() (bool, error) { - err := ports.Delete(is.networkClient, port.PortID).ExtractErr() + err := ports.Delete(s.networkClient, port.PortID).ExtractErr() if err != nil { return false, nil } @@ -468,7 +473,7 @@ func (is *Service) InstanceDelete(machine *v1alpha2.Machine) error { } // delete instance - return servers.Delete(is.computeClient, parsed.ID()).ExtractErr() + return servers.Delete(s.computeClient, parsed.ID()).ExtractErr() } type InstanceListOpts struct { @@ -485,7 +490,7 @@ type InstanceListOpts struct { Name string `q:"name"` } -func (is *Service) GetInstanceList(opts *InstanceListOpts) ([]*Instance, error) { +func (s *Service) GetInstanceList(opts *InstanceListOpts) ([]*Instance, error) { var listOpts servers.ListOpts if opts != nil { listOpts = servers.ListOpts{ @@ -495,7 +500,7 @@ func (is *Service) GetInstanceList(opts *InstanceListOpts) ([]*Instance, error) listOpts = servers.ListOpts{} } - allPages, err := servers.List(is.computeClient, listOpts).AllPages() + allPages, err := servers.List(s.computeClient, listOpts).AllPages() if err != nil { return nil, fmt.Errorf("get service list: %v", err) } @@ -503,7 +508,7 @@ func (is *Service) GetInstanceList(opts *InstanceListOpts) ([]*Instance, error) if err != nil { return nil, fmt.Errorf("extract services list: %v", err) } - var instanceList []*Instance + instanceList := []*Instance{} for _, server := range serverList { instanceList = append(instanceList, &Instance{ Server: server, @@ -513,25 +518,25 @@ func (is *Service) GetInstanceList(opts *InstanceListOpts) ([]*Instance, error) return instanceList, nil } -func (is *Service) GetInstance(resourceId string) (instance *Instance, err error) { - if resourceId == "" { - return nil, fmt.Errorf("ResourceId should be specified to get detail.") +func (s *Service) GetInstance(resourceID string) (instance *Instance, err error) { + if resourceID == "" { + return nil, fmt.Errorf("resourceId should be specified to get detail") } - server, err := servers.Get(is.computeClient, resourceId).Extract() + server, err := servers.Get(s.computeClient, resourceID).Extract() if err != nil { - return nil, fmt.Errorf("get server %q detail failed: %v", resourceId, err) + return nil, fmt.Errorf("get server %q detail failed: %v", resourceID, err) } return &Instance{Server: *server, State: infrav1.InstanceState(server.Status)}, err } -func (is *Service) InstanceExists(openStackMachine *infrav1.OpenStackMachine) (instance *Instance, err error) { +func (s *Service) InstanceExists(openStackMachine *infrav1.OpenStackMachine) (instance *Instance, err error) { opts := &InstanceListOpts{ Name: openStackMachine.Name, Image: openStackMachine.Spec.Image, Flavor: openStackMachine.Spec.Flavor, } - instanceList, err := is.GetInstanceList(opts) + instanceList, err := s.GetInstanceList(opts) if err != nil { return nil, err } @@ -540,21 +545,3 @@ func (is *Service) InstanceExists(openStackMachine *infrav1.OpenStackMachine) (i } return instanceList[0], nil } - -// UpdateToken to update token if need. -func (is *Service) UpdateToken() error { - token := is.provider.Token() - result, err := tokens.Validate(is.identityClient, token) - if err != nil { - return fmt.Errorf("validate token: %v", err) - } - if result { - return nil - } - klog.V(2).Infof("Token is out of date, getting new token.") - reAuthFunction := is.provider.ReauthFunc - if reAuthFunction() != nil { - return fmt.Errorf("reAuth: %v", err) - } - return nil -} diff --git a/pkg/cloud/services/compute/service.go b/pkg/cloud/services/compute/service.go index 16ab5afb04..7f44bcfb29 100644 --- a/pkg/cloud/services/compute/service.go +++ b/pkg/cloud/services/compute/service.go @@ -18,6 +18,8 @@ package compute import ( "fmt" + "github.com/go-logr/logr" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/utils/openstack/clientconfig" @@ -29,9 +31,10 @@ type Service struct { identityClient *gophercloud.ServiceClient networkClient *gophercloud.ServiceClient imagesClient *gophercloud.ServiceClient + logger logr.Logger } -func NewService(client *gophercloud.ProviderClient, clientOpts *clientconfig.ClientOpts) (*Service, error) { +func NewService(client *gophercloud.ProviderClient, clientOpts *clientconfig.ClientOpts, logger logr.Logger) (*Service, error) { identityClient, err := openstack.NewIdentityV3(client, gophercloud.EndpointOpts{ Region: "", }) @@ -66,5 +69,6 @@ func NewService(client *gophercloud.ProviderClient, clientOpts *clientconfig.Cli computeClient: computeClient, networkClient: networkingClient, imagesClient: imagesClient, + logger: logger, }, nil } diff --git a/pkg/cloud/services/loadbalancer/loadbalancer.go b/pkg/cloud/services/loadbalancer/loadbalancer.go index b43f87560c..3789b3df6b 100644 --- a/pkg/cloud/services/loadbalancer/loadbalancer.go +++ b/pkg/cloud/services/loadbalancer/loadbalancer.go @@ -3,6 +3,9 @@ package loadbalancer import ( "errors" "fmt" + "github.com/go-logr/logr" + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners" "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers" @@ -10,30 +13,28 @@ import ( "github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/klog" infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha2" - "sigs.k8s.io/cluster-api/api/v1alpha2" + clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha2" "sigs.k8s.io/cluster-api/util" - "time" ) func (s *Service) ReconcileLoadBalancer(clusterName string, openStackCluster *infrav1.OpenStackCluster) error { if openStackCluster.Spec.ExternalNetworkID == "" { - klog.V(3).Infof("No need to create loadbalancer, due to missing ExternalNetworkID") + s.logger.V(3).Info("No need to create loadbalancer, due to missing ExternalNetworkID") return nil } if openStackCluster.Spec.APIServerLoadBalancerFloatingIP == "" { - klog.V(3).Infof("No need to create loadbalancer, due to missing APIServerLoadBalancerFloatingIP") + s.logger.V(3).Info("No need to create loadbalancer, due to missing APIServerLoadBalancerFloatingIP") return nil } if openStackCluster.Spec.APIServerLoadBalancerPort == 0 { - klog.V(3).Infof("No need to create loadbalancer, due to missing APIServerLoadBalancerPort") + s.logger.V(3).Info("No need to create loadbalancer, due to missing APIServerLoadBalancerPort") return nil } loadBalancerName := fmt.Sprintf("%s-cluster-%s-%s", networkPrefix, clusterName, kubeapiLBSuffix) - klog.Infof("Reconciling loadbalancer %s", loadBalancerName) + s.logger.Info("Reconciling loadbalancer", "name", loadBalancerName) // lb lb, err := checkIfLbExists(s.loadbalancerClient, loadBalancerName) @@ -41,7 +42,7 @@ func (s *Service) ReconcileLoadBalancer(clusterName string, openStackCluster *in return err } if lb == nil { - klog.Infof("Creating loadbalancer %s", loadBalancerName) + s.logger.Info("Creating loadbalancer", "name", loadBalancerName) lbCreateOpts := loadbalancers.CreateOpts{ Name: loadBalancerName, VipSubnetID: openStackCluster.Status.Network.Subnet.ID, @@ -51,7 +52,7 @@ func (s *Service) ReconcileLoadBalancer(clusterName string, openStackCluster *in if err != nil { return fmt.Errorf("error creating loadbalancer: %s", err) } - err = waitForLoadBalancer(s.loadbalancerClient, lb.ID, "ACTIVE") + err = waitForLoadBalancer(s.logger, s.loadbalancerClient, lb.ID, "ACTIVE") if err != nil { return err } @@ -63,7 +64,7 @@ func (s *Service) ReconcileLoadBalancer(clusterName string, openStackCluster *in return err } if fp == nil { - klog.Infof("Creating floating ip %s", openStackCluster.Spec.APIServerLoadBalancerFloatingIP) + s.logger.Info("Creating floating ip", "ip", openStackCluster.Spec.APIServerLoadBalancerFloatingIP) fpCreateOpts := &floatingips.CreateOpts{ FloatingIP: openStackCluster.Spec.APIServerLoadBalancerFloatingIP, FloatingNetworkID: openStackCluster.Spec.ExternalNetworkID, @@ -75,7 +76,7 @@ func (s *Service) ReconcileLoadBalancer(clusterName string, openStackCluster *in } // associate floating ip - klog.Infof("Associating floating ip %s", openStackCluster.Spec.APIServerLoadBalancerFloatingIP) + s.logger.Info("Associating floating ip", "ip", openStackCluster.Spec.APIServerLoadBalancerFloatingIP) fpUpdateOpts := &floatingips.UpdateOpts{ PortID: &lb.VipPortID, } @@ -83,7 +84,7 @@ func (s *Service) ReconcileLoadBalancer(clusterName string, openStackCluster *in if err != nil { return fmt.Errorf("error allocating floating IP: %s", err) } - err = waitForFloatingIP(s.networkingClient, fp.ID, "ACTIVE") + err = waitForFloatingIP(s.logger, s.networkingClient, fp.ID, "ACTIVE") if err != nil { return err } @@ -99,7 +100,7 @@ func (s *Service) ReconcileLoadBalancer(clusterName string, openStackCluster *in return err } if listener == nil { - klog.Infof("Creating lb listener %s", lbPortObjectsName) + s.logger.Info("Creating lb listener", "name", lbPortObjectsName) listenerCreateOpts := listeners.CreateOpts{ Name: lbPortObjectsName, Protocol: "TCP", @@ -110,11 +111,11 @@ func (s *Service) ReconcileLoadBalancer(clusterName string, openStackCluster *in if err != nil { return fmt.Errorf("error creating listener: %s", err) } - err = waitForLoadBalancer(s.loadbalancerClient, lb.ID, "ACTIVE") + err = waitForLoadBalancer(s.logger, s.loadbalancerClient, lb.ID, "ACTIVE") if err != nil { return err } - err = waitForListener(s.loadbalancerClient, listener.ID, "ACTIVE") + err = waitForListener(s.logger, s.loadbalancerClient, listener.ID, "ACTIVE") if err != nil { return err } @@ -126,7 +127,7 @@ func (s *Service) ReconcileLoadBalancer(clusterName string, openStackCluster *in return err } if pool == nil { - klog.Infof("Creating lb pool %s", lbPortObjectsName) + s.logger.Info("Creating lb pool", "name", lbPortObjectsName) poolCreateOpts := pools.CreateOpts{ Name: lbPortObjectsName, Protocol: "TCP", @@ -137,7 +138,7 @@ func (s *Service) ReconcileLoadBalancer(clusterName string, openStackCluster *in if err != nil { return fmt.Errorf("error creating pool: %s", err) } - err = waitForLoadBalancer(s.loadbalancerClient, lb.ID, "ACTIVE") + err = waitForLoadBalancer(s.logger, s.loadbalancerClient, lb.ID, "ACTIVE") if err != nil { return err } @@ -149,7 +150,7 @@ func (s *Service) ReconcileLoadBalancer(clusterName string, openStackCluster *in return err } if monitor == nil { - klog.Infof("Creating lb monitor %s", lbPortObjectsName) + s.logger.Info("Creating lb monitor", "name", lbPortObjectsName) monitorCreateOpts := monitors.CreateOpts{ Name: lbPortObjectsName, PoolID: pool.ID, @@ -162,7 +163,7 @@ func (s *Service) ReconcileLoadBalancer(clusterName string, openStackCluster *in if err != nil { return fmt.Errorf("error creating monitor: %s", err) } - err = waitForLoadBalancer(s.loadbalancerClient, lb.ID, "ACTIVE") + err = waitForLoadBalancer(s.logger, s.loadbalancerClient, lb.ID, "ACTIVE") if err != nil { return err } @@ -178,7 +179,7 @@ func (s *Service) ReconcileLoadBalancer(clusterName string, openStackCluster *in return nil } -func (s *Service) ReconcileLoadBalancerMember(clusterName string, machine *v1alpha2.Machine, openStackMachine *infrav1.OpenStackMachine, openStackCluster *infrav1.OpenStackCluster, ip string) error { +func (s *Service) ReconcileLoadBalancerMember(clusterName string, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine, openStackCluster *infrav1.OpenStackCluster, ip string) error { if !util.IsControlPlaneMachine(machine) { return nil } @@ -194,7 +195,7 @@ func (s *Service) ReconcileLoadBalancerMember(clusterName string, machine *v1alp } loadBalancerName := fmt.Sprintf("%s-cluster-%s-%s", networkPrefix, clusterName, kubeapiLBSuffix) - klog.Infof("Reconciling loadbalancer %s for member %s", loadBalancerName, openStackMachine.Name) + s.logger.Info("Reconciling loadbalancer", "name", loadBalancerName) lbID := openStackCluster.Status.Network.APIServerLoadBalancer.ID subnetID := openStackCluster.Status.Network.Subnet.ID @@ -225,10 +226,10 @@ func (s *Service) ReconcileLoadBalancerMember(clusterName string, machine *v1alp return nil } - klog.Infof("Deleting lb member %s (because the IP of the machine changed)", name) + s.logger.Info("Deleting lb member (because the IP of the machine changed)", "name", name) // lb member changed so let's delete it so we can create it again with the correct IP - err = waitForLoadBalancer(s.loadbalancerClient, lbID, "ACTIVE") + err = waitForLoadBalancer(s.logger, s.loadbalancerClient, lbID, "ACTIVE") if err != nil { return err } @@ -236,13 +237,13 @@ func (s *Service) ReconcileLoadBalancerMember(clusterName string, machine *v1alp if err != nil { return fmt.Errorf("error deleting lbmember: %s", err) } - err = waitForLoadBalancer(s.loadbalancerClient, lbID, "ACTIVE") + err = waitForLoadBalancer(s.logger, s.loadbalancerClient, lbID, "ACTIVE") if err != nil { return err } } - klog.Infof("Creating lb member %s", name) + s.logger.Info("Creating lb member", "name", name) // if we got to this point we should either create or re-create the lb member lbMemberOpts := pools.CreateMemberOpts{ @@ -252,16 +253,13 @@ func (s *Service) ReconcileLoadBalancerMember(clusterName string, machine *v1alp SubnetID: subnetID, } - err = waitForLoadBalancer(s.loadbalancerClient, lbID, "ACTIVE") - if err != nil { + if err := waitForLoadBalancer(s.logger, s.loadbalancerClient, lbID, "ACTIVE"); err != nil { return err } - lbMember, err = pools.CreateMember(s.loadbalancerClient, pool.ID, lbMemberOpts).Extract() - if err != nil { + if _, err := pools.CreateMember(s.loadbalancerClient, pool.ID, lbMemberOpts).Extract(); err != nil { return fmt.Errorf("error create lbmember: %s", err) } - err = waitForLoadBalancer(s.loadbalancerClient, lbID, "ACTIVE") - if err != nil { + if err := waitForLoadBalancer(s.logger, s.loadbalancerClient, lbID, "ACTIVE"); err != nil { return err } } @@ -274,13 +272,17 @@ func (s *Service) DeleteLoadBalancer(clusterName string, openStackCluster *infra if err != nil { return err } + if lb == nil { + // nothing to do + return nil + } // only Octavia supports Cascade if openStackCluster.Spec.UseOctavia { deleteOpts := loadbalancers.DeleteOpts{ Cascade: true, } - klog.Infof("Deleting loadbalancer %s", loadBalancerName) + s.logger.Info("Deleting loadbalancer", "name", loadBalancerName) err = loadbalancers.Delete(s.loadbalancerClient, lb.ID, deleteOpts).ExtractErr() if err != nil { return fmt.Errorf("error deleting loadbalancer: %s", err) @@ -329,12 +331,12 @@ func (s *Service) deleteLoadBalancerNeutronV2(id string) error { for _, pool := range lbPools { // delete the monitors if pool.MonitorID != "" { - klog.Infof("Deleting lb monitor %s", pool.MonitorID) + s.logger.Info("Deleting lb monitor", "id", pool.MonitorID) err := monitors.Delete(s.loadbalancerClient, pool.MonitorID).ExtractErr() if err != nil { return fmt.Errorf("error deleting lbaas monitor %s: %v", pool.MonitorID, err) } - if err = waitForLoadBalancer(s.loadbalancerClient, lb.ID, "ACTIVE"); err != nil { + if err = waitForLoadBalancer(s.logger, s.loadbalancerClient, lb.ID, "ACTIVE"); err != nil { return fmt.Errorf("loadbalancer %s did not get back to %s state in time", lb.ID, "Active") } } @@ -350,41 +352,41 @@ func (s *Service) deleteLoadBalancerNeutronV2(id string) error { } // delete all members of pool for _, member := range members { - klog.Infof("Deleting lb member %s (%s)", member.Name, member.ID) + s.logger.Info("Deleting lb member", "name", member.Name, "id", member.ID) err := pools.DeleteMember(s.loadbalancerClient, pool.ID, member.ID).ExtractErr() if err != nil { return fmt.Errorf("error deleting lbaas member %s on pool %s: %v", member.ID, pool.ID, err) } - if err = waitForLoadBalancer(s.loadbalancerClient, lb.ID, "ACTIVE"); err != nil { + if err = waitForLoadBalancer(s.logger, s.loadbalancerClient, lb.ID, "ACTIVE"); err != nil { return fmt.Errorf("loadbalancer %s did not get back to %s state in time", lb.ID, "ACTIVE") } } // delete pool - klog.Infof("Deleting lb pool %s (%s)", pool.Name, pool.ID) + s.logger.Info("Deleting lb pool", "name", pool.Name, "id", pool.ID) err = pools.Delete(s.loadbalancerClient, pool.ID).ExtractErr() if err != nil { return fmt.Errorf("error deleting lbaas pool %s: %v", pool.ID, err) } - if err = waitForLoadBalancer(s.loadbalancerClient, lb.ID, "ACTIVE"); err != nil { + if err = waitForLoadBalancer(s.logger, s.loadbalancerClient, lb.ID, "ACTIVE"); err != nil { return fmt.Errorf("loadbalancer %s did not get back to %s state in time", lb.ID, "ACTIVE") } } // delete all listeners for _, listener := range lbListeners { - klog.Infof("Deleting lb listener %s (%s)", listener.Name, listener.ID) + s.logger.Info("Deleting lb listener", "name", listener.Name, "id", listener.ID) err = listeners.Delete(s.loadbalancerClient, listener.ID).ExtractErr() if err != nil { return fmt.Errorf("error deleting lbaas listener %s: %v", listener.ID, err) } - if err = waitForLoadBalancer(s.loadbalancerClient, lb.ID, "ACTIVE"); err != nil { + if err = waitForLoadBalancer(s.logger, s.loadbalancerClient, lb.ID, "ACTIVE"); err != nil { return fmt.Errorf("loadbalancer %s did not get back to %s state in time", lb.ID, "ACTIVE") } } // delete loadbalancer - klog.Infof("Deleting loadbalancer %s (%s)", lb.Name, lb.ID) + s.logger.Info("Deleting loadbalancer", "name", lb.Name, "id", lb.ID) if err = loadbalancers.Delete(s.loadbalancerClient, lb.ID, loadbalancers.DeleteOpts{}).ExtractErr(); err != nil { return fmt.Errorf("error deleting lbaas %s: %v", lb.ID, err) } @@ -392,14 +394,14 @@ func (s *Service) deleteLoadBalancerNeutronV2(id string) error { return nil } -func (s *Service) DeleteLoadBalancerMember(clusterName string, machine *v1alpha2.Machine, openStackMachine *infrav1.OpenStackMachine, openStackCluster *infrav1.OpenStackCluster) error { +func (s *Service) DeleteLoadBalancerMember(clusterName string, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine, openStackCluster *infrav1.OpenStackCluster) error { if openStackMachine == nil || !util.IsControlPlaneMachine(machine) { return nil } loadBalancerName := fmt.Sprintf("%s-cluster-%s-%s", networkPrefix, clusterName, kubeapiLBSuffix) - klog.Infof("Reconciling loadbalancer %s", loadBalancerName) + s.logger.Info("Reconciling loadbalancer", "name", loadBalancerName) lbID := openStackCluster.Status.Network.APIServerLoadBalancer.ID @@ -414,7 +416,7 @@ func (s *Service) DeleteLoadBalancerMember(clusterName string, machine *v1alpha2 return err } if pool == nil { - klog.Infof("Pool %s does not exist", lbPortObjectsName) + s.logger.Info("Pool does not exist", "name", lbPortObjectsName) continue } @@ -426,7 +428,7 @@ func (s *Service) DeleteLoadBalancerMember(clusterName string, machine *v1alpha2 if lbMember != nil { // lb member changed so let's delete it so we can create it again with the correct IP - err = waitForLoadBalancer(s.loadbalancerClient, lbID, "ACTIVE") + err = waitForLoadBalancer(s.logger, s.loadbalancerClient, lbID, "ACTIVE") if err != nil { return err } @@ -434,7 +436,7 @@ func (s *Service) DeleteLoadBalancerMember(clusterName string, machine *v1alpha2 if err != nil { return fmt.Errorf("error deleting lbmember: %s", err) } - err = waitForLoadBalancer(s.loadbalancerClient, lbID, "ACTIVE") + err = waitForLoadBalancer(s.logger, s.loadbalancerClient, lbID, "ACTIVE") if err != nil { return err } @@ -541,8 +543,8 @@ var backoff = wait.Backoff{ } // Possible LoadBalancer states are documented here: https://developer.openstack.org/api-ref/network/v2/?expanded=show-load-balancer-status-tree-detail#load-balancer-statuses -func waitForLoadBalancer(client *gophercloud.ServiceClient, id, target string) error { - klog.Infof("Waiting for loadbalancer %s to become %s.", id, target) +func waitForLoadBalancer(logger logr.Logger, client *gophercloud.ServiceClient, id, target string) error { + logger.Info("Waiting for loadbalancer", "id", id, "targetStatus", target) return wait.ExponentialBackoff(backoff, func() (bool, error) { lb, err := loadbalancers.Get(client, id).Extract() if err != nil { @@ -552,8 +554,8 @@ func waitForLoadBalancer(client *gophercloud.ServiceClient, id, target string) e }) } -func waitForFloatingIP(client *gophercloud.ServiceClient, id, target string) error { - klog.Infof("Waiting for floatingip %s to become %s.", id, target) +func waitForFloatingIP(logger logr.Logger, client *gophercloud.ServiceClient, id, target string) error { + logger.Info("Waiting for floatingIP", "id", id, "targetStatus", target) return wait.ExponentialBackoff(backoff, func() (bool, error) { fp, err := floatingips.Get(client, id).Extract() if err != nil { @@ -563,8 +565,8 @@ func waitForFloatingIP(client *gophercloud.ServiceClient, id, target string) err }) } -func waitForListener(client *gophercloud.ServiceClient, id, target string) error { - klog.Infof("Waiting for listener %s to become %s.", id, target) +func waitForListener(logger logr.Logger, client *gophercloud.ServiceClient, id, target string) error { + logger.Info("Waiting for listener", "id", id, "targetStatus", target) return wait.ExponentialBackoff(backoff, func() (bool, error) { _, err := listeners.Get(client, id).Extract() if err != nil { diff --git a/pkg/cloud/services/loadbalancer/service.go b/pkg/cloud/services/loadbalancer/service.go index 7f9aba9b41..3551424397 100644 --- a/pkg/cloud/services/loadbalancer/service.go +++ b/pkg/cloud/services/loadbalancer/service.go @@ -18,6 +18,8 @@ package loadbalancer import ( "fmt" + "github.com/go-logr/logr" + "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/utils/openstack/clientconfig" @@ -33,10 +35,11 @@ const ( type Service struct { loadbalancerClient *gophercloud.ServiceClient networkingClient *gophercloud.ServiceClient + logger logr.Logger } // NewService returns an instance of the loadbalancer service -func NewService(client *gophercloud.ProviderClient, clientOpts *clientconfig.ClientOpts, useOctavia bool) (*Service, error) { +func NewService(client *gophercloud.ProviderClient, clientOpts *clientconfig.ClientOpts, logger logr.Logger, useOctavia bool) (*Service, error) { var err error var loadbalancerClient *gophercloud.ServiceClient if useOctavia { @@ -61,5 +64,6 @@ func NewService(client *gophercloud.ProviderClient, clientOpts *clientconfig.Cli return &Service{ loadbalancerClient: loadbalancerClient, networkingClient: networkingClient, + logger: logger, }, nil } diff --git a/pkg/cloud/services/networking/floatingip.go b/pkg/cloud/services/networking/floatingip.go index b3e11e1614..37d33ddaab 100644 --- a/pkg/cloud/services/networking/floatingip.go +++ b/pkg/cloud/services/networking/floatingip.go @@ -2,25 +2,24 @@ package networking import ( "fmt" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" - "k8s.io/klog" infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha2" ) -func (s *Service) GetOrCreateFloatingIP(openStackCluster *infrav1.OpenStackCluster, ip string) error { +func (s *Service) CreateFloatingIPIfNecessary(openStackCluster *infrav1.OpenStackCluster, ip string) error { fp, err := checkIfFloatingIPExists(s.client, ip) if err != nil { return err } if fp == nil { - klog.Infof("Creating floating ip %s", ip) + s.logger.Info("Creating floating ip", "ip", ip) fpCreateOpts := &floatingips.CreateOpts{ FloatingIP: ip, FloatingNetworkID: openStackCluster.Spec.ExternalNetworkID, } - fp, err = floatingips.Create(s.client, fpCreateOpts).Extract() - if err != nil { + if _, err = floatingips.Create(s.client, fpCreateOpts).Extract(); err != nil { return fmt.Errorf("error allocating floating IP: %s", err) } } diff --git a/pkg/cloud/services/networking/network.go b/pkg/cloud/services/networking/network.go index d93326c568..d4524a48a9 100644 --- a/pkg/cloud/services/networking/network.go +++ b/pkg/cloud/services/networking/network.go @@ -18,12 +18,13 @@ package networking import ( "fmt" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/record" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags" "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" "github.com/pkg/errors" - "k8s.io/klog" infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha2" ) @@ -40,7 +41,7 @@ func (c createOpts) ToNetworkCreateMap() (map[string]interface{}, error) { func (s *Service) ReconcileNetwork(clusterName string, openStackCluster *infrav1.OpenStackCluster) error { networkName := fmt.Sprintf("%s-cluster-%s", networkPrefix, clusterName) - klog.Infof("Reconciling network %s", networkName) + s.logger.Info("Reconciling network", "name", networkName) res, err := s.getNetworkByName(networkName) if err != nil { @@ -69,8 +70,10 @@ func (s *Service) ReconcileNetwork(clusterName string, openStackCluster *infrav1 } network, err := networks.Create(s.client, opts).Extract() if err != nil { + record.Warnf(openStackCluster, "FailedCreateNetwork", "Failed to create network %s: %v", networkName, err) return err } + record.Eventf(openStackCluster, "SuccessfulCreateNetwork", "Created network %s with id %s", networkName, network.ID) _, err = attributestags.ReplaceAll(s.client, "networks", network.ID, attributestags.ReplaceAllOpts{ Tags: []string{ @@ -91,12 +94,12 @@ func (s *Service) ReconcileNetwork(clusterName string, openStackCluster *infrav1 func (s *Service) ReconcileSubnet(clusterName string, openStackCluster *infrav1.OpenStackCluster) error { if openStackCluster.Status.Network == nil || openStackCluster.Status.Network.ID == "" { - klog.V(4).Infof("No need to reconcile network components since no network exists.") + s.logger.V(4).Info("No need to reconcile network components since no network exists.") return nil } subnetName := fmt.Sprintf("%s-cluster-%s", networkPrefix, clusterName) - klog.Infof("Reconciling subnet %s", subnetName) + s.logger.Info("Reconciling subnet", "name", subnetName) allPages, err := subnets.List(s.client, subnets.ListOpts{ NetworkID: openStackCluster.Status.Network.ID, @@ -127,8 +130,10 @@ func (s *Service) ReconcileSubnet(clusterName string, openStackCluster *infrav1. newSubnet, err := subnets.Create(s.client, opts).Extract() if err != nil { + record.Warnf(openStackCluster, "FailedCreateSubnet", "Failed to create subnet %s: %v", subnetName, err) return err } + record.Eventf(openStackCluster, "SuccessfulCreateSubnet", "Created subnet %s with id %s", subnetName, newSubnet.ID) observedSubnet = infrav1.Subnet{ ID: newSubnet.ID, Name: newSubnet.Name, diff --git a/pkg/cloud/services/networking/router.go b/pkg/cloud/services/networking/router.go index 32cbd84bcd..abbdae828f 100644 --- a/pkg/cloud/services/networking/router.go +++ b/pkg/cloud/services/networking/router.go @@ -18,33 +18,34 @@ package networking import ( "fmt" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/record" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" "github.com/gophercloud/gophercloud/pagination" - "k8s.io/klog" infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha2" ) func (s *Service) ReconcileRouter(clusterName string, openStackCluster *infrav1.OpenStackCluster) error { if openStackCluster.Status.Network == nil || openStackCluster.Status.Network.ID == "" { - klog.V(3).Infof("No need to reconcile router since no network exists.") + s.logger.V(3).Info("No need to reconcile router since no network exists.") return nil } if openStackCluster.Status.Network.Subnet == nil || openStackCluster.Status.Network.Subnet.ID == "" { - klog.V(4).Infof("No need to reconcile router since no subnet exists.") + s.logger.V(4).Info("No need to reconcile router since no subnet exists.") return nil } if openStackCluster.Spec.ExternalNetworkID == "" { - klog.V(3).Info("No need to create router, due to missing ExternalNetworkID.") + s.logger.V(3).Info("No need to create router, due to missing ExternalNetworkID.") return nil } routerName := fmt.Sprintf("%s-cluster-%s", networkPrefix, clusterName) - klog.Infof("Reconciling router %s", routerName) + s.logger.Info("Reconciling router", "name", routerName) allPages, err := routers.List(s.client, routers.ListOpts{ Name: routerName, @@ -73,8 +74,10 @@ func (s *Service) ReconcileRouter(clusterName string, openStackCluster *infrav1. } newRouter, err := routers.Create(s.client, opts).Extract() if err != nil { + record.Warnf(openStackCluster, "FailedCreateRouter", "Failed to create router %s: %v", routerName, err) return err } + record.Eventf(openStackCluster, "SuccessfulCreateRouter", "Created router %s with id %s", routerName, newRouter.ID) router = *newRouter } else { router = routerList[0] @@ -106,8 +109,10 @@ func (s *Service) ReconcileRouter(clusterName string, openStackCluster *infrav1. _, err = routers.Update(s.client, router.ID, updateOpts).Extract() if err != nil { + record.Warnf(openStackCluster, "FailedUpdateRouter", "Failed to update router %s: %v", routerName, err) return fmt.Errorf("error updating OpenStack Neutron Router: %s", err) } + record.Eventf(openStackCluster, "SuccessfulUpdateRouter", "Updated router %s with id %s", routerName, router.ID) } observedRouter := infrav1.Router{ @@ -134,14 +139,14 @@ INTERFACE_LOOP: // ... and create a router interface for our subnet. if createInterface { - klog.V(4).Infof("Creating RouterInterface on %s in subnet %s", router.ID, openStackCluster.Status.Network.Subnet.ID) - iface, err := routers.AddInterface(s.client, router.ID, routers.AddInterfaceOpts{ + s.logger.V(4).Info("Creating RouterInterface", "routerID", router.ID, "subnetID", openStackCluster.Status.Network.Subnet.ID) + routerInterface, err := routers.AddInterface(s.client, router.ID, routers.AddInterfaceOpts{ SubnetID: openStackCluster.Status.Network.Subnet.ID, }).Extract() if err != nil { return fmt.Errorf("unable to create router interface: %v", err) } - klog.V(4).Infof("Created RouterInterface: %v", iface) + s.logger.V(4).Info("Created RouterInterface", "id", routerInterface.ID) } _, err = attributestags.ReplaceAll(s.client, "routers", observedRouter.ID, attributestags.ReplaceAllOpts{ @@ -176,7 +181,7 @@ func (s *Service) getRouterInterfaces(routerID string) ([]ports.Port, error) { } // A function for getting the id of a subnet by querying openstack with filters -func GetSubnetsByFilter(networkClient *gophercloud.ServiceClient, opts *subnets.ListOpts) ([]subnets.Subnet, error) { +func GetSubnetsByFilter(networkClient *gophercloud.ServiceClient, opts subnets.ListOptsBuilder) ([]subnets.Subnet, error) { if opts == nil { return []subnets.Subnet{}, fmt.Errorf("no Filters were passed") } @@ -189,9 +194,7 @@ func GetSubnetsByFilter(networkClient *gophercloud.ServiceClient, opts *subnets. } else if len(subnetList) == 0 { return false, fmt.Errorf("no subnets could be found with the filters provided") } - for _, subnet := range subnetList { - snets = append(snets, subnet) - } + snets = append(snets, subnetList...) return true, nil }) if err != nil { diff --git a/pkg/cloud/services/networking/securitygroups.go b/pkg/cloud/services/networking/securitygroups.go index badcd48d6b..6da14d4f40 100644 --- a/pkg/cloud/services/networking/securitygroups.go +++ b/pkg/cloud/services/networking/securitygroups.go @@ -18,8 +18,6 @@ package networking import ( "fmt" - "k8s.io/klog" - "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules" infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha2" @@ -29,6 +27,7 @@ const ( secGroupPrefix string = "k8s" controlPlaneSuffix string = "controlplane" globalSuffix string = "all" + remoteGroupIDSelf string = "self" ) var defaultRules = []infrav1.SecurityGroupRule{ @@ -52,9 +51,9 @@ var defaultRules = []infrav1.SecurityGroupRule{ // Reconcile the security groups. func (s *Service) ReconcileSecurityGroups(clusterName string, openStackCluster *infrav1.OpenStackCluster) error { - klog.Infof("Reconciling security groups for cluster %s", clusterName) + s.logger.Info("Reconciling security groups", "cluster", clusterName) if !openStackCluster.Spec.ManagedSecurityGroups { - klog.V(4).Infof("No need to reconcile security groups for cluster %s", clusterName) + s.logger.V(4).Info("No need to reconcile security groups", "cluster", clusterName) return nil } desiredSecGroups := map[string]infrav1.SecurityGroup{ @@ -64,7 +63,7 @@ func (s *Service) ReconcileSecurityGroups(clusterName string, openStackCluster * observedSecGroups := make(map[string]*infrav1.SecurityGroup) for k, desiredSecGroup := range desiredSecGroups { - klog.Infof("Reconciling security group %s", desiredSecGroup.Name) + s.logger.Info("Reconciling security group", "name", desiredSecGroup.Name) var err error observedSecGroups[k], err = s.getSecurityGroupByName(desiredSecGroup.Name) @@ -75,11 +74,11 @@ func (s *Service) ReconcileSecurityGroups(clusterName string, openStackCluster * if observedSecGroups[k].ID != "" { if matchGroups(&desiredSecGroup, observedSecGroups[k]) { - klog.V(6).Infof("Group %s matched, have nothing to do.", desiredSecGroup.Name) + s.logger.V(6).Info("Group matched, have nothing to do.", "name", desiredSecGroup.Name) continue } - klog.V(6).Infof("Group %s didn't match, reconciling...", desiredSecGroup.Name) + s.logger.V(6).Info("Group didn't match, reconciling...", "name", desiredSecGroup.Name) observedSecGroups[k], err = s.reconcileGroup(&desiredSecGroup, observedSecGroups[k]) if err != nil { return err @@ -87,8 +86,11 @@ func (s *Service) ReconcileSecurityGroups(clusterName string, openStackCluster * continue } - klog.V(6).Infof("Group %s doesn't exist, creating it.", desiredSecGroup.Name) + s.logger.V(6).Info("Group doesn't exist, creating it.", "name", desiredSecGroup.Name) observedSecGroups[k], err = s.createSecGroup(desiredSecGroup) + if err != nil { + return err + } } openStackCluster.Status.ControlPlaneSecurityGroup = observedSecGroups["controlplane"] @@ -161,6 +163,7 @@ func generateGlobalGroup(clusterName string) infrav1.SecurityGroup { secGroupName := fmt.Sprintf("%s-cluster-%s-secgroup-%s", secGroupPrefix, clusterName, globalSuffix) // As above, hardcoded rules. + return infrav1.SecurityGroup{ Name: secGroupName, Rules: append( @@ -171,7 +174,7 @@ func generateGlobalGroup(clusterName string) infrav1.SecurityGroup { PortRangeMin: 1, PortRangeMax: 65535, Protocol: "tcp", - RemoteGroupID: "self", + RemoteGroupID: remoteGroupIDSelf, }, { Direction: "ingress", @@ -179,7 +182,7 @@ func generateGlobalGroup(clusterName string) infrav1.SecurityGroup { PortRangeMin: 1, PortRangeMax: 65535, Protocol: "udp", - RemoteGroupID: "self", + RemoteGroupID: remoteGroupIDSelf, }, { Direction: "ingress", @@ -187,7 +190,7 @@ func generateGlobalGroup(clusterName string) infrav1.SecurityGroup { PortRangeMin: 0, PortRangeMax: 0, Protocol: "icmp", - RemoteGroupID: "self", + RemoteGroupID: remoteGroupIDSelf, }, }, defaultRules..., @@ -205,7 +208,7 @@ func matchGroups(desired, observed *infrav1.SecurityGroup) bool { // Rules aren't in any order, so we're doing this the hard way. for _, desiredRule := range desired.Rules { r := desiredRule - if r.RemoteGroupID == "self" { + if r.RemoteGroupID == remoteGroupIDSelf { r.RemoteGroupID = observed.ID } ruleMatched := false @@ -226,20 +229,20 @@ func matchGroups(desired, observed *infrav1.SecurityGroup) bool { // reconcileGroup reconciles an already existing observed group by essentially emptying out all the rules and // recreating them. func (s *Service) reconcileGroup(desired, observed *infrav1.SecurityGroup) (*infrav1.SecurityGroup, error) { - klog.V(6).Infof("Deleting all rules for group %s", observed.Name) + s.logger.V(6).Info("Deleting all rules for group", "name", observed.Name) for _, rule := range observed.Rules { - klog.V(6).Infof("Deleting rule %s from group %s", rule.ID, observed.Name) + s.logger.V(6).Info("Deleting rule", "ruleID", rule.ID, "groupName", observed.Name) err := rules.Delete(s.client, rule.ID).ExtractErr() if err != nil { return &infrav1.SecurityGroup{}, err } } recreatedRules := make([]infrav1.SecurityGroupRule, 0, len(desired.Rules)) - klog.V(6).Infof("Recreating all rules for group %s", observed.Name) + s.logger.V(6).Info("Recreating all rules for group", "name", observed.Name) for _, rule := range desired.Rules { r := rule r.SecurityGroupID = observed.ID - if r.RemoteGroupID == "self" { + if r.RemoteGroupID == remoteGroupIDSelf { r.RemoteGroupID = observed.ID } newRule, err := s.createRule(r) @@ -257,7 +260,7 @@ func (s *Service) createSecGroup(group infrav1.SecurityGroup) (*infrav1.Security Name: group.Name, Description: "Cluster API managed group", } - klog.V(6).Infof("Creating group %+v", createOpts) + s.logger.V(6).Info("Creating group", "name", group.Name) g, err := groups.Create(s.client, createOpts).Extract() if err != nil { return &infrav1.SecurityGroup{}, err @@ -265,11 +268,11 @@ func (s *Service) createSecGroup(group infrav1.SecurityGroup) (*infrav1.Security newGroup := convertOSSecGroupToConfigSecGroup(*g) securityGroupRules := make([]infrav1.SecurityGroupRule, 0, len(group.Rules)) - klog.V(6).Infof("Creating rules for group %s", group.Name) + s.logger.V(6).Info("Creating rules for group", "name", group.Name) for _, rule := range group.Rules { r := rule r.SecurityGroupID = newGroup.ID - if r.RemoteGroupID == "self" { + if r.RemoteGroupID == remoteGroupIDSelf { r.RemoteGroupID = newGroup.ID } newRule, err := s.createRule(r) @@ -288,7 +291,7 @@ func (s *Service) getSecurityGroupByName(name string) (*infrav1.SecurityGroup, e Name: name, } - klog.V(6).Infof("Attempting to fetch security group with name %s", name) + s.logger.V(6).Info("Attempting to fetch security group with", "name", name) allPages, err := groups.List(s.client, opts).AllPages() if err != nil { return &infrav1.SecurityGroup{}, err @@ -324,7 +327,7 @@ func (s *Service) createRule(r infrav1.SecurityGroupRule) (infrav1.SecurityGroup RemoteIPPrefix: r.RemoteIPPrefix, SecGroupID: r.SecurityGroupID, } - klog.V(6).Infof("Creating rule %+v", createOpts) + s.logger.V(6).Info("Creating rule") rule, err := rules.Create(s.client, createOpts).Extract() if err != nil { return infrav1.SecurityGroupRule{}, err diff --git a/pkg/cloud/services/networking/service.go b/pkg/cloud/services/networking/service.go index 805843b893..e73cfc762d 100644 --- a/pkg/cloud/services/networking/service.go +++ b/pkg/cloud/services/networking/service.go @@ -18,6 +18,8 @@ package networking import ( "fmt" + "github.com/go-logr/logr" + "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/utils/openstack/clientconfig" @@ -32,10 +34,11 @@ const ( // It will create a network related infrastructure for the cluster, like network, subnet, router, security groups. type Service struct { client *gophercloud.ServiceClient + logger logr.Logger } // NewService returns an instance of the networking service -func NewService(client *gophercloud.ProviderClient, clientOpts *clientconfig.ClientOpts) (*Service, error) { +func NewService(client *gophercloud.ProviderClient, clientOpts *clientconfig.ClientOpts, logger logr.Logger) (*Service, error) { serviceClient, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ Region: clientOpts.RegionName, }) @@ -44,5 +47,6 @@ func NewService(client *gophercloud.ProviderClient, clientOpts *clientconfig.Cli } return &Service{ client: serviceClient, + logger: logger, }, nil } diff --git a/pkg/cloud/services/provider/provider.go b/pkg/cloud/services/provider/provider.go index 1449354db6..1a3a6fe402 100644 --- a/pkg/cloud/services/provider/provider.go +++ b/pkg/cloud/services/provider/provider.go @@ -5,13 +5,14 @@ import ( "crypto/tls" "crypto/x509" "fmt" + "net/http" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" "github.com/gophercloud/utils/openstack/clientconfig" "gopkg.in/yaml.v2" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" - "net/http" infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha2" "sigs.k8s.io/controller-runtime/pkg/client" ) diff --git a/pkg/record/record.go b/pkg/record/recorder.go similarity index 100% rename from pkg/record/record.go rename to pkg/record/recorder.go