From dfe7a9e0186e3a6d86fcb63b9591adfe5840a0fc Mon Sep 17 00:00:00 2001 From: Maksim Paskal Date: Mon, 6 Nov 2023 14:44:36 +0000 Subject: [PATCH] refactor chart Signed-off-by: Maksim Paskal --- .github/workflows/e2e.yml | 14 + Makefile | 11 +- README.md | 179 ++++++++- cmd/main.go | 11 +- e2e/configs/full.yaml | 124 ++++++ e2e/configs/v1.25.yaml | 30 ++ e2e/configs/v1.26.yaml | 10 + e2e/configs/v1.27.yaml | 10 + e2e/configs/v1.28-amd64-eu-central-fsn1.yaml | 10 + e2e/configs/v1.28-amd64-us-east-ash.yaml | 20 + e2e/configs/v1.28-arm64.yaml | 19 + e2e/e2e_test.go | 279 ++++++++++++++ .../test/templates/test-deployment.yaml | 7 +- examples/charts/test/templates/test-hpa.yaml | 15 + examples/config-full.yaml | 109 ------ go.mod | 34 ++ go.sum | 117 ++++++ internal/internal.go | 33 ++ pkg/api/api.go | 100 +---- pkg/config/config.go | 177 ++++----- pkg/config/config_test.go | 4 +- pkg/config/config_test.yaml | 3 +- pkg/config/const.go | 1 + pkg/drain/drain.go | 238 ++++++++++++ pkg/utils/utils.go | 27 ++ scripts/chart/Chart.lock | 19 +- scripts/chart/Chart.yaml | 17 +- scripts/chart/templates/autoscaler.yaml | 208 ---------- scripts/chart/templates/ccm.yaml | 99 ----- scripts/chart/templates/config.yaml | 23 +- scripts/chart/templates/hcloud-csi.yml | 358 ------------------ scripts/chart/templates/kube-flannel.yml | 217 ----------- scripts/chart/templates/metrics-server.yml | 196 ---------- scripts/chart/templates/nfs-server.yaml | 15 +- scripts/chart/templates/pdb.yaml | 2 +- scripts/chart/templates/registry.yaml | 2 +- scripts/chart/values.yaml | 140 +++++-- .../coredns.yaml} | 0 scripts/patch/kube-flannel-ds.yaml | 25 ++ scripts/patch/kube-proxy.yaml | 9 + scripts/post-install.sh | 16 +- scripts/prepare-scripts.sh | 13 +- utils/main.go | 99 +++++ 43 files changed, 1603 insertions(+), 1437 deletions(-) create mode 100644 .github/workflows/e2e.yml create mode 100644 e2e/configs/full.yaml create mode 100644 e2e/configs/v1.25.yaml create mode 100644 e2e/configs/v1.26.yaml create mode 100644 e2e/configs/v1.27.yaml create mode 100644 e2e/configs/v1.28-amd64-eu-central-fsn1.yaml create mode 100644 e2e/configs/v1.28-amd64-us-east-ash.yaml create mode 100644 e2e/configs/v1.28-arm64.yaml create mode 100644 e2e/e2e_test.go create mode 100644 examples/charts/test/templates/test-hpa.yaml delete mode 100644 examples/config-full.yaml create mode 100644 internal/internal.go create mode 100644 pkg/drain/drain.go create mode 100644 pkg/utils/utils.go delete mode 100644 scripts/chart/templates/autoscaler.yaml delete mode 100644 scripts/chart/templates/ccm.yaml delete mode 100644 scripts/chart/templates/hcloud-csi.yml delete mode 100644 scripts/chart/templates/kube-flannel.yml delete mode 100644 scripts/chart/templates/metrics-server.yml rename scripts/{patch-coredns.yaml => patch/coredns.yaml} (100%) create mode 100644 scripts/patch/kube-flannel-ds.yaml create mode 100644 scripts/patch/kube-proxy.yaml create mode 100644 utils/main.go diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 0000000..318cff7 --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,14 @@ +on: + workflow_dispatch: + pull_request: + +jobs: + e2e: + name: e2e + runs-on: self-hosted + steps: + - uses: actions/setup-go@v2 + with: + go-version: '1.21' + - uses: actions/checkout@v2 + - run: make e2e branch=$GITHUB_HEAD_REF \ No newline at end of file diff --git a/Makefile b/Makefile index e5d8518..d06cc3c 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,9 @@ KUBECONFIG=$(HOME)/.kube/hcloud action=list-configurations config=config.yaml -fullConfig=./examples/config-full.yaml +fullConfig=./e2e/configs/full.yaml args="" +branch=`git rev-parse --abbrev-ref HEAD` test: ./scripts/validate-license.sh @@ -10,6 +11,11 @@ test: go mod tidy go run github.com/golangci/golangci-lint/cmd/golangci-lint@latest run -v CONFIG=config_test.yaml go test -race -coverprofile coverage.out ./cmd/... ./pkg/... +.PHONY: e2e +e2e: + GIT_BRANCH=$(branch) go test -v -count=1 -timeout=4h ./e2e/e2e_test.go +update-readme: + go run ./utils/main.go coverage: go tool cover -html=coverage.out create-cluster: @@ -49,9 +55,6 @@ test-kubernetes-yaml: helm lint ./examples/charts/test --values=$(fullConfig) helm template ./scripts/chart --values=$(fullConfig) | kubectl apply --dry-run=client --validate=true -f - helm template ./examples/charts/test --values=$(fullConfig) | kubectl apply --dry-run=client --validate=true -f - -download-yamls: - curl -sSL -o ./scripts/chart/templates/kube-flannel.yml https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml - curl -sSL -o ./scripts/chart/templates/metrics-server.yml https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml install: go run github.com/goreleaser/goreleaser@latest build \ --single-target \ diff --git a/README.md b/README.md index f1df59c..46a7aeb 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ for other OS download binnary from [release pages](https://github.com/maksim-pas This will create kubernetes cluster in Hetzner Cloud Europe region with 3 instances, 1 load balancer for the kubernetes control plane and 1 kubernetes worker node, after successful installation the cluster will have: -- [Kubernetes v1.24](https://github.com/kubernetes/kubernetes) +- [Kubernetes v1.28](https://github.com/kubernetes/kubernetes) - [Kubernetes Autoscaler](https://github.com/kubernetes/autoscaler) - [Flannel](https://github.com/flannel-io/flannel) - [Kubernetes Cloud Controller Manager for Hetzner Cloud](https://github.com/hetznercloud/hcloud-cloud-controller-manager) @@ -36,7 +36,7 @@ This will create kubernetes cluster in Hetzner Cloud Europe region with 3 instan for HA needs odd number of master nodes (minimum 3) -create a simple configuration file `config.yaml` full configuration example [here](https://github.com/maksim-paskal/hcloud-k8s-ctl/blob/main/examples/config-full.yaml) +Create a simple configuration file `config.yaml` full configuration example [here](https://github.com/maksim-paskal/hcloud-k8s-ctl/blob/main/e2e/configs/full.yaml) ```yaml # kubeconfig path @@ -49,22 +49,183 @@ ipRange: "10.0.0.0/16" masterCount: 3 ``` -to create kubernetes cluster in Hetzner Cloud US region append to `config.yaml` next lines +customize configuration file for your needs ```yaml +# kubeconfig path +kubeConfigPath: ~/.kube/hcloud +# Hetzner cloud internal network CIDR +ipRange: "10.0.0.0/16" +# servers for kubernetes master (recommended 3) +# for development purposes cluster can have 1 master node +# in this case cluster will be created without load balancer and pods can schedule on master +masterCount: 3 +# server components for all nodes in cluster +serverComponents: + kubernetes: + # customize kubertenes version + version: 1.25.14 + docker: + # customize apt package version for docker install + # apt-cache madison docker-ce + version: 5:24.0.6-1~ubuntu.20.04~focal + containerd: + # customize apt package version for containerd install + # apt-cache madison containerd.io + version: 1.6.24-1 +# add autoscaler chart extra values +cluster-autoscaler: + replicaCount: 3 + resources: + requests: + cpu: 200m + memory: 300Mi +# add some custom script for all nodes in cluster +preStartScript: | + # add some custom cron job on node + crontab < /etc/containerd/certs.d/some-registry.io/hosts.toml < +
Kubernetes v1.25 in Europe + +```yaml +ipRange: "10.0.0.0/16" +masterCount: 3 +serverComponents: + kubernetes: + version: 1.25.14 + docker: + version: 5:24.0.6-1~ubuntu.20.04~focal + containerd: + version: 1.6.24-1 +cluster-autoscaler: + replicaCount: 3 + resources: + requests: + cpu: 100m + memory: 300Mi +preStartScript: | + # add some custom cron job on node + crontab < /etc/containerd/certs.d/some-registry.io/hosts.toml < +
Kubernetes v1.26 in Europe + +```yaml +ipRange: "10.0.0.0/16" +masterCount: 3 +serverComponents: + kubernetes: + version: 1.26.9 + docker: + version: 5:24.0.6-1~ubuntu.20.04~focal + containerd: + version: 1.6.24-1 + +``` +
+
Kubernetes v1.27 in Europe + +```yaml +ipRange: "10.0.0.0/16" +masterCount: 3 +serverComponents: + kubernetes: + version: 1.27.6 + docker: + version: 5:24.0.6-1~ubuntu.20.04~focal + containerd: + version: 1.6.24-1 + +``` +
+
Kubernetes v1.28 in Europe + +```yaml +ipRange: "10.0.0.0/16" +masterCount: 3 +serverComponents: + kubernetes: + version: 1.28.2 + docker: + version: 5:24.0.6-1~ubuntu.20.04~focal + containerd: + version: 1.6.24-1 + +``` +
+
Kubernetes v1.28 in US East + +```yaml +ipRange: "10.0.0.0/16" +masterCount: 3 networkZone: us-east location: ash datacenter: ash-dc1 masterServers: servertype: cpx21 -autoscaler: - workers: - - location: ash - type: - - cpx51 +serverComponents: + kubernetes: + version: 1.28.2 + docker: + version: 5:24.0.6-1~ubuntu.20.04~focal + containerd: + version: 1.6.24-1 +cluster-autoscaler: + autoscalingGroups: + - name: CPX51:ASH:cpx51-ash + minSize: 1 + maxSize: 20 +``` +
+
Kubernetes v1.28 in Europe (ARM64 architecture) + +```yaml +ipRange: "10.0.0.0/16" +masterCount: 3 +serverComponents: + ubuntu: + architecture: arm + kubernetes: + version: 1.28.2 + docker: + version: 5:24.0.6-1~ubuntu.20.04~focal + containerd: + version: 1.6.24-1 +masterServers: + servertype: cax11 +cluster-autoscaler: + autoscalingGroups: + - name: CAX41:FSN1:cax-fsn1 + minSize: 1 + maxSize: 20 ``` +
-and start application + ```bash # create 3 instance with 1 load balancer diff --git a/cmd/main.go b/cmd/main.go index 881f41b..a7c2031 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -22,6 +22,7 @@ import ( "syscall" "time" + "github.com/maksim-paskal/hcloud-k8s-ctl/internal" "github.com/maksim-paskal/hcloud-k8s-ctl/pkg/api" "github.com/maksim-paskal/hcloud-k8s-ctl/pkg/config" log "github.com/sirupsen/logrus" @@ -47,7 +48,7 @@ func main() { //nolint:cyclop,funlen log.SetReportCaller(true) - if err := config.Load(); err != nil { + if err := internal.Init(); err != nil { log.WithError(err).Fatal("error loading config") } @@ -58,8 +59,6 @@ func main() { //nolint:cyclop,funlen log.SetLevel(logLevel) - log.Infof("Loaded config:\n%s\n", config.String()) - if err := config.Check(); err != nil { log.WithError(err).Fatal("error checking config") } @@ -80,7 +79,10 @@ func main() { //nolint:cyclop,funlen case "list-configurations": applicationAPI.ListConfigurations(ctx) case "patch-cluster": - applicationAPI.PatchClusterDeployment(ctx) + err = applicationAPI.PatchClusterDeployment(ctx) + if err != nil { + log.WithError(err).Fatal() + } case "adhoc": if len(*config.Get().CliArgs.AdhocCommand) == 0 { log.Fatal("add -adhoc.command argument") @@ -123,6 +125,7 @@ func getInterruptionContext() context.Context { go func() { <-c + log.Warn("Received interruption signal, stopping...") cancel() // wait 5s for graceful shutdown diff --git a/e2e/configs/full.yaml b/e2e/configs/full.yaml new file mode 100644 index 0000000..3856794 --- /dev/null +++ b/e2e/configs/full.yaml @@ -0,0 +1,124 @@ +clusterName: k8s +serverComponents: + ubuntu: + version: ubuntu-20.04 + username: hcloud-user + architecture: x86 + kubernetes: + version: 1.28.2 + docker: + version: 5:24.0.6-1~ubuntu.20.04~focal + containerd: + version: 1.6.24-1 + pausecontainer: registry.k8s.io/pause:3.2 +ipRange: 10.0.0.0/16 +ipRangeSubnet: 10.0.0.0/16 +masterCount: 3 +networkZone: eu-central +location: fsn1 +datacenter: fsn1-dc14 +masterServers: + namepattern: master-%d + placementgroupname: master-placement-group + servertype: cx21 + labels: + role: master + waittimeinretry: 3s + retrytimelimit: 20 + serversinitparams: + targz: https://github.com/maksim-paskal/hcloud-k8s-ctl/archive/refs/heads/main.tar.gz + folder: hcloud-k8s-ctl-main +masterLoadBalancer: + loadbalancertype: lb11 + listenport: 6443 + destinationport: 6443 +cliArgs: + loglevel: DEBUG + configpath: config.yaml + saveconfigpath: ./e2e/configs/full.yaml + action: save-full-config + adhoccommand: "" + adhoccopynewfile: false + adhocmasters: false + adhocworkers: true + adhocuser: "" + upgradecontrolplaneversion: "" + createfirewallcontrolplane: false + createfirewallworkers: false +deployments: {} +preStartScript: "" +postStartScript: "" +cluster-autoscaler: + autoscalingGroups: + - name: CX21:FSN1:cx21-fsn1 + minSize: 1 + maxSize: 20 + - name: CPX21:FSN1:cpx21-fsn1 + minSize: 1 + maxSize: 20 + - name: CX31:FSN1:cx31-fsn1 + minSize: 1 + maxSize: 20 + - name: CPX31:FSN1:cpx31-fsn1 + minSize: 1 + maxSize: 20 + - name: CX41:FSN1:cx41-fsn1 + minSize: 1 + maxSize: 20 + - name: CPX41:FSN1:cpx41-fsn1 + minSize: 1 + maxSize: 20 + - name: CX51:FSN1:cx51-fsn1 + minSize: 1 + maxSize: 20 + - name: CPX51:FSN1:cpx51-fsn1 + minSize: 1 + maxSize: 20 + - name: CX21:NBG1:cx21-nbg1 + minSize: 1 + maxSize: 20 + - name: CPX21:NBG1:cpx21-nbg1 + minSize: 1 + maxSize: 20 + - name: CX31:NBG1:cx31-nbg1 + minSize: 1 + maxSize: 20 + - name: CPX31:NBG1:cpx31-nbg1 + minSize: 1 + maxSize: 20 + - name: CX41:NBG1:cx41-nbg1 + minSize: 1 + maxSize: 20 + - name: CPX41:NBG1:cpx41-nbg1 + minSize: 1 + maxSize: 20 + - name: CX51:NBG1:cx51-nbg1 + minSize: 1 + maxSize: 20 + - name: CPX51:NBG1:cpx51-nbg1 + minSize: 1 + maxSize: 20 + - name: CX21:HEL1:cx21-hel1 + minSize: 1 + maxSize: 20 + - name: CPX21:HEL1:cpx21-hel1 + minSize: 1 + maxSize: 20 + - name: CX31:HEL1:cx31-hel1 + minSize: 1 + maxSize: 20 + - name: CPX31:HEL1:cpx31-hel1 + minSize: 1 + maxSize: 20 + - name: CX41:HEL1:cx41-hel1 + minSize: 1 + maxSize: 20 + - name: CPX41:HEL1:cpx41-hel1 + minSize: 1 + maxSize: 20 + - name: CX51:HEL1:cx51-hel1 + minSize: 1 + maxSize: 20 + - name: CPX51:HEL1:cpx51-hel1 + minSize: 1 + maxSize: 20 diff --git a/e2e/configs/v1.25.yaml b/e2e/configs/v1.25.yaml new file mode 100644 index 0000000..d9acd19 --- /dev/null +++ b/e2e/configs/v1.25.yaml @@ -0,0 +1,30 @@ +# Kubernetes v1.25 in Europe +ipRange: "10.0.0.0/16" +masterCount: 3 +serverComponents: + kubernetes: + version: 1.25.14 + docker: + version: 5:24.0.6-1~ubuntu.20.04~focal + containerd: + version: 1.6.24-1 +cluster-autoscaler: + replicaCount: 3 + resources: + requests: + cpu: 100m + memory: 300Mi +preStartScript: | + # add some custom cron job on node + crontab < /etc/containerd/certs.d/some-registry.io/hosts.toml < 0 && + c.PodNotReady == 0 && + c.PVBound > 0 && + c.PVNotBound == 0 && + c.NodeRunning > 0 && + c.NodeNotRunning == 0 && + c.LoadBalancerIngress == 3 // hloud allocate 3 ip addresses for load balancer +} + +func (c clusterResults) String() string { + b, err := json.Marshal(c) + if err != nil { + return err.Error() + } + + return string(b) +} + +// test kubernetes cluster +// need all pods to be ready +// need all PVs to be bound +// need all nodes to be running +// need all load balancers have 3 ingress ips. +func testKubernetesCluster(ctx context.Context, t *testing.T, kubeconfig string) error { //nolint:funlen,gocognit,cyclop,lll + t.Helper() + + t.Logf("Using kubeconfig: %s", kubeconfig) + + restconfig, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + return errors.Wrap(err, "error in clientcmd.BuildConfigFromFlags") + } + + clientset, err := kubernetes.NewForConfig(restconfig) + if err != nil { + return errors.Wrap(err, "error in kubernetes.NewForConfig") + } + + for ctx.Err() == nil { + result := clusterResults{} + + pods, err := clientset.CoreV1().Pods("").List(ctx, metav1.ListOptions{}) + if err != nil { + return errors.Wrap(err, "error in clientset.CoreV1().Pods") + } + + for _, pod := range pods.Items { + if pod.Status.Phase != corev1.PodRunning { + result.PodNotReady++ + } else { + result.PodReady++ + } + } + + pvs, err := clientset.CoreV1().PersistentVolumes().List(ctx, metav1.ListOptions{}) + if err != nil { + return errors.Wrap(err, "error in clientset.CoreV1().PersistentVolumes") + } + + for _, pv := range pvs.Items { + if pv.Status.Phase != corev1.VolumeBound { + result.PVNotBound++ + } else { + result.PVBound++ + } + } + + nodes, err := clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) + if err != nil { + return errors.Wrap(err, "error in clientset.CoreV1().Pods") + } + + for _, node := range nodes.Items { + nodeReady := false + + for _, condition := range node.Status.Conditions { + if condition.Type == corev1.NodeReady && condition.Status == corev1.ConditionTrue { + nodeReady = true + + break + } + } + + if nodeReady { + result.NodeRunning++ + } else { + result.NodeNotRunning++ + } + } + + services, err := clientset.CoreV1().Services("").List(ctx, metav1.ListOptions{}) + if err != nil { + return errors.Wrap(err, "error in clientset.CoreV1().Services") + } + + for _, service := range services.Items { + if service.Spec.Type == corev1.ServiceTypeLoadBalancer { + result.LoadBalancerIngress += len(service.Status.LoadBalancer.Ingress) + } + } + + t.Log(result.String()) + + if validate := result.Validate(); validate { + t.Log("Cluster is OK") + + break + } + + utils.SleepContext(ctx, 10*time.Second) + } + + return nil +} diff --git a/examples/charts/test/templates/test-deployment.yaml b/examples/charts/test/templates/test-deployment.yaml index 2bd1014..47e9343 100644 --- a/examples/charts/test/templates/test-deployment.yaml +++ b/examples/charts/test/templates/test-deployment.yaml @@ -19,16 +19,16 @@ metadata: spec: selector: matchLabels: - app: {{ $.Release.Name }} + app: {{ $.Release.Name }}-{{ $location }} replicas: 1 template: metadata: labels: - app: {{ $.Release.Name }} + app: {{ $.Release.Name }}-{{ $location }} spec: nodeSelector: topology.kubernetes.io/region: {{ $location }} - beta.kubernetes.io/instance-type: cx21 + node.kubernetes.io/instance-type: cx21 volumes: - name: my-csi-volume persistentVolumeClaim: @@ -38,6 +38,7 @@ spec: image: nginx:1.14.2 resources: limits: + cpu: 100m memory: 100Mi ports: - containerPort: 80 diff --git a/examples/charts/test/templates/test-hpa.yaml b/examples/charts/test/templates/test-hpa.yaml new file mode 100644 index 0000000..e07ff52 --- /dev/null +++ b/examples/charts/test/templates/test-hpa.yaml @@ -0,0 +1,15 @@ +{{ range $idx, $location := .Values.locations }} +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ $.Release.Name }}-{{ $location }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ $.Release.Name }}-{{ $location }} + minReplicas: 1 + maxReplicas: 10 + targetCPUUtilizationPercentage: 50 +{{ end }} \ No newline at end of file diff --git a/examples/config-full.yaml b/examples/config-full.yaml deleted file mode 100644 index 025a4b6..0000000 --- a/examples/config-full.yaml +++ /dev/null @@ -1,109 +0,0 @@ -clusterName: k8s -kubeConfigPath: /Users/mpascal/.kube/hcloud -hetznerToken: - main: - csi: "" - ccm: "" -serverComponents: - ubuntu: - version: ubuntu-20.04 - username: hcloud-user - architecture: x86 - kubernetes: - version: 1.24.9 - docker: - version: 5:20.10.17~3-0~ubuntu-focal - containerd: - version: 1.6.6-1 - pausecontainer: k8s.gcr.io/pause:3.2 -ipRange: 10.0.0.0/16 -ipRangeSubnet: 10.0.0.0/16 -sshPrivateKey: /Users/mpascal/.ssh/id_rsa -sshPublicKey: /Users/mpascal/.ssh/id_rsa.pub -masterCount: 3 -networkZone: eu-central -location: fsn1 -datacenter: fsn1-dc14 -masterServers: - namepattern: master-%d - placementgroupname: master-placement-group - servertype: cx21 - labels: - role: master - waittimeinretry: 3s - retrytimelimit: 20 - serversinitparams: - targz: https://github.com/maksim-paskal/hcloud-k8s-ctl/archive/refs/heads/main.tar.gz - folder: hcloud-k8s-ctl-main -masterLoadBalancer: - loadbalancertype: lb11 - listenport: 6443 - destinationport: 6443 -cliArgs: - loglevel: DEBUG - configpath: config.yaml - saveconfigpath: ./examples/config-full.yaml - action: save-full-config - adhoccommand: "" - adhoccopynewfile: false - adhocmasters: false - adhocworkers: true - adhocuser: "" - upgradecontrolplaneversion: "" - createfirewallcontrolplane: false - createfirewallworkers: false -deployments: {} -autoscaler: - enabled: true - image: docker.io/paskalmaksim/cluster-autoscaler-amd64:fc3aefc3d - args: - - --v=4 - - --cloud-provider=hetzner - - --stderrthreshold=info - - --expander=least-waste - - --scale-down-enabled=true - - --skip-nodes-with-local-storage=false - - --skip-nodes-with-system-pods=false - - --scale-down-utilization-threshold=0.8 - defaults: - min: 0 - max: 20 - workers: - - location: fsn1 - min: 0 - max: 0 - type: - - cx21 - - cpx21 - - cx31 - - cpx31 - - cx41 - - cpx41 - - cx51 - - cpx51 - - location: nbg1 - min: 0 - max: 0 - type: - - cx21 - - cpx21 - - cx31 - - cpx31 - - cx41 - - cpx41 - - cx51 - - cpx51 - - location: hel1 - min: 0 - max: 0 - type: - - cx21 - - cpx21 - - cx31 - - cpx31 - - cx41 - - cpx41 - - cx51 - - cpx51 -preStartScript: "" -postStartScript: "" diff --git a/go.mod b/go.mod index e564d1e..5d95dda 100644 --- a/go.mod +++ b/go.mod @@ -8,19 +8,53 @@ require ( github.com/sirupsen/logrus v1.9.3 golang.org/x/crypto v0.14.0 gopkg.in/yaml.v3 v3.0.1 + k8s.io/api v0.28.3 + k8s.io/apimachinery v0.28.3 + k8s.io/client-go v0.28.3 ) require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/imdario/mergo v0.3.6 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/prometheus/client_golang v1.17.0 // indirect github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.11.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect + golang.org/x/time v0.3.0 // indirect + google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect + k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 7b1a302..1471438 100644 --- a/go.sum +++ b/go.sum @@ -2,24 +2,74 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hetznercloud/hcloud-go/v2 v2.4.0 h1:MqlAE+w125PLvJRCpAJmEwrIxoVdUdOyuFUhE/Ukbok= github.com/hetznercloud/hcloud-go/v2 v2.4.0/go.mod h1:l7fA5xsncFBzQTyw29/dw5Yr88yEGKKdc6BHf24ONS0= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +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.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= +github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM= +github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -36,23 +86,67 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y= +golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= @@ -60,6 +154,29 @@ google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= +k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= +k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A= +k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8= +k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= +k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= +k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/internal/internal.go b/internal/internal.go new file mode 100644 index 0000000..9feffc6 --- /dev/null +++ b/internal/internal.go @@ -0,0 +1,33 @@ +/* +Copyright paskal.maksim@gmail.com +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 internal + +import ( + "github.com/maksim-paskal/hcloud-k8s-ctl/pkg/config" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" +) + +func Init() error { + if err := config.Load(); err != nil { + return errors.Wrap(err, "error loading config") + } + + log.Infof("Loaded config:\n%s\n", config.String()) + + if err := config.Check(); err != nil { + return errors.Wrap(err, "error checking config") + } + + return nil +} diff --git a/pkg/api/api.go b/pkg/api/api.go index f1e8821..95b95c4 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -20,10 +20,11 @@ import ( "net" "os" "sync" - "time" "github.com/hetznercloud/hcloud-go/v2/hcloud" "github.com/maksim-paskal/hcloud-k8s-ctl/pkg/config" + "github.com/maksim-paskal/hcloud-k8s-ctl/pkg/drain" + "github.com/maksim-paskal/hcloud-k8s-ctl/pkg/utils" "github.com/pkg/errors" log "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" @@ -48,7 +49,7 @@ func NewApplicationAPI(ctx context.Context) (*ApplicationAPI, error) { log.Info("Connecting to Hetzner Cloud API...") api := ApplicationAPI{ - hcloudClient: hcloud.NewClient(hcloud.WithToken(config.Get().HetznerToken.Main)), + hcloudClient: hcloud.NewClient(hcloud.WithToken(config.Get().HetznerToken)), } if err := api.validateConfig(ctx); err != nil { @@ -192,11 +193,11 @@ func (api *ApplicationAPI) joinToMasterNodes(ctx context.Context, server string) } if retryCount > 0 { - time.Sleep(config.Get().MasterServers.WaitTimeInRetry) + utils.SleepContext(ctx, config.Get().MasterServers.WaitTimeInRetry) } retryCount++ - log.Infof("Waiting for server... try=%d", retryCount) + log.Infof("Waiting for server... try=%03d", retryCount) serverIP, err := api.waitForServer(ctx, server) if err != nil { @@ -240,11 +241,11 @@ func (api *ApplicationAPI) postInstall(ctx context.Context, copyNewScripts bool) } if retryCount > 0 { - time.Sleep(config.Get().MasterServers.WaitTimeInRetry) + utils.SleepContext(ctx, config.Get().MasterServers.WaitTimeInRetry) } retryCount++ - log.Infof("Waiting for master node... try=%d", retryCount) + log.Infof("Waiting for master node... try=%03d", retryCount) serverIP, err := api.waitForServer(ctx, serverName) if err != nil { @@ -305,11 +306,11 @@ func (api *ApplicationAPI) initFirstMasterNode(ctx context.Context) error { //no } if retryCount > 0 { - time.Sleep(config.Get().MasterServers.WaitTimeInRetry) + utils.SleepContext(ctx, config.Get().MasterServers.WaitTimeInRetry) } retryCount++ - log.Infof("Waiting for master node... try=%d", retryCount) + log.Infof("Waiting for master node... try=%03d", retryCount) serverIP, err := api.waitForServer(ctx, serverName) if err != nil { @@ -531,7 +532,7 @@ func (api *ApplicationAPI) createServer(ctx context.Context) error { //nolint:fu err = api.attachToBalancer(ctx, serverResults, k8sLoadBalancer) if err != nil { log.WithError(err).Debug() - time.Sleep(config.Get().MasterServers.WaitTimeInRetry) + utils.SleepContext(ctx, config.Get().MasterServers.WaitTimeInRetry) continue } @@ -658,78 +659,13 @@ func (api *ApplicationAPI) NewCluster(ctx context.Context) error { //nolint:cycl return nil } -func (api *ApplicationAPI) DeleteCluster(ctx context.Context) { //nolint: cyclop,funlen - log.Info("Deleting cluster...") +func (api *ApplicationAPI) DeleteCluster(ctx context.Context) { + drainer := drain.NewClusterDrainer(api.hcloudClient) - k8sNetwork, _, _ := api.hcloudClient.Network.Get(ctx, config.Get().ClusterName) - if k8sNetwork != nil { - _, err := api.hcloudClient.Network.Delete(ctx, k8sNetwork) - if err != nil { - log.WithError(err).Warn("error deleting Network") - } - } - - k8sSSHKey, _, _ := api.hcloudClient.SSHKey.Get(ctx, config.Get().ClusterName) - if k8sSSHKey != nil { - _, err := api.hcloudClient.SSHKey.Delete(ctx, k8sSSHKey) - if err != nil { - log.WithError(err).Warn("error deleting SSHKey") - } - } - - k8sLoadBalancer, _, _ := api.hcloudClient.LoadBalancer.Get(ctx, config.Get().ClusterName) - if k8sLoadBalancer != nil { - _, err := api.hcloudClient.LoadBalancer.Delete(ctx, k8sLoadBalancer) - if err != nil { - log.WithError(err).Warn("error deleting LoadBalancer") - } - } - - // get master nodes - allServers, _, _ := api.hcloudClient.Server.List(ctx, hcloud.ServerListOpts{ - ListOpts: hcloud.ListOpts{ - LabelSelector: api.getMasterLabels(), - }, - }) - - // get worker nodes - nodeServers, _, _ := api.hcloudClient.Server.List(ctx, hcloud.ServerListOpts{ - ListOpts: hcloud.ListOpts{ - LabelSelector: nodeGroupSelector, - }, - }) - - allServers = append(allServers, nodeServers...) + drainer.MasterSelector = api.getMasterLabels() + drainer.NodeGroupSelector = nodeGroupSelector - for _, nodeServer := range allServers { - _, _, err := api.hcloudClient.Server.DeleteWithResult(ctx, nodeServer) - if err != nil { - log.WithError(err).Warnf("error deleting Server=%s", nodeServer.Name) - } - } - - placementGroup, _, _ := api.hcloudClient.PlacementGroup.Get(ctx, config.Get().MasterServers.PlacementGroupName) - if placementGroup != nil { - _, err := api.hcloudClient.PlacementGroup.Delete(ctx, placementGroup) - if err != nil { - log.WithError(err).Warnf("error deleting PlacementGroup=%s", placementGroup.Name) - } - } - - log.Debug("Wait some time for deleting firewall...") - time.Sleep(3 * time.Second) //nolint:gomnd - - k8sFirewalls, _, _ := api.hcloudClient.Firewall.List(ctx, hcloud.FirewallListOpts{ - ListOpts: hcloud.ListOpts{ - LabelSelector: "cluster=" + config.Get().ClusterName, - }, - }) - for _, k8sFirewall := range k8sFirewalls { - _, err := api.hcloudClient.Firewall.Delete(ctx, k8sFirewall) - if err != nil { - log.WithError(err).Warn("error deleting Firewall") - } - } + drainer.DeleteCluster(ctx) } func (api *ApplicationAPI) execCommand(ipAddress string, command string) (string, string, error) { @@ -848,12 +784,14 @@ func (api *ApplicationAPI) getDeploymentValues() string { return base64.StdEncoding.EncodeToString(resultYAML) } -func (api *ApplicationAPI) PatchClusterDeployment(ctx context.Context) { +func (api *ApplicationAPI) PatchClusterDeployment(ctx context.Context) error { if err := api.postInstall(ctx, true); err != nil { - log.Fatal(err) + return errors.Wrap(err, "error in patching cluster") } log.Info("Cluster pached!") + + return nil } func (api *ApplicationAPI) ExecuteAdHoc(ctx context.Context, user string, command string, runOnMasters bool, runOnWorkers bool, copyNewScripts bool) { //nolint:funlen,lll,cyclop diff --git a/pkg/config/config.go b/pkg/config/config.go index a60ea75..202c37d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -20,6 +20,7 @@ import ( "os" "os/user" "path/filepath" + "regexp" "strings" "time" @@ -59,31 +60,6 @@ type masterServers struct { ServersInitParams masterServersInitParams } -type autoscalerWorker struct { - Location string - Min int - Max int - Type []string -} - -type autoscalerDefaults struct { - Min int - Max int -} -type autoscaler struct { - Enabled bool - Image string - Args []string - Defaults autoscalerDefaults - Workers []autoscalerWorker -} - -type hetznerToken struct { - Main string - Csi string - Ccm string -} - type emptyStruct struct{} type masterLoadBalancer struct { @@ -115,13 +91,57 @@ type serverComponents struct { Containerd serverComponentContainerd } +type clusterAutoscalingGroup struct { + Name string `yaml:"name"` + MinSize int `yaml:"minSize"` + MaxSize int `yaml:"maxSize"` +} + +func getDefaultMasterServersInitParams(branch string) masterServersInitParams { + return masterServersInitParams{ + TarGz: fmt.Sprintf("https://github.com/maksim-paskal/hcloud-k8s-ctl/archive/refs/heads/%s.tar.gz", branch), + Folder: fmt.Sprintf("hcloud-k8s-ctl-%s", branch), + } +} + +func getDefaultClusterAutoscaler() map[interface{}]interface{} { + result := make([]*clusterAutoscalingGroup, 0) + + defaultLocations := []string{ + hcloudLocationEUFalkenstein, + hcloudLocationEUNuremberg, + hcloudLocationEUHelsinki, + } + defaultServers := strings.Split(defaultAutoscalerInstances, ",") + + for _, location := range defaultLocations { + for _, server := range defaultServers { + result = append(result, &clusterAutoscalingGroup{ + Name: fmt.Sprintf( + "%s:%s:%s-%s", + strings.ToUpper(server), + strings.ToUpper(location), + server, + location, + ), + MinSize: 1, + MaxSize: workersCount, + }) + } + } + + return map[interface{}]interface{}{ + "autoscalingGroups": result, + } +} + //nolint:gochecknoglobals var config = Type{} type Type struct { ClusterName string `yaml:"clusterName"` KubeConfigPath string `yaml:"kubeConfigPath"` - HetznerToken hetznerToken `yaml:"hetznerToken"` + HetznerToken string `yaml:"hetznerToken"` ServerComponents serverComponents `yaml:"serverComponents"` IPRange string `yaml:"ipRange"` IPRangeSubnet string `yaml:"ipRangeSubnet"` @@ -135,9 +155,17 @@ type Type struct { MasterLoadBalancer masterLoadBalancer `yaml:"masterLoadBalancer"` CliArgs cliArgs `yaml:"cliArgs"` Deployments interface{} `yaml:"deployments"` // values.yaml in chart - Autoscaler autoscaler `yaml:"autoscaler"` PreStartScript string `yaml:"preStartScript"` PostStartScript string `yaml:"postStartScript"` + + // helm dependencies values.yaml + ClusterAutoscaler map[interface{}]interface{} `yaml:"cluster-autoscaler,omitempty"` //nolint:tagliatelle,lll + NfsSubdirExternalProvisioner map[interface{}]interface{} `yaml:"nfs-subdir-external-provisioner,omitempty"` //nolint:tagliatelle,lll + KubeletCSRApprover map[interface{}]interface{} `yaml:"kubelet-csr-approver,omitempty"` //nolint:tagliatelle,lll + HCloudCSI map[interface{}]interface{} `yaml:"hcloud-csi,omitempty"` //nolint:tagliatelle,lll + HCloudCCM map[interface{}]interface{} `yaml:"hcloud-cloud-controller-manager,omitempty"` //nolint:tagliatelle,lll + MetricsServer map[interface{}]interface{} `yaml:"metrics-server,omitempty"` //nolint:tagliatelle,lll + Flannel map[interface{}]interface{} `yaml:"flannel,omitempty"` //nolint:tagliatelle,lll } //nolint:gochecknoglobals @@ -156,7 +184,11 @@ var cliArguments = cliArgs{ CreateFirewallWorkers: flag.Bool("create-firewall.workers", false, "create firewall for workers"), } -func defaultConfig() Type { //nolint:funlen +func SetBranch(branch string) { + config.MasterServers.ServersInitParams = getDefaultMasterServersInitParams(branch) +} + +func defaultConfig() Type { privateKey := "~/.ssh/id_rsa" kubeConfigPath := "~/.kube/hcloud" @@ -164,9 +196,7 @@ func defaultConfig() Type { //nolint:funlen serverLabels["role"] = "master" return Type{ - HetznerToken: hetznerToken{ - Main: os.Getenv("HCLOUD_TOKEN"), - }, + HetznerToken: os.Getenv("HCLOUD_TOKEN"), ServerComponents: serverComponents{ Ubuntu: serverComponentUbuntu{ Version: "ubuntu-20.04", @@ -174,13 +204,13 @@ func defaultConfig() Type { //nolint:funlen Architecture: hcloud.ArchitectureX86, // x86 or arm }, Kubernetes: serverComponentKubernetes{ - Version: "1.24.9", + Version: "1.28.2", }, Docker: serverComponentDocker{ - Version: "5:20.10.17~3-0~ubuntu-focal", + Version: "5:24.0.6-1~ubuntu.20.04~focal", }, Containerd: serverComponentContainerd{ - Version: "1.6.6-1", + Version: "1.6.24-1", PauseContainer: "registry.k8s.io/pause:3.2", }, }, @@ -202,55 +232,15 @@ func defaultConfig() Type { //nolint:funlen Labels: serverLabels, WaitTimeInRetry: waitTimeInRetry, RetryTimeLimit: retryTimeLimit, - ServersInitParams: masterServersInitParams{ - TarGz: "https://github.com/maksim-paskal/hcloud-k8s-ctl/archive/refs/heads/main.tar.gz", - Folder: "hcloud-k8s-ctl-main", - }, + ServersInitParams: getDefaultMasterServersInitParams("main"), }, MasterLoadBalancer: masterLoadBalancer{ LoadBalancerType: "lb11", ListenPort: loadBalancerDefaultPort, DestinationPort: loadBalancerDefaultPort, }, - Deployments: emptyStruct{}, - Autoscaler: autoscaler{ - Enabled: true, - Image: "docker.io/paskalmaksim/cluster-autoscaler-amd64:fc3aefc3d", - Args: []string{ - "--v=4", - "--cloud-provider=hetzner", - "--stderrthreshold=info", - "--expander=least-waste", - "--scale-down-enabled=true", - "--skip-nodes-with-local-storage=false", - "--skip-nodes-with-system-pods=false", - "--scale-down-utilization-threshold=0.8", - }, - Defaults: autoscalerDefaults{ - Min: 0, - Max: workersCount, - }, - Workers: []autoscalerWorker{ - { - Location: hcloudLocationEUFalkenstein, - Min: 0, - Max: 0, - Type: strings.Split(defaultAutoscalerInstances, ","), - }, - { - Location: hcloudLocationEUNuremberg, - Min: 0, - Max: 0, - Type: strings.Split(defaultAutoscalerInstances, ","), - }, - { - Location: hcloudLocationEUHelsinki, - Min: 0, - Max: 0, - Type: strings.Split(defaultAutoscalerInstances, ","), - }, - }, - }, + Deployments: emptyStruct{}, + ClusterAutoscaler: getDefaultClusterAutoscaler(), } } @@ -262,12 +252,12 @@ func Load() error { //nolint: cyclop config = defaultConfig() - if len(config.HetznerToken.Main) == 0 { + if len(config.HetznerToken) == 0 { auth, err := os.ReadFile(".hcloudauth") if err != nil { log.Debug(err) } else { - config.HetznerToken.Main = string(auth) + config.HetznerToken = string(auth) } } @@ -309,7 +299,7 @@ func Load() error { //nolint: cyclop } func Check() error { - if len(config.HetznerToken.Main) == 0 { + if len(config.HetznerToken) == 0 { return errNoHetznerToken } @@ -325,19 +315,21 @@ func hideSensitiveData(out []byte, sensitive string) []byte { return out } - return bytes.ReplaceAll(out, []byte(sensitive), []byte("")) + return bytes.ReplaceAll(out, []byte(sensitive), []byte(secretString)) } func String() string { - out, err := yaml.Marshal(config) - if err != nil { - return fmt.Sprintf("ERROR: %t", err) + var b bytes.Buffer + yamlEncoder := yaml.NewEncoder(&b) + yamlEncoder.SetIndent(2) //nolint:gomnd + + if err := yamlEncoder.Encode(&config); err != nil { + return fmt.Sprintf("ERROR %s", err) } + out := b.Bytes() // remove sensitive data - out = hideSensitiveData(out, config.HetznerToken.Main) - out = hideSensitiveData(out, config.HetznerToken.Ccm) - out = hideSensitiveData(out, config.HetznerToken.Csi) + out = hideSensitiveData(out, config.HetznerToken) return string(out) } @@ -364,14 +356,13 @@ func envDefault(name string, defaultValue string) string { } func SaveConfig(filePath string) error { - configByte, err := yaml.Marshal(config) - if err != nil { - return errors.Wrap(err, "failed to marshal config") - } - const configPermissions = 0o600 - err = os.WriteFile(filePath, hideSensitiveData(configByte, config.HetznerToken.Main), configPermissions) + re := regexp.MustCompile("(?m)[\r\n]+^.*(kubeConfigPath|hetznerToken|sshPrivateKey|sshPublicKey).*$") + + content := re.ReplaceAllString(String(), "") + + err := os.WriteFile(filePath, []byte(content), configPermissions) if err != nil { return errors.Wrap(err, "failed to write config file") } diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 97d5161..593d61d 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -38,8 +38,8 @@ func TestConfig(t *testing.T) { t.Fatal("MasterCount != 33") } - if config.Get().HetznerToken.Main != "sometoken" { - t.Fatal("HetznerToken.Main != sometoken") + if config.Get().HetznerToken != "sometoken" { + t.Fatal("HetznerToken != sometoken") } if strings.Contains(config.String(), "sometoken") { diff --git a/pkg/config/config_test.yaml b/pkg/config/config_test.yaml index fb00e79..d829597 100644 --- a/pkg/config/config_test.yaml +++ b/pkg/config/config_test.yaml @@ -1,5 +1,4 @@ ipRange: "11.0.0.0/16" ipRangeSubnet: "11.0.0.0/17" masterCount: 33 -hetznerToken: - main: sometoken \ No newline at end of file +hetznerToken: "sometoken" \ No newline at end of file diff --git a/pkg/config/const.go b/pkg/config/const.go index 31a8d83..ee75ff8 100644 --- a/pkg/config/const.go +++ b/pkg/config/const.go @@ -20,6 +20,7 @@ const ( loadBalancerDefaultPort = 6443 waitTimeInRetry = 3 * time.Second retryTimeLimit = 20 + secretString = "" defaultLocation = hcloudLocationEUFalkenstein defaultDatacenter = hcloudLocationEUFalkenstein + "-dc14" defaultAutoscalerInstances = "cx21,cpx21,cx31,cpx31,cx41,cpx41,cx51,cpx51" diff --git a/pkg/drain/drain.go b/pkg/drain/drain.go new file mode 100644 index 0000000..da15de0 --- /dev/null +++ b/pkg/drain/drain.go @@ -0,0 +1,238 @@ +/* +Copyright paskal.maksim@gmail.com +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 drain + +import ( + "context" + "time" + + "github.com/hetznercloud/hcloud-go/v2/hcloud" + "github.com/maksim-paskal/hcloud-k8s-ctl/pkg/config" + "github.com/maksim-paskal/hcloud-k8s-ctl/pkg/utils" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" +) + +// ClusterDrainer is a struct for drain cluster. +type ClusterDrainer struct { + hcloudClient *hcloud.Client + WaitTime time.Duration + MasterSelector string + NodeGroupSelector string +} + +func NewClusterDrainer(hcloudClient *hcloud.Client) *ClusterDrainer { + return &ClusterDrainer{ + hcloudClient: hcloudClient, + WaitTime: 3 * time.Second, //nolint:gomnd + } +} + +func (api *ClusterDrainer) DeleteCluster(ctx context.Context) { + for ctx.Err() == nil { + if err := api.processDeleteCluster(ctx); err != nil { + log.WithError(err).Error("delete cluster") + } else { + break + } + + utils.SleepContext(ctx, api.WaitTime) + } +} + +func (api *ClusterDrainer) processDeleteCluster(ctx context.Context) error { + if err := api.deleteNetworks(ctx); err != nil { + return err + } + + if err := api.deleteSSHKeys(ctx); err != nil { + return err + } + + if err := api.deleteLoadBalancer(ctx); err != nil { + return err + } + + if err := api.deleteServers(ctx); err != nil { + return err + } + + if err := api.deletePlacementGroup(ctx); err != nil { + return err + } + + if err := api.deleteFirewalls(ctx); err != nil { + return err + } + + if err := api.deleteVolumes(ctx); err != nil { + return err + } + + return nil +} + +func (api *ClusterDrainer) deleteNetworks(ctx context.Context) error { + for ctx.Err() == nil { + k8sNetwork, _, _ := api.hcloudClient.Network.Get(ctx, config.Get().ClusterName) + if k8sNetwork == nil { + return nil + } + + _, err := api.hcloudClient.Network.Delete(ctx, k8sNetwork) + if err != nil { + return errors.Wrap(err, "delete network") + } + } + + return errors.Wrap(ctx.Err(), "delete network") +} + +func (api *ClusterDrainer) deleteSSHKeys(ctx context.Context) error { + for ctx.Err() == nil { + k8sSSHKey, _, _ := api.hcloudClient.SSHKey.Get(ctx, config.Get().ClusterName) + if k8sSSHKey == nil { + return nil + } + + _, err := api.hcloudClient.SSHKey.Delete(ctx, k8sSSHKey) + if err != nil { + return errors.Wrap(err, "error deleting SSHKey") + } + } + + return errors.Wrap(ctx.Err(), "error deleting SSHKey") +} + +func (api *ClusterDrainer) deleteLoadBalancer(ctx context.Context) error { + for ctx.Err() == nil { + allBalancers := make([]*hcloud.LoadBalancer, 0) + + k8sLoadBalancer, _, _ := api.hcloudClient.LoadBalancer.Get(ctx, config.Get().ClusterName) + if k8sLoadBalancer != nil { + allBalancers = append(allBalancers, k8sLoadBalancer) + } + + loadBalancers, _, _ := api.hcloudClient.LoadBalancer.List(ctx, hcloud.LoadBalancerListOpts{ + ListOpts: hcloud.ListOpts{ + LabelSelector: "hcloud-ccm/service-uid", + }, + }) + + allBalancers = append(allBalancers, loadBalancers...) + + if len(allBalancers) == 0 { + return nil + } + + for _, loadBalancer := range allBalancers { + _, err := api.hcloudClient.LoadBalancer.Delete(ctx, loadBalancer) + if err != nil { + return errors.Wrap(err, "error deleting LoadBalancer") + } + } + } + + return errors.Wrap(ctx.Err(), "error deleting LoadBalancer") +} + +func (api *ClusterDrainer) deleteServers(ctx context.Context) error { + for ctx.Err() == nil { + // get master nodes + allServers, _, _ := api.hcloudClient.Server.List(ctx, hcloud.ServerListOpts{ + ListOpts: hcloud.ListOpts{ + LabelSelector: api.MasterSelector, + }, + }) + + // get worker nodes + nodeServers, _, _ := api.hcloudClient.Server.List(ctx, hcloud.ServerListOpts{ + ListOpts: hcloud.ListOpts{ + LabelSelector: api.NodeGroupSelector, + }, + }) + + allServers = append(allServers, nodeServers...) + + if len(allServers) == 0 { + return nil + } + + for _, nodeServer := range allServers { + _, _, err := api.hcloudClient.Server.DeleteWithResult(ctx, nodeServer) + if err != nil { + return errors.Wrapf(err, "error deleting Server=%s", nodeServer.Name) + } + } + } + + return errors.Wrap(ctx.Err(), "error deleting Server") +} + +func (api *ClusterDrainer) deletePlacementGroup(ctx context.Context) error { + for ctx.Err() == nil { + placementGroup, _, _ := api.hcloudClient.PlacementGroup.Get(ctx, config.Get().MasterServers.PlacementGroupName) + if placementGroup == nil { + return nil + } + + _, err := api.hcloudClient.PlacementGroup.Delete(ctx, placementGroup) + if err != nil { + return errors.Wrapf(err, "error deleting PlacementGroup=%s", placementGroup.Name) + } + } + + return errors.Wrap(ctx.Err(), "error deleting PlacementGroup") +} + +func (api *ClusterDrainer) deleteFirewalls(ctx context.Context) error { + for ctx.Err() == nil { + k8sFirewalls, _, _ := api.hcloudClient.Firewall.List(ctx, hcloud.FirewallListOpts{ + ListOpts: hcloud.ListOpts{ + LabelSelector: "cluster=" + config.Get().ClusterName, + }, + }) + + if len(k8sFirewalls) == 0 { + return nil + } + + for _, k8sFirewall := range k8sFirewalls { + _, err := api.hcloudClient.Firewall.Delete(ctx, k8sFirewall) + if err != nil { + return errors.Wrap(err, "error deleting Firewall") + } + } + } + + return errors.Wrap(ctx.Err(), "error deleting Firewall") +} + +func (api *ClusterDrainer) deleteVolumes(ctx context.Context) error { + for ctx.Err() == nil { + k8sVolumes, _, _ := api.hcloudClient.Volume.List(ctx, hcloud.VolumeListOpts{}) + + if len(k8sVolumes) == 0 { + return nil + } + + for _, k8sVolume := range k8sVolumes { + _, err := api.hcloudClient.Volume.Delete(ctx, k8sVolume) + if err != nil { + return errors.Wrap(err, "error deleting Volume") + } + } + } + + return errors.Wrap(ctx.Err(), "error deleting Volume") +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go new file mode 100644 index 0000000..861156b --- /dev/null +++ b/pkg/utils/utils.go @@ -0,0 +1,27 @@ +/* +Copyright paskal.maksim@gmail.com +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 utils + +import ( + "context" + "time" +) + +func SleepContext(ctx context.Context, duration time.Duration) { + select { + case <-ctx.Done(): + return + case <-time.After(duration): + return + } +} diff --git a/scripts/chart/Chart.lock b/scripts/chart/Chart.lock index 258c516..371ee0b 100644 --- a/scripts/chart/Chart.lock +++ b/scripts/chart/Chart.lock @@ -5,5 +5,20 @@ dependencies: - name: kubelet-csr-approver repository: https://postfinance.github.io/kubelet-csr-approver version: 0.2.3 -digest: sha256:d3e1e2d0a31bbf246e1a89028ef9437c34a1f04c714fcb43276a86e9fe216a9f -generated: "2022-07-16T08:50:22.897843+03:00" +- name: hcloud-csi + repository: https://charts.hetzner.cloud + version: 2.5.1 +- name: hcloud-cloud-controller-manager + repository: https://charts.hetzner.cloud + version: v1.18.0 +- name: cluster-autoscaler + repository: https://kubernetes.github.io/autoscaler + version: 9.29.4 +- name: metrics-server + repository: https://kubernetes-sigs.github.io/metrics-server + version: 3.11.0 +- name: flannel + repository: https://flannel-io.github.io/flannel + version: v0.22.3 +digest: sha256:22d3a2e924881aeba8f75be376e266aff23ed20f5c57a0bbf1e4e9829c741bb0 +generated: "2023-11-02T15:18:05.426271Z" diff --git a/scripts/chart/Chart.yaml b/scripts/chart/Chart.yaml index 5d770eb..91e7f60 100644 --- a/scripts/chart/Chart.yaml +++ b/scripts/chart/Chart.yaml @@ -8,4 +8,19 @@ dependencies: condition: deployments.nfs.nfs-subdir-external-provisioner.enabled - name: kubelet-csr-approver version: "0.2.3" - repository: https://postfinance.github.io/kubelet-csr-approver \ No newline at end of file + repository: https://postfinance.github.io/kubelet-csr-approver +- name: hcloud-csi + version: "2.5.1" + repository: https://charts.hetzner.cloud +- name: hcloud-cloud-controller-manager + version: "v1.18.0" + repository: https://charts.hetzner.cloud +- name: cluster-autoscaler + version: "9.29.4" + repository: https://kubernetes.github.io/autoscaler +- name: metrics-server + version: "3.11.0" + repository: https://kubernetes-sigs.github.io/metrics-server +- name: flannel + version: "v0.22.3" + repository: https://flannel-io.github.io/flannel \ No newline at end of file diff --git a/scripts/chart/templates/autoscaler.yaml b/scripts/chart/templates/autoscaler.yaml deleted file mode 100644 index ebc30fc..0000000 --- a/scripts/chart/templates/autoscaler.yaml +++ /dev/null @@ -1,208 +0,0 @@ -{{ if .Values.autoscaler.enabled }} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - k8s-addon: cluster-autoscaler.addons.k8s.io - k8s-app: cluster-autoscaler - name: cluster-autoscaler - namespace: kube-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: cluster-autoscaler - labels: - k8s-addon: cluster-autoscaler.addons.k8s.io - k8s-app: cluster-autoscaler -rules: -- apiGroups: [""] - resources: ["events", "endpoints"] - verbs: ["create", "patch"] -- apiGroups: [""] - resources: ["pods/eviction"] - verbs: ["create"] -- apiGroups: [""] - resources: ["pods/status"] - verbs: ["update"] -- apiGroups: [""] - resources: ["endpoints"] - resourceNames: ["cluster-autoscaler"] - verbs: ["get", "update"] -- apiGroups: [""] - resources: ["nodes"] - verbs: ["watch", "list", "get", "update"] -- apiGroups: [""] - resources: - - "pods" - - "services" - - "replicationcontrollers" - - "persistentvolumeclaims" - - "persistentvolumes" - - "namespaces" - verbs: ["watch", "list", "get"] -- apiGroups: ["extensions"] - resources: ["replicasets", "daemonsets"] - verbs: ["watch", "list", "get"] -- apiGroups: ["policy"] - resources: ["poddisruptionbudgets"] - verbs: ["watch", "list"] -- apiGroups: ["apps"] - resources: ["statefulsets", "replicasets", "daemonsets"] - verbs: ["watch", "list", "get"] -- apiGroups: ["storage.k8s.io"] - resources: ["storageclasses", "csinodes","csistoragecapacities","csidrivers"] - verbs: ["watch", "list", "get"] -- apiGroups: ["batch", "extensions"] - resources: ["jobs"] - verbs: ["get", "list", "watch", "patch"] -- apiGroups: ["coordination.k8s.io"] - resources: ["leases"] - verbs: ["create"] -- apiGroups: ["coordination.k8s.io"] - resourceNames: ["cluster-autoscaler"] - resources: ["leases"] - verbs: ["get", "update"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: cluster-autoscaler - namespace: kube-system - labels: - k8s-addon: cluster-autoscaler.addons.k8s.io - k8s-app: cluster-autoscaler -rules: -- apiGroups: [""] - resources: ["configmaps"] - verbs: ["create","list","watch"] -- apiGroups: [""] - resources: ["configmaps"] - resourceNames: ["cluster-autoscaler-status", "cluster-autoscaler-priority-expander"] - verbs: ["delete", "get", "update", "watch"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: cluster-autoscaler - labels: - k8s-addon: cluster-autoscaler.addons.k8s.io - k8s-app: cluster-autoscaler -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-autoscaler -subjects: - - kind: ServiceAccount - name: cluster-autoscaler - namespace: kube-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: cluster-autoscaler - namespace: kube-system - labels: - k8s-addon: cluster-autoscaler.addons.k8s.io - k8s-app: cluster-autoscaler -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: cluster-autoscaler -subjects: - - kind: ServiceAccount - name: cluster-autoscaler - namespace: kube-system ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: cluster-autoscaler - namespace: kube-system - labels: - app: cluster-autoscaler -spec: - replicas: 2 - selector: - matchLabels: - app: cluster-autoscaler - template: - metadata: - labels: - app: cluster-autoscaler - annotations: - prometheus.io/scrape: 'true' - prometheus.io/port: '8085' - checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }} - spec: - priorityClassName: system-cluster-critical - serviceAccountName: cluster-autoscaler - tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/master - - effect: NoSchedule - key: node-role.kubernetes.io/control-plane - affinity: - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchExpressions: - - key: app - operator: In - values: - - cluster-autoscaler - topologyKey: kubernetes.io/hostname - containers: - - name: cluster-autoscaler - image: {{ .Values.autoscaler.image | quote }} - imagePullPolicy: IfNotPresent - resources: - requests: - cpu: 100m - memory: 300Mi - command: - - ./cluster-autoscaler -{{ tpl (toYaml .Values.autoscaler.args) . | indent 8 }} - {{- range $i, $worker := .Values.autoscaler.workers }} - {{- range $y, $type := $worker.type }} - - --nodes={{ default $.Values.autoscaler.defaults.min $worker.min }}:{{ default $.Values.autoscaler.defaults.max $worker.max }}:{{ upper $type }}:{{ upper (tpl (default $.Values.location $worker.location) $) }}:{{ $type }}-{{ lower (tpl (default $.Values.location $worker.location) $) }} - {{- end }} - {{- end }} - env: - - name: HCLOUD_IMAGE - value: {{ .Values.serverComponents.ubuntu.version }} - - name: HCLOUD_TOKEN - valueFrom: - secretKeyRef: - name: hcloud - key: token - - name: HCLOUD_CLOUD_INIT - valueFrom: - configMapKeyRef: - name: hcloud-init - key: bootstrap - - name: HCLOUD_SSH_KEY - value: {{ .Values.clusterName }} - - name: HCLOUD_NETWORK - value: {{ .Values.clusterName }} - volumeMounts: - - name: ssl-certs - mountPath: /etc/ssl/certs/ca-certificates.crt - readOnly: true - volumes: - - name: ssl-certs - hostPath: - path: "/etc/ssl/certs/ca-certificates.crt" ---- -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: cluster-autoscaler - namespace: kube-system -spec: - minAvailable: 1 - selector: - matchLabels: - app: cluster-autoscaler -{{ end }} \ No newline at end of file diff --git a/scripts/chart/templates/ccm.yaml b/scripts/chart/templates/ccm.yaml deleted file mode 100644 index ecac1cf..0000000 --- a/scripts/chart/templates/ccm.yaml +++ /dev/null @@ -1,99 +0,0 @@ -# https://github.com/hetznercloud/hcloud-cloud-controller-manager/releases/latest/download/ccm.yaml ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: cloud-controller-manager - namespace: kube-system ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: system:cloud-controller-manager -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: - - kind: ServiceAccount - name: cloud-controller-manager - namespace: kube-system ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: hcloud-cloud-controller-manager - namespace: kube-system -spec: - replicas: 2 - revisionHistoryLimit: 2 - selector: - matchLabels: - app: hcloud-cloud-controller-manager - template: - metadata: - labels: - app: hcloud-cloud-controller-manager - annotations: - prometheus.io/scrape: 'true' - prometheus.io/port: '8233' - scheduler.alpha.kubernetes.io/critical-pod: '' - checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }} - spec: - priorityClassName: system-cluster-critical - serviceAccountName: cloud-controller-manager - dnsPolicy: Default - tolerations: - # this taint is set by all kubelets running `--cloud-provider=external` - # so we should tolerate it to schedule the cloud controller manager - - key: "node.cloudprovider.kubernetes.io/uninitialized" - value: "true" - effect: "NoSchedule" - - key: "CriticalAddonsOnly" - operator: "Exists" - # cloud controller manages should be able to run on masters - - key: "node-role.kubernetes.io/master" - effect: NoSchedule - operator: Exists - - key: "node-role.kubernetes.io/control-plane" - effect: NoSchedule - operator: Exists - - key: "node.kubernetes.io/not-ready" - effect: "NoSchedule" - affinity: - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchExpressions: - - key: app - operator: In - values: - - hcloud-cloud-controller-manager - topologyKey: kubernetes.io/hostname - hostNetwork: true - containers: - - image: paskalmaksim/hcloud-cloud-controller-manager:15b5eabb - imagePullPolicy: IfNotPresent - name: hcloud-cloud-controller-manager - command: - - "/bin/hcloud-cloud-controller-manager" - - "--cloud-provider=hcloud" - - "--leader-elect=true" - - "--allow-untagged-cloud" - - "--allocate-node-cidrs=true" - - "--cluster-cidr=10.244.0.0/16" - resources: - requests: - cpu: 100m - memory: 50Mi - env: -{{ tpl (toYaml .Values.deployments.ccmconfig.env) . | indent 12 }} - - name: NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - - name: HCLOUD_TOKEN - valueFrom: - secretKeyRef: - name: hcloud-ccm - key: token diff --git a/scripts/chart/templates/config.yaml b/scripts/chart/templates/config.yaml index eb91a93..539eba6 100644 --- a/scripts/chart/templates/config.yaml +++ b/scripts/chart/templates/config.yaml @@ -5,7 +5,7 @@ metadata: name: hcloud type: Opaque data: - token: {{ .Values.hetznerToken.main | b64enc | quote }} + token: {{ .Values.hetznerToken | b64enc | quote }} --- apiVersion: v1 kind: Secret @@ -13,7 +13,7 @@ metadata: name: hcloud-csi type: Opaque data: - token: {{ default .Values.hetznerToken.main .Values.hetznerToken.csi | b64enc | quote }} + token: {{ .Values.hetznerToken | b64enc | quote }} --- apiVersion: v1 kind: Secret @@ -21,4 +21,21 @@ metadata: name: hcloud-ccm type: Opaque data: - token: {{ default .Values.hetznerToken.main .Values.hetznerToken.ccm | b64enc | quote }} \ No newline at end of file + token: {{ .Values.hetznerToken | b64enc | quote }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: hcloud-ccm-env +data: + HCLOUD_NETWORK: {{ .Values.clusterName | quote }} + HCLOUD_LOAD_BALANCERS_LOCATION: {{ lower .Values.location | quote }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cluster-autoscaler-env +data: + HCLOUD_IMAGE: {{ .Values.serverComponents.ubuntu.version | quote }} + HCLOUD_SSH_KEY: {{ .Values.clusterName | quote }} + HCLOUD_NETWORK: {{ .Values.clusterName | quote }} \ No newline at end of file diff --git a/scripts/chart/templates/hcloud-csi.yml b/scripts/chart/templates/hcloud-csi.yml deleted file mode 100644 index 5e2c27d..0000000 --- a/scripts/chart/templates/hcloud-csi.yml +++ /dev/null @@ -1,358 +0,0 @@ -# https://raw.githubusercontent.com/hetznercloud/csi-driver/v1.6.0/deploy/kubernetes/hcloud-csi.yml ---- -apiVersion: storage.k8s.io/v1 -kind: CSIDriver -metadata: - name: csi.hetzner.cloud -spec: - attachRequired: true - podInfoOnMount: true - volumeLifecycleModes: - - Persistent ---- -kind: StorageClass -apiVersion: storage.k8s.io/v1 -metadata: - namespace: kube-system - name: hcloud-volumes - annotations: - storageclass.kubernetes.io/is-default-class: "true" -provisioner: csi.hetzner.cloud -volumeBindingMode: WaitForFirstConsumer -allowVolumeExpansion: true ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: hcloud-csi - namespace: kube-system ---- -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: hcloud-csi -rules: - # attacher - - apiGroups: [""] - resources: ["persistentvolumes"] - verbs: ["get", "list", "watch", "update", "patch"] - - apiGroups: [""] - resources: ["nodes"] - verbs: ["get", "list", "watch"] - - apiGroups: ["csi.storage.k8s.io"] - resources: ["csinodeinfos"] - verbs: ["get", "list", "watch"] - - apiGroups: ["storage.k8s.io"] - resources: ["csinodes"] - verbs: ["get", "list", "watch"] - - apiGroups: ["storage.k8s.io"] - resources: ["volumeattachments"] - verbs: ["get", "list", "watch", "update", "patch"] - - apiGroups: ["storage.k8s.io"] - resources: ["volumeattachments/status"] - verbs: ["patch"] - # provisioner - - apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "list"] - - apiGroups: [""] - resources: ["persistentvolumes"] - verbs: ["get", "list", "watch", "create", "delete", "patch"] - - apiGroups: [""] - resources: ["persistentvolumeclaims", "persistentvolumeclaims/status"] - verbs: ["get", "list", "watch", "update", "patch"] - - apiGroups: ["storage.k8s.io"] - resources: ["storageclasses"] - verbs: ["get", "list", "watch"] - - apiGroups: [""] - resources: ["events"] - verbs: ["list", "watch", "create", "update", "patch"] - - apiGroups: ["snapshot.storage.k8s.io"] - resources: ["volumesnapshots"] - verbs: ["get", "list"] - - apiGroups: ["snapshot.storage.k8s.io"] - resources: ["volumesnapshotcontents"] - verbs: ["get", "list"] - # resizer - - apiGroups: [""] - resources: ["pods"] - verbs: ["get", "list", "watch"] - # node - - apiGroups: [""] - resources: ["events"] - verbs: ["get", "list", "watch", "create", "update", "patch"] ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: hcloud-csi -subjects: - - kind: ServiceAccount - name: hcloud-csi - namespace: kube-system -roleRef: - kind: ClusterRole - name: hcloud-csi - apiGroup: rbac.authorization.k8s.io ---- -kind: StatefulSet -apiVersion: apps/v1 -metadata: - name: hcloud-csi-controller - namespace: kube-system -spec: - selector: - matchLabels: - app: hcloud-csi-controller - serviceName: hcloud-csi-controller - replicas: 1 - template: - metadata: - labels: - app: hcloud-csi-controller - annotations: - prometheus.io/scrape: 'true' - prometheus.io/port: '9189' - checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }} - spec: - serviceAccount: hcloud-csi - containers: - - name: csi-attacher - image: registry.k8s.io/sig-storage/csi-attacher:v3.2.1 - volumeMounts: - - name: socket-dir - mountPath: /run/csi - securityContext: - privileged: true - capabilities: - add: ["SYS_ADMIN"] - allowPrivilegeEscalation: true - - name: csi-resizer - image: registry.k8s.io/sig-storage/csi-resizer:v1.2.0 - volumeMounts: - - name: socket-dir - mountPath: /run/csi - securityContext: - privileged: true - capabilities: - add: ["SYS_ADMIN"] - allowPrivilegeEscalation: true - - name: csi-provisioner - image: registry.k8s.io/sig-storage/csi-provisioner:v2.2.2 - args: - - --feature-gates=Topology=true - - --default-fstype=ext4 - volumeMounts: - - name: socket-dir - mountPath: /run/csi - securityContext: - privileged: true - capabilities: - add: ["SYS_ADMIN"] - allowPrivilegeEscalation: true - - name: hcloud-csi-driver - image: paskalmaksim/hcloud-csi-driver:c49f18c - command: ["/bin/hcloud-csi-driver-controller"] - imagePullPolicy: IfNotPresent - env: - - name: CSI_ENDPOINT - value: unix:///run/csi/socket - - name: METRICS_ENDPOINT - value: 0.0.0.0:9189 - - name: ENABLE_METRICS - value: "true" - - name: KUBE_NODE_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.nodeName - - name: HCLOUD_TOKEN - valueFrom: - secretKeyRef: - name: hcloud-csi - key: token - volumeMounts: - - name: socket-dir - mountPath: /run/csi - ports: - - containerPort: 9189 - name: metrics - - name: healthz - containerPort: 9808 - protocol: TCP - livenessProbe: - failureThreshold: 5 - httpGet: - path: /healthz - port: healthz - initialDelaySeconds: 10 - timeoutSeconds: 3 - periodSeconds: 2 - securityContext: - privileged: true - capabilities: - add: ["SYS_ADMIN"] - allowPrivilegeEscalation: true - - name: liveness-probe - imagePullPolicy: Always - image: registry.k8s.io/sig-storage/livenessprobe:v2.3.0 - volumeMounts: - - mountPath: /run/csi - name: socket-dir - volumes: - - name: socket-dir - emptyDir: {} ---- -kind: DaemonSet -apiVersion: apps/v1 -metadata: - name: hcloud-csi-node - namespace: kube-system - labels: - app: hcloud-csi -spec: - selector: - matchLabels: - app: hcloud-csi - template: - metadata: - labels: - app: hcloud-csi - annotations: - prometheus.io/scrape: 'true' - prometheus.io/port: '9189' - checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }} - spec: - tolerations: - - key: node-role.kubernetes.io/master - effect: NoSchedule - operator: Exists - - effect: NoSchedule - key: node-role.kubernetes.io/control-plane - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: "instance.hetzner.cloud/is-root-server" - operator: NotIn - values: - - "true" - serviceAccount: hcloud-csi - containers: - - name: csi-node-driver-registrar - image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.2.0 - args: - - --kubelet-registration-path=/var/lib/kubelet/plugins/csi.hetzner.cloud/socket - env: - - name: KUBE_NODE_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.nodeName - volumeMounts: - - name: plugin-dir - mountPath: /run/csi - - name: registration-dir - mountPath: /registration - securityContext: - privileged: true - - name: hcloud-csi-driver - image: paskalmaksim/hcloud-csi-driver:c49f18c - command: ["/bin/hcloud-csi-driver-node"] - imagePullPolicy: IfNotPresent - env: - - name: CSI_ENDPOINT - value: unix:///run/csi/socket - - name: METRICS_ENDPOINT - value: 0.0.0.0:9189 - - name: ENABLE_METRICS - value: "true" - - name: HCLOUD_TOKEN - valueFrom: - secretKeyRef: - name: hcloud-csi - key: token - - name: KUBE_NODE_NAME - valueFrom: - fieldRef: - apiVersion: v1 - fieldPath: spec.nodeName - volumeMounts: - - name: kubelet-dir - mountPath: /var/lib/kubelet - mountPropagation: "Bidirectional" - - name: plugin-dir - mountPath: /run/csi - - name: device-dir - mountPath: /dev - securityContext: - privileged: true - ports: - - containerPort: 9189 - name: metrics - - name: healthz - containerPort: 9808 - protocol: TCP - livenessProbe: - failureThreshold: 5 - httpGet: - path: /healthz - port: healthz - initialDelaySeconds: 10 - timeoutSeconds: 3 - periodSeconds: 2 - - name: liveness-probe - imagePullPolicy: Always - image: registry.k8s.io/sig-storage/livenessprobe:v2.3.0 - volumeMounts: - - mountPath: /run/csi - name: plugin-dir - volumes: - - name: kubelet-dir - hostPath: - path: /var/lib/kubelet - type: Directory - - name: plugin-dir - hostPath: - path: /var/lib/kubelet/plugins/csi.hetzner.cloud/ - type: DirectoryOrCreate - - name: registration-dir - hostPath: - path: /var/lib/kubelet/plugins_registry/ - type: Directory - - name: device-dir - hostPath: - path: /dev - type: Directory ---- -apiVersion: v1 -kind: Service -metadata: - name: hcloud-csi-controller-metrics - namespace: kube-system - labels: - app: hcloud-csi -spec: - selector: - app: hcloud-csi-controller - ports: - - port: 9189 - name: metrics - targetPort: metrics - ---- -apiVersion: v1 -kind: Service -metadata: - name: hcloud-csi-node-metrics - namespace: kube-system - labels: - app: hcloud-csi -spec: - selector: - app: hcloud-csi - ports: - - port: 9189 - name: metrics - targetPort: metrics diff --git a/scripts/chart/templates/kube-flannel.yml b/scripts/chart/templates/kube-flannel.yml deleted file mode 100644 index 322b11f..0000000 --- a/scripts/chart/templates/kube-flannel.yml +++ /dev/null @@ -1,217 +0,0 @@ ---- -kind: Namespace -apiVersion: v1 -metadata: - name: kube-flannel - labels: - pod-security.kubernetes.io/enforce: privileged ---- -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: flannel -rules: -- apiGroups: - - "" - resources: - - pods - verbs: - - get -- apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - nodes/status - verbs: - - patch -- apiGroups: - - "networking.k8s.io" - resources: - - clustercidrs - verbs: - - list - - watch ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: flannel -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: flannel -subjects: -- kind: ServiceAccount - name: flannel - namespace: kube-flannel ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: flannel - namespace: kube-flannel ---- -kind: ConfigMap -apiVersion: v1 -metadata: - name: kube-flannel-cfg - namespace: kube-flannel - labels: - tier: node - app: flannel -data: - cni-conf.json: | - { - "name": "cbr0", - "cniVersion": "0.3.1", - "plugins": [ - { - "type": "flannel", - "delegate": { - "hairpinMode": true, - "isDefaultGateway": true - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - } - ] - } - net-conf.json: | - { - "Network": "10.244.0.0/16", - "Backend": { - "Type": "vxlan" - } - } ---- -apiVersion: apps/v1 -kind: DaemonSet -metadata: - name: kube-flannel-ds - namespace: kube-flannel - labels: - tier: node - app: flannel -spec: - selector: - matchLabels: - app: flannel - template: - metadata: - labels: - tier: node - app: flannel - spec: - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/os - operator: In - values: - - linux - hostNetwork: true - priorityClassName: system-node-critical - tolerations: - - operator: Exists - effect: NoSchedule - serviceAccountName: flannel - initContainers: - - name: install-cni-plugin - image: docker.io/flannel/flannel-cni-plugin:v1.1.2 - #image: docker.io/rancher/mirrored-flannelcni-flannel-cni-plugin:v1.1.2 - command: - - cp - args: - - -f - - /flannel - - /opt/cni/bin/flannel - volumeMounts: - - name: cni-plugin - mountPath: /opt/cni/bin - - name: install-cni - image: docker.io/flannel/flannel:v0.20.2 - #image: docker.io/rancher/mirrored-flannelcni-flannel:v0.20.2 - command: - - cp - args: - - -f - - /etc/kube-flannel/cni-conf.json - - /etc/cni/net.d/10-flannel.conflist - volumeMounts: - - name: cni - mountPath: /etc/cni/net.d - - name: flannel-cfg - mountPath: /etc/kube-flannel/ - containers: - - name: kube-flannel - image: docker.io/flannel/flannel:v0.20.2 - #image: docker.io/rancher/mirrored-flannelcni-flannel:v0.20.2 - command: - - /opt/bin/flanneld - args: - - --ip-masq - - --kube-subnet-mgr - resources: - requests: - cpu: "100m" - memory: "50Mi" - limits: - cpu: "100m" - memory: "50Mi" - securityContext: - privileged: false - capabilities: - add: ["NET_ADMIN", "NET_RAW"] - env: - # use internal network - - name: FLANNELD_IFACE - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: EVENT_QUEUE_DEPTH - value: "5000" - volumeMounts: - - name: run - mountPath: /run/flannel - - name: flannel-cfg - mountPath: /etc/kube-flannel/ - - name: xtables-lock - mountPath: /run/xtables.lock - volumes: - - name: run - hostPath: - path: /run/flannel - - name: cni-plugin - hostPath: - path: /opt/cni/bin - - name: cni - hostPath: - path: /etc/cni/net.d - - name: flannel-cfg - configMap: - name: kube-flannel-cfg - - name: xtables-lock - hostPath: - path: /run/xtables.lock - type: FileOrCreate \ No newline at end of file diff --git a/scripts/chart/templates/metrics-server.yml b/scripts/chart/templates/metrics-server.yml deleted file mode 100644 index 97c4500..0000000 --- a/scripts/chart/templates/metrics-server.yml +++ /dev/null @@ -1,196 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - k8s-app: metrics-server - name: metrics-server - namespace: kube-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - k8s-app: metrics-server - rbac.authorization.k8s.io/aggregate-to-admin: "true" - rbac.authorization.k8s.io/aggregate-to-edit: "true" - rbac.authorization.k8s.io/aggregate-to-view: "true" - name: system:aggregated-metrics-reader -rules: -- apiGroups: - - metrics.k8s.io - resources: - - pods - - nodes - verbs: - - get - - list - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - k8s-app: metrics-server - name: system:metrics-server -rules: -- apiGroups: - - "" - resources: - - nodes/metrics - verbs: - - get -- apiGroups: - - "" - resources: - - pods - - nodes - verbs: - - get - - list - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - labels: - k8s-app: metrics-server - name: metrics-server-auth-reader - namespace: kube-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: extension-apiserver-authentication-reader -subjects: -- kind: ServiceAccount - name: metrics-server - namespace: kube-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - k8s-app: metrics-server - name: metrics-server:system:auth-delegator -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:auth-delegator -subjects: -- kind: ServiceAccount - name: metrics-server - namespace: kube-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - k8s-app: metrics-server - name: system:metrics-server -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: system:metrics-server -subjects: -- kind: ServiceAccount - name: metrics-server - namespace: kube-system ---- -apiVersion: v1 -kind: Service -metadata: - labels: - k8s-app: metrics-server - name: metrics-server - namespace: kube-system -spec: - ports: - - name: https - port: 443 - protocol: TCP - targetPort: https - selector: - k8s-app: metrics-server ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - k8s-app: metrics-server - name: metrics-server - namespace: kube-system -spec: - selector: - matchLabels: - k8s-app: metrics-server - strategy: - rollingUpdate: - maxUnavailable: 0 - template: - metadata: - labels: - k8s-app: metrics-server - spec: - containers: - - args: - - --cert-dir=/tmp - - --secure-port=4443 - - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname - - --kubelet-use-node-status-port - - --metric-resolution=15s - image: registry.k8s.io/metrics-server/metrics-server:v0.6.1 - imagePullPolicy: IfNotPresent - livenessProbe: - failureThreshold: 3 - httpGet: - path: /livez - port: https - scheme: HTTPS - periodSeconds: 10 - name: metrics-server - ports: - - containerPort: 4443 - name: https - protocol: TCP - readinessProbe: - failureThreshold: 3 - httpGet: - path: /readyz - port: https - scheme: HTTPS - initialDelaySeconds: 20 - periodSeconds: 10 - resources: - requests: - cpu: 100m - memory: 200Mi - securityContext: - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - volumeMounts: - - mountPath: /tmp - name: tmp-dir - nodeSelector: - kubernetes.io/os: linux - priorityClassName: system-cluster-critical - serviceAccountName: metrics-server - volumes: - - emptyDir: {} - name: tmp-dir ---- -apiVersion: apiregistration.k8s.io/v1 -kind: APIService -metadata: - labels: - k8s-app: metrics-server - name: v1beta1.metrics.k8s.io -spec: - group: metrics.k8s.io - groupPriorityMinimum: 100 - insecureSkipTLSVerify: true - service: - name: metrics-server - namespace: kube-system - version: v1beta1 - versionPriority: 100 diff --git a/scripts/chart/templates/nfs-server.yaml b/scripts/chart/templates/nfs-server.yaml index f65fe23..e5a35f3 100644 --- a/scripts/chart/templates/nfs-server.yaml +++ b/scripts/chart/templates/nfs-server.yaml @@ -34,9 +34,14 @@ spec: labels: app: nfs-server spec: + priorityClassName: system-cluster-critical containers: - name: nfs-server - image: itsthenetwork/nfs-server-alpine:12 + # git clone git@github.com:sjiveson/nfs-server-alpine.git + # cd nfs-server-alpine + # remove --no-nfs-version 2 in nfsd.sh + # docker build . --platform=linux/amd64,linux/arm64 --pull --push -t paskalmaksim/nfs-server-alpine:$(date '+%Y%m%d')$(git rev-parse --short HEAD) + image: paskalmaksim/nfs-server-alpine:20231106ca320f7 env: - name: SHARED_DIRECTORY value: "/exports" @@ -49,6 +54,14 @@ spec: requests: cpu: "50m" memory: "300Mi" + readinessProbe: + exec: + command: + - showmount + - -e + - 127.0.0.1 + initialDelaySeconds: 10 + periodSeconds: 5 volumeClaimTemplates: - metadata: name: nfs-data diff --git a/scripts/chart/templates/pdb.yaml b/scripts/chart/templates/pdb.yaml index 8480680..26baa66 100644 --- a/scripts/chart/templates/pdb.yaml +++ b/scripts/chart/templates/pdb.yaml @@ -18,4 +18,4 @@ spec: minAvailable: 1 selector: matchLabels: - app: hcloud-cloud-controller-manager \ No newline at end of file + app.kubernetes.io/name: hcloud-cloud-controller-manager \ No newline at end of file diff --git a/scripts/chart/templates/registry.yaml b/scripts/chart/templates/registry.yaml index d72460d..79d25c4 100644 --- a/scripts/chart/templates/registry.yaml +++ b/scripts/chart/templates/registry.yaml @@ -65,7 +65,7 @@ metadata: labels: app: docker-registry spec: - type: ClusterIP + type: {{ .Values.deployments.registry.service.type }} clusterIP: {{ .Values.deployments.registry.clusterIP }} ports: - name: registry diff --git a/scripts/chart/values.yaml b/scripts/chart/values.yaml index 5830679..84eea2e 100644 --- a/scripts/chart/values.yaml +++ b/scripts/chart/values.yaml @@ -4,31 +4,6 @@ hetznerToken: ccm: null clusterName: k8s location: nbg1 -autoscaler: - enabled: true - args: - - --v=4 - - --cloud-provider=hetzner - - --stderrthreshold=info - - --expander=least-waste - - --scale-down-enabled=true - - --skip-nodes-with-local-storage=false - - --skip-nodes-with-system-pods=false - - --scale-down-utilization-threshold=0.8 - defaults: - min: 0 - max: 20 - workers: - - location: "" - min: 0 - max: 0 - type: - - cx11 - - cx21 - - cpx31 - - cpx41 - - cx51 - - cpx51 deployments: nfs: server: @@ -43,21 +18,35 @@ deployments: image: registry:2.8.1 imagePullPolicy: IfNotPresent clusterIP: "10.100.0.11" + service: + type: ClusterIP env: {} resources: requests: cpu: 10m memory: 100Mi - ccmconfig: - env: - - name: HCLOUD_NETWORK_ROUTES_ENABLED + +hcloud-cloud-controller-manager: + replicaCount: 2 + env: + NODE_NAME: + valueFrom: + fieldRef: + fieldPath: spec.nodeName + HCLOUD_NETWORK_ROUTES_ENABLED: value: "false" - - name: HCLOUD_NETWORK - value: "{{ .Values.clusterName }}" - - name: HCLOUD_LOAD_BALANCERS_USE_PRIVATE_IP + HCLOUD_NETWORK: + valueFrom: + configMapKeyRef: + name: hcloud-ccm-env + key: HCLOUD_NETWORK + HCLOUD_LOAD_BALANCERS_USE_PRIVATE_IP: value: "true" - - name: HCLOUD_LOAD_BALANCERS_LOCATION - value: "{{ lower .Values.location }}" + HCLOUD_LOAD_BALANCERS_LOCATION: + valueFrom: + configMapKeyRef: + name: hcloud-ccm-env + key: HCLOUD_LOAD_BALANCERS_LOCATION nfs-subdir-external-provisioner: replicaCount: 2 @@ -77,4 +66,87 @@ nfs-subdir-external-provisioner: archiveOnDelete: false kubelet-csr-approver: - bypassDnsResolution: true \ No newline at end of file + bypassDnsResolution: true + +cluster-autoscaler: + replicaCount: 2 + priorityClassName: system-cluster-critical + resources: + requests: + cpu: 100m + memory: 300Mi + tolerations: + - effect: NoSchedule + key: node-role.kubernetes.io/master + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - cluster-autoscaler + topologyKey: kubernetes.io/hostname + cloudProvider: hetzner + autoDiscovery: + clusterName: k8s + extraArgs: + expander: least-waste + scale-down-enabled: true + skip-nodes-with-local-storage: false + skip-nodes-with-system-pods: false + scale-down-utilization-threshold: 0.8 + envFromConfigMap: "cluster-autoscaler-env" + extraEnvConfigMaps: + HCLOUD_CLOUD_INIT: + name: hcloud-init + key: bootstrap + extraEnvSecrets: + HCLOUD_TOKEN: + name: hcloud + key: token + +hcloud-csi: + controller: + resources: + csiAttacher: + requests: + cpu: 10m + memory: 50Mi + csiResizer: + requests: + cpu: 10m + memory: 50Mi + csiProvisioner: + requests: + cpu: 10m + memory: 50Mi + livenessProbe: + requests: + cpu: 10m + memory: 50Mi + hcloudCSIDriver: + requests: + cpu: 10m + memory: 50Mi + hcloudToken: + existingSecret: + name: hcloud-csi + key: token + node: + resources: + csiNodeDriverRegistrar: + requests: + cpu: 10m + memory: 50Mi + livenessProbe: + requests: + cpu: 10m + memory: 50Mi + hcloudCSIDriver: + requests: + cpu: 10m + memory: 50Mi \ No newline at end of file diff --git a/scripts/patch-coredns.yaml b/scripts/patch/coredns.yaml similarity index 100% rename from scripts/patch-coredns.yaml rename to scripts/patch/coredns.yaml diff --git a/scripts/patch/kube-flannel-ds.yaml b/scripts/patch/kube-flannel-ds.yaml new file mode 100644 index 0000000..5746bfc --- /dev/null +++ b/scripts/patch/kube-flannel-ds.yaml @@ -0,0 +1,25 @@ +spec: + template: + spec: + initContainers: + - name: install-cni-plugin + resources: + requests: + cpu: 100m + memory: 50Mi + - name: install-cni + resources: + requests: + cpu: 100m + memory: 50Mi + containers: + - name: kube-flannel + resources: + requests: + cpu: 100m + memory: 50Mi + env: + - name: FLANNELD_IFACE + valueFrom: + fieldRef: + fieldPath: status.podIP \ No newline at end of file diff --git a/scripts/patch/kube-proxy.yaml b/scripts/patch/kube-proxy.yaml new file mode 100644 index 0000000..a95fbcd --- /dev/null +++ b/scripts/patch/kube-proxy.yaml @@ -0,0 +1,9 @@ +spec: + template: + spec: + containers: + - name: kube-proxy + resources: + requests: + cpu: 100m + memory: 50Mi \ No newline at end of file diff --git a/scripts/post-install.sh b/scripts/post-install.sh index 6777ac2..08413cf 100755 --- a/scripts/post-install.sh +++ b/scripts/post-install.sh @@ -24,12 +24,14 @@ export KUBECONFIG=$KUBECONFIG_PATH kubectl annotate node --overwrite -lnode-role.kubernetes.io/master cluster-autoscaler.kubernetes.io/scale-down-disabled=true kubectl annotate node --overwrite -lnode-role.kubernetes.io/control-plane cluster-autoscaler.kubernetes.io/scale-down-disabled=true +OS_ARCH=$(dpkg --print-architecture) + # install helm for kubernetes deploymens -curl -o helm.tar.gz https://get.helm.sh/helm-v3.4.2-linux-amd64.tar.gz +curl -o helm.tar.gz "https://get.helm.sh/helm-v3.11.3-linux-${OS_ARCH}.tar.gz" tar -xvf helm.tar.gz -mv linux-amd64/helm /usr/local/bin/helm +mv "linux-${OS_ARCH}/helm" /usr/local/bin/helm rm helm.tar.gz -rm -rf linux-amd64 +rm -rf "linux-${OS_ARCH}" # delete all tokens kubeadm token list -o jsonpath='{.token}{"\n"}' | xargs kubeadm token delete @@ -45,7 +47,7 @@ HCLOUD_CLOUD_INIT=$(base64 -w 0 < /root/scripts/cloud-init.sh) kubectl -n kube-system delete configmap hcloud-init || true kubectl -n kube-system create configmap hcloud-init --from-literal=bootstrap="$HCLOUD_CLOUD_INIT" # shutdown current autoscaler that have wrong cloud-init configuration -kubectl -n kube-system scale deploy cluster-autoscaler --replicas=0 || true +kubectl -n kube-system scale deploy hcloud-k8s-ctl-hetzner-cluster-autoscaler --replicas=0 || true # install dependencies helm dep up $SCRIPT_PATH/scripts/chart @@ -53,5 +55,7 @@ helm dep up $SCRIPT_PATH/scripts/chart # install helm chart helm upgrade --install hcloud-k8s-ctl --namespace kube-system $SCRIPT_PATH/scripts/chart --values=$SCRIPT_PATH/values.yaml -# patch coredns -kubectl -n kube-system patch deployment coredns --patch "$(cat $SCRIPT_PATH/scripts/patch-coredns.yaml)" +# patch objects +kubectl -n kube-system patch deploy coredns --patch "$(cat $SCRIPT_PATH/scripts/patch/coredns.yaml)" +kubectl -n kube-system patch ds kube-flannel-ds --patch "$(cat $SCRIPT_PATH/scripts/patch/kube-flannel-ds.yaml)" +kubectl -n kube-system patch ds kube-proxy --patch "$(cat $SCRIPT_PATH/scripts/patch/kube-proxy.yaml)" diff --git a/scripts/prepare-scripts.sh b/scripts/prepare-scripts.sh index d75f123..acdea6f 100644 --- a/scripts/prepare-scripts.sh +++ b/scripts/prepare-scripts.sh @@ -15,15 +15,22 @@ # limitations under the License. set -ex -GO_TEMPLATE_VERSION=0.1.1 +GO_TEMPLATE_VERSION="0.1.2" +OS_ARCH=$(dpkg --print-architecture) # use go-binary to template script files -curl -sSL -o /usr/local/bin/go-template https://github.com/maksim-paskal/go-template/releases/download/v${GO_TEMPLATE_VERSION}/go-template_${GO_TEMPLATE_VERSION}_linux_amd64 +curl -sSL -o /usr/local/bin/go-template "https://github.com/maksim-paskal/go-template/releases/download/v${GO_TEMPLATE_VERSION}/go-template_${GO_TEMPLATE_VERSION}_linux_${OS_ARCH}" chmod +x /usr/local/bin/go-template # create checksum file touch /tmp/checksum -echo "59d980c91c52b2e2f0195aadd8b21d2ce1e9e514defb66e5f5d5495e804211aa /usr/local/bin/go-template" >> /tmp/checksum +if [ "${OS_ARCH}" == "amd64" ]; then + echo "65d6b1e296dafb062c785c5a14135beeca953b11c577a70da74e5071793a4120 /usr/local/bin/go-template" >> /tmp/checksum +fi + +if [ "${OS_ARCH}" == "arm64" ]; then + echo "c70ad5472a7a4db5ee9fd2593ebbad1437f345c9ee4a0fda3ba688199350e277 /usr/local/bin/go-template" >> /tmp/checksum +fi # test downloaded script with sha256sum sha256sum -c /tmp/checksum diff --git a/utils/main.go b/utils/main.go new file mode 100644 index 0000000..a5baaf7 --- /dev/null +++ b/utils/main.go @@ -0,0 +1,99 @@ +/* +Copyright paskal.maksim@gmail.com +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 main + +import ( + "os" + "path/filepath" + "strings" + + "github.com/pkg/errors" +) + +func main() { + if err := updateReadme(); err != nil { + panic(err) + } +} + +type ReadmeExample struct { + raw string +} + +func (e *ReadmeExample) GetHeader() string { + return strings.Split(e.raw, "\n")[0] +} + +func (e *ReadmeExample) GetFormattedHeader() string { + return strings.TrimPrefix(e.GetHeader(), "# ") +} + +func (e *ReadmeExample) GetContent() string { + return strings.TrimPrefix(e.raw, e.GetHeader()) +} + +func updateReadme() error { + files, err := filepath.Glob("./e2e/configs/*.yaml") + if err != nil { + return errors.Wrap(err, "filepath.Glob") + } + + b := strings.Builder{} + + for _, file := range files { + if strings.HasSuffix(file, "full.yaml") { + continue + } + + fileContent, err := os.ReadFile(file) + if err != nil { + return errors.Wrap(err, "os.ReadFile") + } + + article := &ReadmeExample{raw: string(fileContent)} + + b.WriteString("
") + b.WriteString("") + b.WriteString(article.GetFormattedHeader()) + b.WriteString("\n") + b.WriteString("\n```yaml") + b.WriteString(article.GetContent()) + b.WriteString("\n```\n") + b.WriteString("
\n") + } + + readme, err := os.ReadFile("README.md") + if err != nil { + return errors.Wrap(err, "os.ReadFile") + } + + readmeContent := string(readme) + + const ( + startMarker = "" + endMarker = "" + ) + + // remove old content + sPosition := strings.Index(readmeContent, startMarker) + ePosition := strings.Index(readmeContent, endMarker) + + readmeContent = readmeContent[0:sPosition] + readmeContent[ePosition:] + readmeContent = strings.ReplaceAll(readmeContent, endMarker, startMarker+"\n"+b.String()+"\n"+endMarker) + + if err := os.WriteFile("README.md", []byte(readmeContent), 0o644); err != nil { //nolint:gomnd,gosec + return errors.Wrap(err, "os.WriteFile") + } + + return nil +}