├── .github └── workflows │ ├── build.yml │ └── tag.yml ├── .gitignore ├── .travis.yml ├── .vscode ├── launch.json └── tasks.json ├── Dockerfile ├── Dockerfile.amd64 ├── Dockerfile.arm64 ├── LICENSE ├── Makefile ├── README.md ├── artifacts └── examples │ ├── controlplane-rke2.yaml │ ├── controlplane.yaml │ ├── crd.yaml │ └── example.yaml ├── build-and-run.sh ├── build.sh ├── builder ├── Dockerfile └── README.md ├── client └── client.go ├── constantes ├── annotations.go └── errors.go ├── context └── context.go ├── examples ├── external │ ├── cluster-autoscaler-vanilla.yaml │ └── cluster-autoscaler.yaml ├── k3s │ ├── cluster-autoscaler-vanilla.yaml │ └── cluster-autoscaler.yaml ├── kubeadm │ ├── cluster-autoscaler-vanilla.yaml │ └── cluster-autoscaler.yaml └── rke2 │ ├── cluster-autoscaler-vanilla.yaml │ └── cluster-autoscaler.yaml ├── externalgrpc ├── externalgrpc.pb.go ├── externalgrpc_grpc.pb.go └── protoc_grpc.sh ├── go.mod ├── go.sum ├── grpc ├── grpc.pb.go ├── grpc.proto ├── grpc_grpc.pb.go └── protoc_grpc.sh ├── hack ├── boilerplate.go.txt ├── custom-boilerplate.go.txt ├── tools.go ├── update-codegen.sh └── verify-codegen.sh ├── main.go ├── masterkube └── README.md ├── pkg ├── apis │ └── nodemanager │ │ ├── register.go │ │ └── v1alpha1 │ │ ├── doc.go │ │ ├── register.go │ │ ├── types.go │ │ └── zz_generated.deepcopy.go ├── generated │ ├── clientset │ │ └── versioned │ │ │ ├── clientset.go │ │ │ ├── fake │ │ │ ├── clientset_generated.go │ │ │ ├── doc.go │ │ │ └── register.go │ │ │ ├── scheme │ │ │ ├── doc.go │ │ │ └── register.go │ │ │ └── typed │ │ │ └── nodemanager │ │ │ └── v1alpha1 │ │ │ ├── doc.go │ │ │ ├── fake │ │ │ ├── doc.go │ │ │ ├── fake_managednode.go │ │ │ └── fake_nodemanager_client.go │ │ │ ├── generated_expansion.go │ │ │ ├── managednode.go │ │ │ └── nodemanager_client.go │ ├── informers │ │ └── externalversions │ │ │ ├── factory.go │ │ │ ├── generic.go │ │ │ ├── internalinterfaces │ │ │ └── factory_interfaces.go │ │ │ └── nodemanager │ │ │ ├── interface.go │ │ │ └── v1alpha1 │ │ │ ├── interface.go │ │ │ └── managednode.go │ └── listers │ │ └── nodemanager │ │ └── v1alpha1 │ │ ├── expansion_generated.go │ │ └── managednode.go └── signals │ ├── signal.go │ ├── signal_posix.go │ └── signal_windows.go ├── push_image.sh ├── run.sh ├── scripts ├── cleanup.sh └── run-tests.sh ├── server ├── all_test.go ├── controller.go ├── externalgrpc.go ├── node.go ├── nodegroup.go ├── nodegroup_test.go ├── server.go └── server_test.go ├── sonar-project.properties ├── test ├── config.json └── vsphere.json ├── types └── types.go ├── utils ├── communicator.go ├── podfilters.go └── utils.go └── vsphere ├── autostart.go ├── client.go ├── configuration.go ├── datacenter.go ├── datastore.go ├── network.go ├── vm.go └── vsphere_test.go /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build-vsphere-autoscaler 2 | on: 3 | push: 4 | branches: 5 | - '*' 6 | tags: 7 | - '!*' 8 | 9 | pull_request: 10 | types: [opened, synchronize, reopened] 11 | 12 | env: 13 | REGISTRY: ${{ secrets.REGISTRY }} 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 16 | 17 | jobs: 18 | build: 19 | name: Build 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v4 24 | 25 | - name: Cache vendor 26 | uses: actions/cache@v3.3.2 27 | env: 28 | cache-name: cache-vendor 29 | with: 30 | path: vendor 31 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('go.sum') }} 32 | restore-keys: | 33 | ${{ runner.os }}-build-${{ env.cache-name }}- 34 | 35 | - name: Setup Go environment 36 | uses: actions/setup-go@v5 37 | with: 38 | go-version: 1.21 39 | 40 | - name: Build binary 41 | shell: bash 42 | run: | 43 | make -e REGISTRY=$REGISTRY -e TAG=build-ci build-in-docker 44 | 45 | test: 46 | name: Test 47 | runs-on: ubuntu-latest 48 | needs: build 49 | steps: 50 | - name: Checkout 51 | uses: actions/checkout@v4 52 | 53 | - name: Cache vendor 54 | uses: actions/cache@v3 55 | env: 56 | cache-name: cache-vendor 57 | with: 58 | path: vendor 59 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('go.sum') }} 60 | restore-keys: | 61 | ${{ runner.os }}-build-${{ env.cache-name }}- 62 | 63 | - name: test-in-docker 64 | shell: bash 65 | run: | 66 | make -e REGISTRY=$REGISTRY -e TAG=test-ci test-in-docker 67 | 68 | sonarcloud: 69 | name: SonarCloud 70 | runs-on: ubuntu-latest 71 | needs: build 72 | steps: 73 | - name: Checkout 74 | uses: actions/checkout@v4 75 | with: 76 | fetch-depth: 0 77 | 78 | - name: SonarCloud Scan 79 | uses: SonarSource/sonarcloud-github-action@v2.1.0 80 | 81 | - name: Prepare SonarCloud 82 | shell: bash 83 | run: sudo chown -R $USER .scannerwork 84 | -------------------------------------------------------------------------------- /.github/workflows/tag.yml: -------------------------------------------------------------------------------- 1 | name: deploy-vsphere-autoscaler 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | env: 8 | REGISTRY: ${{ secrets.REGISTRY }} 9 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 10 | 11 | jobs: 12 | deploy: 13 | if: startsWith(github.ref, 'refs/tags/v') 14 | name: Deploy 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | 21 | - name: Cache vendor 22 | uses: actions/cache@v3.3.2 23 | env: 24 | cache-name: cache-vendor 25 | with: 26 | path: vendor 27 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('go.sum') }} 28 | restore-keys: | 29 | ${{ runner.os }}-build-${{ env.cache-name }}- 30 | 31 | - name: Setup Go environment 32 | uses: actions/setup-go@v4 33 | with: 34 | go-version: 1.21 35 | 36 | - name: Test 37 | shell: bash 38 | run: | 39 | IMAGE_TAG=${GITHUB_REF#refs/tags/} 40 | make -e REGISTRY=$REGISTRY -e TAG="${IMAGE_TAG}" test-in-docker 41 | 42 | - name: Prepare docker buildx 43 | id: prep 44 | shell: bash 45 | run: | 46 | IMAGE_TAG=${GITHUB_REF#refs/tags/} 47 | echo "tag=${IMAGE_TAG}" >> $GITHUB_OUTPUT 48 | docker buildx version; 49 | echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin; 50 | docker context create builders 51 | 52 | - name: Setup docker buildx 53 | uses: docker/setup-buildx-action@v3.0.0 54 | with: 55 | version: latest 56 | endpoint: builders 57 | use: true 58 | 59 | - name: Build docker image 60 | id: build 61 | shell: bash 62 | run: | 63 | IMAGE_TAG=${GITHUB_REF#refs/tags/} 64 | make -e REGISTRY=$REGISTRY -e TAG="${IMAGE_TAG}" container-push-manifest 65 | sudo chown -R $USER out vendor 66 | cp out/linux/amd64/vsphere-autoscaler vsphere-autoscaler-amd64 67 | cp out/linux/arm64/vsphere-autoscaler vsphere-autoscaler-arm64 68 | 69 | - name: Remove same release 70 | uses: liudonghua123/delete-release-action@v1 71 | with: 72 | release_name: ${{ steps.prep.outputs.tag }} 73 | suppress_errors: true 74 | 75 | - name: Release 76 | uses: softprops/action-gh-release@v1 77 | with: 78 | tag_name: ${{ steps.prep.outputs.tag }} 79 | draft: false 80 | files: | 81 | vsphere-autoscaler-amd64 82 | vsphere-autoscaler-arm64 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | vendor 14 | \.vscode/ 15 | 16 | *.code-workspace 17 | 18 | out 19 | masterkube/cluster/* 20 | masterkube/kubernetes/* 21 | masterkube/config/* 22 | masterkube/etc/ssl/* 23 | 24 | vsphere/agora\.json 25 | 26 | masterkube/guestinfos/*\.txt 27 | masterkube/guestinfos/*\.json 28 | masterkube/guestinfos/*\.yaml 29 | 30 | launch\.sh 31 | 32 | kubernetes-vmware-autoscaler 33 | 34 | masterkube/bin/govc.defs 35 | 36 | build-dev.sh 37 | 38 | .secrets 39 | .config 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Use the newer Travis-CI build templates based on the 2 | # Debian Linux distribution "Xenial" release. 3 | os: linux 4 | dist: focal 5 | language: minimal 6 | env: 7 | global: 8 | - DOCKER_CLI_EXPERIMENTAL=enabled 9 | addons: 10 | sonarcloud: 11 | organization: fred78290-github 12 | token: 13 | secure: "X6QTVwIX6ZGN3CSozfvTevW6XxvVFBXuZZxTSIsloldJ4uwxGZiKUyfO83PSov7I7FghKTaMppPxMgO6ZT5FlBqFNGJZ53QfrajGJ8//tS+fYZ94SucEYMZ4W7B4K+YjEMJERgBSw2mgrMIy6mZ+P4j0mhuHbAtvloFA7tJIjXYMTuoPqfHzeYkuBPGcWJ0r2n0zQTMNGVnjnsbZlj50BPoMx9H7S6dvtp1L67yyom4NnrDKwNNnq/dy5O0lhlIR3KzBXlf8E+pzC+CCDIXiasENhtJawez4yQ63gPtbJSR+9FbUmdoEX3Lr83HnhGrXnWJ5BsxYdjrQSV7E3B9EMJpP2C5pIjKC9HVgNyoeHgLT8nxcKtsQpmSyS/+lbus8/Vj+lTlir7vmcPDlehUHOeIemJajB23kmNiwWfo71MvfSnVt+4MqAYgegUW5G6fJAqEahVA2hgo/o58RXnD6m61EyPOlC273CpLJ8qrFqa6+cHspGkWa9KQ5RNfDIkj8i2iHR+HXkaeNy4L1KM2+6KIx7sJl1R1fo6RYwYM3S0HfL6Qm+HGqYesOhH9tTs2X27whzqBb+GzgwFuxYrBR6X+rAD/SXjF7ubqroJNmN+o4uGeVVevMCkFZc5vI2Q3XW8TU28orhHXm+aGHaq9GJ7I83om+69dcSi9uiNixFNA=" 14 | cache: 15 | directories: 16 | - $HOME/.sonar/cache 17 | - vendor 18 | jobs: 19 | include: 20 | - stage: build 21 | if: NOT(tag IS present OR commit_message =~ /\/ci-deploy/) 22 | install: true 23 | script: 24 | - make -e REGISTRY=fred78290 -e TAG=$TRAVIS_TAG container 25 | - sonar-scanner 26 | - stage: test 27 | if: NOT(tag IS present OR commit_message =~ /\/ci-deploy/) 28 | install: true 29 | script: make test-unit 30 | - stage: deploy 31 | if: tag IS present OR commit_message =~ /\/ci-deploy/ 32 | services: 33 | - docker 34 | before_install: 35 | - mkdir -vp ~/.docker/cli-plugins/ 36 | - curl --silent -L "https://github.com/docker/buildx/releases/download/v0.5.1/buildx-v0.5.1.linux-amd64" > ~/.docker/cli-plugins/docker-buildx 37 | - chmod a+x ~/.docker/cli-plugins/docker-buildx 38 | install: true 39 | script: 40 | - docker buildx version 41 | - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin 42 | - docker buildx create --use 43 | - make -e REGISTRY=fred78290 -e TAG=$TRAVIS_TAG container-push-manifest 44 | - cp out/linux/amd64/vsphere-autoscaler vsphere-autoscaler-amd64 45 | - cp out/linux/arm64/vsphere-autoscaler vsphere-autoscaler-arm64 46 | deploy: 47 | provider: releases 48 | api_key: $GITHUB_OAUTH_TOKEN 49 | skip_cleanup: true 50 | on: 51 | tags: true 52 | repo: Fred78290/kubernetes-vmware-autoscaler 53 | file: 54 | - vsphere-autoscaler-amd64 55 | - vsphere-autoscaler-arm64 56 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Utilisez IntelliSense pour en savoir plus sur les attributs possibles. 3 | // Pointez pour afficher la description des attributs existants. 4 | // Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "preLaunchTask": "kubeadm", 9 | "name": "kubeadm custom grpc", 10 | "type": "go", 11 | "request": "launch", 12 | "mode": "auto", 13 | "program": "${workspaceFolder}", 14 | "cwd": "${env:HOME}/go/src/github.com/Fred78290/kubernetes-vmware-autoscaler", 15 | "args": [ 16 | "--request-timeout=120s", 17 | "--no-use-vanilla-grpc", 18 | "--config=${workspaceFolder}/.config/vmware-ca-k8s/kubernetes-vmware-autoscaler.json", 19 | "--save=${workspaceFolder}/.config/vmware-ca-k8s/autoscaler-state.json", 20 | "--kubeconfig=${workspaceFolder}/.config/vmware-ca-k8s/config", 21 | "--log-level=info" 22 | ] 23 | }, 24 | { 25 | "preLaunchTask": "kubeadm", 26 | "name": "kubeadm external grpc", 27 | "type": "go", 28 | "request": "launch", 29 | "mode": "auto", 30 | "program": "${workspaceFolder}", 31 | "cwd": "${env:HOME}/go/src/github.com/Fred78290/kubernetes-vmware-autoscaler", 32 | "args": [ 33 | "--request-timeout=120s", 34 | "--use-vanilla-grpc", 35 | "--config=${workspaceFolder}/.config/vmware-ca-k8s/kubernetes-vmware-autoscaler.json", 36 | "--save=${workspaceFolder}/.config/vmware-ca-k8s/autoscaler-state.json", 37 | "--kubeconfig=${workspaceFolder}/.config/vmware-ca-k8s/config", 38 | "--log-level=info" 39 | ] 40 | }, 41 | { 42 | "preLaunchTask": "k3s", 43 | "name": "k3s custom grpc", 44 | "type": "go", 45 | "request": "launch", 46 | "mode": "auto", 47 | "program": "${workspaceFolder}", 48 | "cwd": "${env:HOME}/go/src/github.com/Fred78290/kubernetes-vmware-autoscaler", 49 | "args": [ 50 | "--request-timeout=120s", 51 | "--no-use-vanilla-grpc", 52 | "--config=${workspaceFolder}/.config/vmware-dev-k3s/kubernetes-vmware-autoscaler.json", 53 | "--save=${workspaceFolder}/.config/vmware-dev-k3s/autoscaler-state.json", 54 | "--kubeconfig=${workspaceFolder}/.config/vmware-dev-k3s/config", 55 | "--log-level=info" 56 | ] 57 | }, 58 | { 59 | "preLaunchTask": "k3s", 60 | "name": "k3s external grpc", 61 | "type": "go", 62 | "request": "launch", 63 | "mode": "auto", 64 | "program": "${workspaceFolder}", 65 | "cwd": "${env:HOME}/go/src/github.com/Fred78290/kubernetes-vmware-autoscaler", 66 | "args": [ 67 | "--request-timeout=120s", 68 | "--use-vanilla-grpc", 69 | "--config=${workspaceFolder}/.config/vmware-dev-k3s/kubernetes-vmware-autoscaler.json", 70 | "--save=${workspaceFolder}/.config/vmware-dev-k3s/autoscaler-state.json", 71 | "--kubeconfig=${workspaceFolder}/.config/vmware-dev-k3s/config", 72 | "--log-level=info" 73 | ] 74 | }, 75 | { 76 | "preLaunchTask": "rke2", 77 | "name": "rke2 external grpc", 78 | "type": "go", 79 | "request": "launch", 80 | "mode": "auto", 81 | "program": "${workspaceFolder}", 82 | "cwd": "${env:HOME}/go/src/github.com/Fred78290/kubernetes-vmware-autoscaler", 83 | "args": [ 84 | "--request-timeout=120s", 85 | "--use-vanilla-grpc", 86 | "--config=${workspaceFolder}/.config/vmware-dev-rke2/kubernetes-vmware-autoscaler.json", 87 | "--save=${workspaceFolder}/.config/vmware-dev-rke2/autoscaler-state.json", 88 | "--kubeconfig=${workspaceFolder}/.config/vmware-dev-rke2/config", 89 | "--log-level=info" 90 | ] 91 | }, 92 | { 93 | "preLaunchTask": "rke2", 94 | "name": "rke2 custom grpc", 95 | "type": "go", 96 | "request": "launch", 97 | "mode": "auto", 98 | "program": "${workspaceFolder}", 99 | "cwd": "${env:HOME}/go/src/github.com/Fred78290/kubernetes-vmware-autoscaler", 100 | "args": [ 101 | "--request-timeout=120s", 102 | "--no-use-vanilla-grpc", 103 | "--config=${workspaceFolder}/.config/vmware-dev-rke2/kubernetes-vmware-autoscaler.json", 104 | "--save=${workspaceFolder}/.config/vmware-dev-rke2/autoscaler-state.json", 105 | "--kubeconfig=${workspaceFolder}/.config/vmware-dev-rke2/config", 106 | "--log-level=info" 107 | ] 108 | }, 109 | ] 110 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "kubeadm", 8 | "type": "shell", 9 | "command": "${workspaceFolder}/scripts/cleanup.sh vmware-ca-k8s", 10 | "problemMatcher": [], 11 | "group": { 12 | "kind": "build", 13 | "isDefault": true 14 | } 15 | }, 16 | { 17 | "label": "k3s", 18 | "type": "shell", 19 | "command": "${workspaceFolder}/scripts/cleanup.sh vmware-dev-k3s", 20 | "problemMatcher": [], 21 | "group": { 22 | "kind": "build", 23 | "isDefault": true 24 | } 25 | }, 26 | { 27 | "label": "rke2", 28 | "type": "shell", 29 | "command": "${workspaceFolder}/scripts/cleanup.sh vmware-dev-rke2", 30 | "problemMatcher": [], 31 | "group": { 32 | "kind": "build", 33 | "isDefault": true 34 | } 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Frederic Boltz Author. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | #ARG BASEIMAGE=gcr.io/distroless/static:latest-amd64 16 | #ARG BASEIMAGE=gcr.io/distroless/static:nonroot 17 | ARG BASEIMAGE=alpine:3.19 18 | FROM alpine:3.19 AS builder 19 | ARG TARGETPLATFORM 20 | ARG BUILDPLATFORM 21 | 22 | COPY out . 23 | RUN ls / ; mv /$TARGETPLATFORM/vsphere-autoscaler /vsphere-autoscaler ; chmod uog+x /vsphere-autoscaler 24 | 25 | FROM $BASEIMAGE 26 | ARG TARGETPLATFORM 27 | ARG BUILDPLATFORM 28 | ARG USER=default 29 | 30 | ENV HOME /home/$USER 31 | 32 | LABEL maintainer="Frederic Boltz " 33 | 34 | COPY --from=builder /vsphere-autoscaler /usr/local/bin/vsphere-autoscaler 35 | 36 | # add new user 37 | RUN apk update \ 38 | && apk add --no-cache sudo openssh \ 39 | && adduser -D $USER -u 65532 \ 40 | && echo "$USER ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/$USER \ 41 | && chmod 0440 /etc/sudoers.d/$USER 42 | 43 | USER $USER 44 | 45 | EXPOSE 5200 46 | 47 | CMD ["/usr/local/bin/vsphere-autoscaler"] 48 | -------------------------------------------------------------------------------- /Dockerfile.amd64: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Frederic Boltz Author. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ARG BASEIMAGE=gcr.io/distroless/static:nonroot-amd64 16 | FROM $BASEIMAGE 17 | LABEL maintainer="Frederic Boltz " 18 | 19 | COPY out/linux/amd64/vsphere-autoscaler /usr/local/bin/vsphere-autoscaler 20 | 21 | EXPOSE 5200 22 | 23 | CMD ["/usr/local/bin/vsphere-autoscaler"] 24 | -------------------------------------------------------------------------------- /Dockerfile.arm64: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Frederic Boltz Author. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ARG BASEIMAGE=gcr.io/distroless/static:nonroot-arm64 16 | FROM $BASEIMAGE 17 | LABEL maintainer="Frederic Boltz " 18 | 19 | COPY out/linux/arm64/vsphere-autoscaler /usr/local/bin/vsphere-autoscaler 20 | 21 | EXPOSE 5200 22 | 23 | CMD ["/usr/local/bin/vsphere-autoscaler"] 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ALL_ARCH = amd64 arm64 2 | 3 | .EXPORT_ALL_VARIABLES: 4 | 5 | all: $(addprefix build-arch-,$(ALL_ARCH)) 6 | 7 | VERSION_MAJOR ?= 1 8 | VERSION_MINOR ?= 29 9 | VERSION_BUILD ?= 0 10 | TAG?=v$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_BUILD) 11 | FLAGS= 12 | ENVVAR=CGO_ENABLED=0 13 | LDFLAGS?=-s 14 | GOOS?=$(shell go env GOOS) 15 | GOARCH?=$(shell go env GOARCH) 16 | REGISTRY?=fred78290 17 | BUILD_DATE?=`date +%Y-%m-%dT%H:%M:%SZ` 18 | VERSION_LDFLAGS=-X main.phVersion=$(TAG) 19 | IMAGE=$(REGISTRY)/vsphere-autoscaler 20 | 21 | deps: 22 | go mod vendor 23 | # wget "https://raw.githubusercontent.com/Fred78290/autoscaler/master/cluster-autoscaler/cloudprovider/grpc/grpc.proto" -O grpc/grpc.proto 24 | # protoc -I . -I vendor grpc/grpc.proto --go_out=plugins=grpc:. 25 | 26 | build: $(addprefix build-arch-,$(ALL_ARCH)) 27 | 28 | build-arch-%: deps clean-arch-% 29 | $(ENVVAR) GOOS=$(GOOS) GOARCH=$* go build -buildvcs=false -ldflags="-X main.phVersion=$(TAG) -X main.phBuildDate=$(BUILD_DATE) ${LDFLAGS}" -a -o out/$(GOOS)/$*/vsphere-autoscaler 30 | 31 | test-unit: clean build 32 | go install github.com/vmware/govmomi/vcsim@v0.30.0 33 | bash ./scripts/run-tests.sh 34 | 35 | make-image: $(addprefix make-image-arch-,$(ALL_ARCH)) 36 | 37 | make-image-arch-%: 38 | docker build --pull -t ${IMAGE}-$*:${TAG} -f Dockerfile.$* . 39 | @echo "Image ${TAG}-$* completed" 40 | 41 | push-image: $(addprefix push-image-arch-,$(ALL_ARCH)) 42 | 43 | push-image-arch-%: 44 | docker push ${IMAGE}-$*:${TAG} 45 | 46 | push-manifest: 47 | docker buildx build --pull --platform linux/amd64,linux/arm64 --push -t ${IMAGE}:${TAG} . 48 | @echo "Image ${TAG}* completed" 49 | 50 | container-push-manifest: container push-manifest 51 | 52 | clean: $(addprefix clean-arch-,$(ALL_ARCH)) 53 | 54 | clean-arch-%: 55 | rm -f ./out/$(GOOS)/$*/vsphere-autoscaler 56 | 57 | docker-builder: 58 | docker build -t kubernetes-vmware-autoscaler-builder ./builder 59 | 60 | build-in-docker: $(addprefix build-in-docker-arch-,$(ALL_ARCH)) 61 | 62 | build-in-docker-arch-%: clean-arch-% docker-builder 63 | docker run --rm -v `pwd`:/gopath/src/github.com/Fred78290/vsphere-autoscaler/ kubernetes-vmware-autoscaler-builder:latest bash \ 64 | -c 'cd /gopath/src/github.com/Fred78290/vsphere-autoscaler \ 65 | && BUILD_TAGS=${BUILD_TAGS} make -e REGISTRY=${REGISTRY} -e TAG=${TAG} -e BUILD_DATE=`date +%Y-%m-%dT%H:%M:%SZ` build-arch-$*' 66 | 67 | container: $(addprefix container-arch-,$(ALL_ARCH)) 68 | 69 | container-arch-%: build-in-docker-arch-% 70 | @echo "Full in-docker image ${TAG}-$* completed" 71 | 72 | test-in-docker: docker-builder 73 | docker run --rm -v `pwd`:/gopath/src/github.com/Fred78290/kubernetes-vmware-autoscaler/ kubernetes-vmware-autoscaler-builder:latest bash \ 74 | -c 'cd /gopath/src/github.com/Fred78290/kubernetes-vmware-autoscaler && bash ./scripts/run-tests.sh' 75 | 76 | .PHONY: all build test-unit clean docker-builder build-in-docker push-image push-manifest 77 | -------------------------------------------------------------------------------- /artifacts/examples/controlplane-rke2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "nodemanager.aldunelabs.com/v1alpha1" 2 | kind: "ManagedNode" 3 | metadata: 4 | name: "vmware-dev-rke2-master-02" 5 | spec: 6 | nodegroup: vmware-dev-rke2 7 | controlPlane: true 8 | allowDeployment: false 9 | vcpus: 4 10 | memorySizeInMb: 4096 11 | diskSizeInMb: 20480 12 | labels: 13 | - demo-label.aldunelabs.com=demo 14 | - sample-label.aldunelabs.com=sample 15 | annotations: 16 | - demo-annotation.aldunelabs.com=demo 17 | - sample-annotation.aldunelabs.com=sample 18 | networks: 19 | - 20 | network: "VM Network" 21 | address: 10.0.0.121 22 | netmask: 255.255.255.0 23 | gateway: 10.0.0.1 24 | - 25 | network: "VM Private" 26 | address: 192.168.1.121 27 | netmask: 255.255.255.0 28 | -------------------------------------------------------------------------------- /artifacts/examples/controlplane.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "nodemanager.aldunelabs.com/v1alpha1" 2 | kind: "ManagedNode" 3 | metadata: 4 | name: "vmware-ca-k8s-master-02" 5 | spec: 6 | nodegroup: vmware-ca-k8s 7 | controlPlane: true 8 | allowDeployment: false 9 | vcpus: 4 10 | memorySizeInMb: 4096 11 | diskSizeInMb: 20480 12 | labels: 13 | - demo-label.aldunelabs.com=demo 14 | - sample-label.aldunelabs.com=sample 15 | annotations: 16 | - demo-annotation.aldunelabs.com=demo 17 | - sample-annotation.aldunelabs.com=sample 18 | networks: 19 | - 20 | network: "VM Network" 21 | address: 10.0.0.21 22 | netmask: 255.255.255.0 23 | gateway: 10.0.0.1 24 | - 25 | network: "VM Private" 26 | address: 192.168.1.21 27 | netmask: 255.255.255.0 28 | -------------------------------------------------------------------------------- /artifacts/examples/crd.yaml: -------------------------------------------------------------------------------- 1 | kind: CustomResourceDefinition 2 | apiVersion: apiextensions.k8s.io/v1 3 | metadata: 4 | name: managednodes.nodemanager.aldunelabs.com 5 | spec: 6 | group: nodemanager.aldunelabs.com 7 | names: 8 | plural: managednodes 9 | singular: managednode 10 | shortNames: 11 | - mn 12 | kind: ManagedNode 13 | listKind: ManagedNodeList 14 | scope: Cluster 15 | versions: 16 | - name: v1alpha1 17 | served: true 18 | storage: true 19 | schema: 20 | openAPIV3Schema: 21 | type: object 22 | properties: 23 | spec: 24 | type: object 25 | properties: 26 | allowDeployment: 27 | type: boolean 28 | annotations: 29 | type: array 30 | items: 31 | type: string 32 | controlPlane: 33 | type: boolean 34 | diskSizeInMb: 35 | type: integer 36 | default: 10240 37 | labels: 38 | type: array 39 | items: 40 | type: string 41 | memorySizeInMb: 42 | type: integer 43 | default: 2048 44 | networks: 45 | type: array 46 | items: 47 | type: object 48 | properties: 49 | address: 50 | type: string 51 | dhcp: 52 | type: boolean 53 | use-dhcp-routes: 54 | type: boolean 55 | gateway: 56 | type: string 57 | mac-address: 58 | type: string 59 | netmask: 60 | type: string 61 | network: 62 | type: string 63 | nodegroup: 64 | type: string 65 | vcpus: 66 | type: integer 67 | default: 2 68 | x-kubernetes-preserve-unknown-fields: true 69 | x-kubernetes-preserve-unknown-fields: true 70 | subresources: 71 | status: {} 72 | -------------------------------------------------------------------------------- /artifacts/examples/example.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "nodemanager.aldunelabs.com/v1alpha1" 2 | kind: "ManagedNode" 3 | metadata: 4 | name: "vmware-ca-k8s-managed-01" 5 | spec: 6 | nodegroup: vmware-ca-k8s 7 | controlPlane: false 8 | allowDeployment: false 9 | vcpus: 2 10 | memorySizeInMb: 2048 11 | diskSizeInMb: 10240 12 | labels: 13 | - demo-label.aldunelabs.com=demo 14 | - sample-label.aldunelabs.com=sample 15 | annotations: 16 | - demo-annotation.aldunelabs.com=demo 17 | - sample-annotation.aldunelabs.com=sample 18 | networks: 19 | - 20 | network: "VM Network" 21 | address: 10.0.0.80 22 | netmask: 255.255.255.0 23 | gateway: 10.0.0.1 24 | - 25 | network: "VM Private" 26 | address: 192.168.1.80 27 | netmask: 255.255.255.0 28 | --- 29 | apiVersion: "nodemanager.aldunelabs.com/v1alpha1" 30 | kind: "ManagedNode" 31 | metadata: 32 | name: "vmware-ca-k8s-managed-02" 33 | spec: 34 | nodegroup: vmware-ca-k8s 35 | controlPlane: false 36 | allowDeployment: false 37 | vcpus: 2 38 | memorySizeInMb: 2048 39 | diskSizeInMb: 10240 40 | labels: 41 | - demo-label.aldunelabs.com=demo 42 | - sample-label.aldunelabs.com=sample 43 | annotations: 44 | - demo-annotation.aldunelabs.com=demo 45 | - sample-annotation.aldunelabs.com=sample 46 | networks: 47 | - 48 | network: "VM Network" 49 | address: 10.0.0.81 50 | netmask: 255.255.255.0 51 | gateway: 10.0.0.1 52 | - 53 | network: "VM Private" 54 | address: 192.168.1.81 55 | netmask: 255.255.255.0 56 | --- 57 | apiVersion: "nodemanager.aldunelabs.com/v1alpha1" 58 | kind: "ManagedNode" 59 | metadata: 60 | name: "vmware-ca-k8s-managed-03" 61 | spec: 62 | nodegroup: vmware-ca-k8s 63 | controlPlane: false 64 | allowDeployment: false 65 | vcpus: 2 66 | memorySizeInMb: 2048 67 | diskSizeInMb: 10240 68 | labels: 69 | - demo-label.aldunelabs.com=demo 70 | - sample-label.aldunelabs.com=sample 71 | annotations: 72 | - demo-annotation.aldunelabs.com=demo 73 | - sample-annotation.aldunelabs.com=sample 74 | networks: 75 | - 76 | network: "VM Network" 77 | address: 10.0.0.82 78 | netmask: 255.255.255.0 79 | gateway: 10.0.0.1 80 | - 81 | network: "VM Private" 82 | dhcp: true 83 | use-dhcp-routes: false 84 | -------------------------------------------------------------------------------- /build-and-run.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | pushd $(dirname $0) 3 | 4 | make container 5 | GOOS=$(go env GOOS) 6 | GOARCH=$(go env GOARCH) 7 | ./out/$GOOS/$GOARCH/vsphere-autoscaler \ 8 | --request-timeout=120s \ 9 | --config=${HOME}/Projects/autoscaled-masterkube-vmware/config/vmware-ca-k8s/config/kubernetes-vmware-autoscaler.json \ 10 | --save=${HOME}/Projects/autoscaled-masterkube-vmware/config/vmware-ca-k8s/config/autoscaler-state.json \ 11 | --log-level=info 12 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo rm -rf out 3 | 4 | VERSION=v1.29.0 5 | REGISTRY=fred78290 6 | 7 | make -e REGISTRY=$REGISTRY -e TAG=$VERSION container-push-manifest 8 | -------------------------------------------------------------------------------- /builder/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Frederic Boltz Author. All rights reserved 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM golang:latest 16 | LABEL maintainer="Frederic Boltz " 17 | 18 | ENV GOPATH /gopath/ 19 | ENV PATH $GOPATH/bin:$PATH 20 | 21 | RUN apt-get update && apt-get --yes install libseccomp-dev && \ 22 | go version && \ 23 | go install github.com/vmware/govmomi/vcsim@v0.28.0 24 | CMD ["/bin/bash"] 25 | -------------------------------------------------------------------------------- /builder/README.md: -------------------------------------------------------------------------------- 1 | A Docker image that is used to build autoscaling-related binaries. -------------------------------------------------------------------------------- /constantes/annotations.go: -------------------------------------------------------------------------------- 1 | package constantes 2 | 3 | const ( 4 | // ResourceNameCores is string name for cores. It's used by ResourceLimiter. 5 | ResourceNameCores = "cpu" 6 | // ResourceNameMemory is string name for memory. It's used by ResourceLimiter. 7 | // Memory should always be provided in bytes. 8 | ResourceNameMemory = "memory" 9 | // ResourceNameNodes is string name for node. It's used by ResourceLimiter. 10 | ResourceNameNodes = "nodes" 11 | 12 | // ResourceNameManagedNodeDisk 13 | ResourceNameManagedNodeDisk = "disk" 14 | // ResourceNameManagedNodeMemory 15 | ResourceNameManagedNodeMemory = "memory" 16 | // ResourceNameManagedNodeCores 17 | ResourceNameManagedNodeCores = "cpus" 18 | ) 19 | 20 | const ( 21 | // NodeLabelControlPlaneRole k8s annotation 22 | NodeLabelControlPlaneRole = "node-role.kubernetes.io/control-plane" 23 | 24 | // NodeLabelMasterRole k8s annotation 25 | NodeLabelMasterRole = "node-role.kubernetes.io/master" 26 | 27 | // NodeLabelWorkerRole k8s annotation 28 | NodeLabelWorkerRole = "node-role.kubernetes.io/worker" 29 | 30 | // AnnotationNodeGroupName k8s annotation 31 | AnnotationNodeGroupName = "cluster.autoscaler.nodegroup/name" 32 | 33 | // AnnotationNodeIndex k8s annotation 34 | AnnotationNodeIndex = "cluster.autoscaler.nodegroup/node-index" 35 | 36 | // AnnotationInstanceID k8s annotation 37 | AnnotationInstanceID = "cluster.autoscaler.nodegroup/instance-id" 38 | 39 | // AnnotationNodeAutoProvisionned k8s annotation 40 | AnnotationNodeAutoProvisionned = "cluster.autoscaler.nodegroup/autoprovision" 41 | 42 | // AnnotationNodeManaged k8s annotation 43 | AnnotationNodeManaged = "cluster.autoscaler.nodegroup/managed" 44 | 45 | // AnnotationScaleDownDisabled k8s annotation 46 | AnnotationScaleDownDisabled = "cluster-autoscaler.kubernetes.io/scale-down-disabled" 47 | ) 48 | -------------------------------------------------------------------------------- /constantes/errors.go: -------------------------------------------------------------------------------- 1 | package constantes 2 | 3 | const ( 4 | // CloudProviderError is an error related to underlying infrastructure 5 | CloudProviderError = "cloudProviderError" 6 | 7 | // APICallError is an error related to communication with k8s API server 8 | APICallError = "apiCallError" 9 | 10 | // InternalError is an error inside Cluster Autoscaler 11 | InternalError = "internalError" 12 | 13 | // TransientError is an error that causes us to skip a single loop, but 14 | // does not require any additional action. 15 | TransientError = "transientError" 16 | ) 17 | 18 | const ( 19 | // ProviderName string 20 | ProviderName = "grpc" 21 | 22 | // ErrMismatchingProvider error msg 23 | ErrMismatchingProvider = "secret doesn't match with target server" 24 | 25 | // ErrNodeGroupNotFound error msg 26 | ErrNodeGroupNotFound = "node group %s not found" 27 | 28 | // ErrNodeGroupForNodeNotFound error msg 29 | ErrNodeGroupForNodeNotFound = "nodeGroup %s not found for Node %s" 30 | 31 | // ErrNodeNotFoundInNodeGroup error msg 32 | ErrNodeNotFoundInNodeGroup = "the node %s not found in node group %s" 33 | 34 | // ErrMachineTypeNotFound error msg 35 | ErrMachineTypeNotFound = "machine type %s not found" 36 | 37 | // ErrNodeGroupAlreadyExists error msg 38 | ErrNodeGroupAlreadyExists = "can't create node group: %s, already exists" 39 | 40 | // ErrUnableToCreateNodeGroup error msg 41 | ErrUnableToCreateNodeGroup = "can't create node group: %s, reason: %v" 42 | 43 | // ErrUnableToLaunchNodeGroupNotCreated error msg 44 | ErrUnableToLaunchNodeGroupNotCreated = "unable to launch group: %s, reason: node group is not created" 45 | 46 | // ErrUnableToLaunchNodeGroup error msg 47 | ErrUnableToLaunchNodeGroup = "unable to launch group: %s, fail to launch some VMs" 48 | 49 | // ErrUnableToDeleteNodeGroup error msg 50 | ErrUnableToDeleteNodeGroup = "can't delete node group: %s, reason: %v" 51 | 52 | // ErrCantUnmarshallNodeWithReason error msg 53 | ErrCantUnmarshallNodeWithReason = "can't unmarshall node definition:%s, reason: %v" 54 | 55 | // ErrCantUnmarshallNode error msg 56 | ErrCantUnmarshallNode = "can't unmarshall node definition[%d] in group %s" 57 | 58 | // ErrUnableToDeleteNode error msg 59 | ErrUnableToDeleteNode = "can't delete node: %s, because not owned by node group: %s" 60 | 61 | // ErrMinSizeReached error msg 62 | ErrMinSizeReached = "min size reached for group: %s, nodes will not be deleted" 63 | 64 | // ErrIncreaseSizeMustBePositive error msg 65 | ErrIncreaseSizeMustBePositive = "size increase must be positive" 66 | 67 | // ErrIncreaseSizeTooLarge error msg 68 | ErrIncreaseSizeTooLarge = "size increase too large, desired: %d max: %d" 69 | 70 | // ErrDecreaseSizeMustBeNegative error msg 71 | ErrDecreaseSizeMustBeNegative = "size decrease must be negative" 72 | 73 | // ErrDecreaseSizeAttemptDeleteNodes error msg 74 | ErrDecreaseSizeAttemptDeleteNodes = "attempt to delete existing nodes, targetSize: %d delta: %d existingNodes: %d" 75 | 76 | // ErrUnableToLaunchVM error msg 77 | ErrUnableToLaunchVM = "unable to launch the VM owned by node: %s, reason: %v" 78 | 79 | // ErrUnableToLaunchVMNodeGroupNotReady error msg 80 | ErrUnableToLaunchVMNodeGroupNotReady = "unable to launch the VM owned by node: %s, reason: launch group is not ready" 81 | 82 | // ErrUnableToDeleteVM error msg 83 | ErrUnableToDeleteVM = "unable to delete the VM owned by node: %s, reason: %v" 84 | 85 | // ErrVMAlreadyCreated error msg 86 | ErrVMAlreadyCreated = "unable to launch VM, %s is already created" 87 | 88 | // ErrVMAlreadyExists error msg 89 | ErrVMAlreadyExists = "the vm named: %s is already exists" 90 | 91 | // ErrUnableToMountPath error msg 92 | ErrUnableToMountPath = "unable to mount host path:%s into guest:%s for node:%s, reason: %v" 93 | 94 | // ErrTempFile error msg 95 | ErrTempFile = "can't create temp file, reason: %v" 96 | 97 | // ErrCloudInitMarshallError error msg 98 | ErrCloudInitMarshallError = "can't marshall cloud-init, reason: %v" 99 | 100 | // ErrCloudInitWriteError error msg 101 | ErrCloudInitWriteError = "can't write cloud-init, reason: %v" 102 | 103 | // ErrGetVMInfoFailed error msg 104 | ErrGetVMInfoFailed = "can't get the info for VM: %s, reason: %v" 105 | 106 | // ErrAutoScalerInfoNotFound error msg 107 | ErrAutoScalerInfoNotFound = "can't find the VM info from AutoScaler for VM: %s" 108 | 109 | // ErrManagedInfoNotFound error msg 110 | ErrManagedNodeNotFound = "can't find the VM info from AutoScaler for UID: %s" 111 | 112 | // ErrKubeAdmJoinFailed error msg 113 | ErrKubeAdmJoinFailed = "unable to join the master kubernetes node for VM: %s, reason: %v" 114 | 115 | // ErrKubeAdmJoinNotRunning error msg 116 | ErrKubeAdmJoinNotRunning = "could not join kubernetes master node, the VM: %s is not running" 117 | 118 | // ErrStopVMFailed error msg 119 | ErrStopVMFailed = "could not stop VM: %s, reason: %v" 120 | 121 | // ErrStartVMFailed error msg 122 | ErrStartVMFailed = "could not start VM: %s, reason: %v" 123 | 124 | // ErrDeleteVMFailed error msg 125 | ErrDeleteVMFailed = "could not delete VM: %s, reason: %v" 126 | 127 | // ErrUpdateEtcdSslFailed msg 128 | ErrUpdateEtcdSslFailed = "could not install etcd ssl on VM: %s, reason: %v" 129 | 130 | // ErrRecopyKubernetesPKIFailed msg 131 | ErrRecopyKubernetesPKIFailed = "could not copy kubernetes pki on VM: %s, reason: %v" 132 | 133 | // ErrVMNotFound error msg 134 | ErrVMNotFound = "unable to find VM: %s" 135 | 136 | // ErrVMStopFailed error msg 137 | ErrVMStopFailed = "unable to stop VM: %s before delete" 138 | 139 | // ErrPodListReturnError error msg 140 | ErrPodListReturnError = "unable to list pods on node %s, reason: %v" 141 | 142 | // ErrNodeGroupCleanupFailOnVM error msg 143 | ErrNodeGroupCleanupFailOnVM = "on node group: %s, failed to delete VM: %s, reason: %v" 144 | 145 | // ErrUncordonNodeReturnError error msg 146 | ErrUncordonNodeReturnError = "uncordon node: %s got error: %s" 147 | 148 | // ErrCordonNodeReturnError error msg 149 | ErrCordonNodeReturnError = "cordon node: %s got error: %s" 150 | 151 | // ErrDrainNodeReturnError error msg 152 | ErrDrainNodeReturnError = "drain node: %s got error: %s" 153 | 154 | // ErrDeleteNodeReturnError error msg 155 | ErrDeleteNodeReturnError = "delete node: %s got error: %s" 156 | 157 | // ErrLabelNodeReturnError error msg 158 | ErrLabelNodeReturnError = "set labels on node: %s got error: %s" 159 | 160 | // ErrTaintNodeReturnError error msg 161 | ErrTaintNodeReturnError = "taint node: %s got error: %s" 162 | 163 | // ErrAnnoteNodeReturnError error msg 164 | ErrAnnoteNodeReturnError = "set annotations on node: %s got error: %s" 165 | 166 | // ErrMissingNodeAnnotationError error msg 167 | ErrMissingNodeAnnotationError = "missing mandatories annotations on node: %s" 168 | 169 | // ErrNotImplemented error msg 170 | ErrNotImplemented = "not implemented" 171 | 172 | // ErrNodeIsNotReady error msg 173 | ErrNodeIsNotReady = "node %s is not ready" 174 | 175 | // ErrUnableToAutoProvisionNodeGroup error msg 176 | ErrUnableToAutoProvisionNodeGroup = "warning can't autoprovision node group, reason: %v" 177 | 178 | // ErrUnmarshallingError error msg 179 | ErrUnmarshallingError = "unable to unmarshall node: %s as json, reason: %v" 180 | 181 | // ErrMarshallingError error msg 182 | ErrMarshallingError = "unable to marshall node: %s as json, reason: %v" 183 | 184 | // ErrProviderIDNotConfigured error msg 185 | ErrProviderIDNotConfigured = "can't set provider ID for node: %s, reason: %v" 186 | 187 | // ErrVMNotProvisionnedByMe error msg 188 | ErrVMNotProvisionnedByMe = "the VM: %s is not provisionned by me" 189 | 190 | // ErrFailedToLoadServerState error msg 191 | ErrFailedToLoadServerState = "failed to load server state, reason: %v" 192 | 193 | // ErrFailedToSaveServerState error msg 194 | ErrFailedToSaveServerState = "failed to save server state, reason: %v" 195 | 196 | // ErrRsyncError error msg 197 | ErrRsyncError = "can't rsync folder for VM: %s, %s, reason: %v" 198 | 199 | // ErrUnableToEncodeGuestInfo error msg 200 | ErrUnableToEncodeGuestInfo = "unable to encode vmware guest info: %s, reason: %v" 201 | 202 | // ErrUnableToAddHardDrive error msg 203 | ErrUnableToAddHardDrive = "unable to add hard drive to VM:%s, reason: %v" 204 | 205 | // ErrUnableToAddNetworkCard error msg 206 | ErrUnableToAddNetworkCard = "unable to add network card to VM:%s, reason: %v" 207 | 208 | // ErrUnableToCreateDeviceChangeOp error msg 209 | ErrUnableToCreateDeviceChangeOp = "unable to create device change operation for VM:%s, reason: %v" 210 | 211 | // ErrCloudInitFailCreation error msg 212 | ErrCloudInitFailCreation = "unable to create cloud-init data for VM:%s, reason: %v" 213 | 214 | // ErrUnableToReconfigureVM error msg 215 | ErrUnableToReconfigureVM = "unable to reconfigure VM:%s, reason: %v" 216 | 217 | // WarnFailedVMNotDeleted warn msg 218 | WarnFailedVMNotDeleted = "the failed VM:%s is not deleted because status is:%v" 219 | 220 | // ErrPodEvictionAborted 221 | ErrPodEvictionAborted = "pod eviction aborted" 222 | 223 | // ErrUndefinedPod err msg 224 | ErrUndefinedPod = "cannot get pod %s/%s, reason: %v" 225 | 226 | // ErrCannotEvictPod err msg 227 | ErrCannotEvictPod = "cannot evict pod %s/%s, reason: %v" 228 | 229 | // ErrUnableToConfirmPodEviction err msg 230 | ErrUnableToConfirmPodEviction = "cannot confirm pod %s/%s was deleted, reason: %v" 231 | 232 | // ErrUnableToGetPodListOnNode err msg 233 | ErrUnableToGetPodListOnNode = "cannot get pods for node %s, reason: %v" 234 | 235 | // ErrUnableEvictAllPodsOnNode err msg 236 | ErrUnableEvictAllPodsOnNode = "cannot evict all pods on node: %s, reason: %v" 237 | 238 | // ErrTimeoutWhenWaitingEvictions err msg 239 | ErrTimeoutWhenWaitingEvictions = "timed out waiting for evictions to complete on node: %s" 240 | 241 | // ErrFatalMissingSSHKey err msg 242 | ErrFatalMissingSSHKey = "%s ssh key not found" 243 | 244 | // ErrFatalEtcdMissingOrUnreadable err msg 245 | ErrFatalEtcdMissingOrUnreadable = "%s etcd certs directory is missing or unreadable" 246 | 247 | // ErrFatalKubernetesPKIMissingOrUnreadable err msg 248 | ErrFatalKubernetesPKIMissingOrUnreadable = "%s kubernetes pki directory is missing or unreadable" 249 | ) 250 | -------------------------------------------------------------------------------- /context/context.go: -------------------------------------------------------------------------------- 1 | package context 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | // Context wrapper 9 | type Context struct { 10 | ctx context.Context 11 | cancel context.CancelFunc 12 | } 13 | 14 | func TODO() context.Context { 15 | return context.TODO() 16 | } 17 | 18 | func Background() context.Context { 19 | return context.Background() 20 | } 21 | 22 | // NewContext return a context. Timeout is in seconds, zero means no timeout 23 | func NewContext(timeout time.Duration) *Context { 24 | var ctx context.Context 25 | var cancel context.CancelFunc 26 | 27 | if timeout == 0 { 28 | ctx, cancel = context.WithCancel(context.Background()) 29 | } else { 30 | ctx, cancel = context.WithTimeout(context.Background(), timeout*time.Second) 31 | } 32 | 33 | return &Context{ 34 | ctx: ctx, 35 | cancel: cancel, 36 | } 37 | } 38 | 39 | // Deadline returns the time when work done on behalf of this context 40 | // should be canceled. Deadline returns ok==false when no deadline is 41 | // set. Successive calls to Deadline return the same results. 42 | func (c *Context) Deadline() (deadline time.Time, ok bool) { 43 | return c.ctx.Deadline() 44 | } 45 | 46 | // Done returns a channel that's closed when work done on behalf of this 47 | // context should be canceled. Done may return nil if this context can 48 | // never be canceled. Successive calls to Done return the same value. 49 | // 50 | // WithCancel arranges for Done to be closed when cancel is called; 51 | // WithDeadline arranges for Done to be closed when the deadline 52 | // expires; WithTimeout arranges for Done to be closed when the timeout 53 | // elapses. 54 | // 55 | // Done is provided for use in select statements: 56 | // 57 | // // Stream generates values with DoSomething and sends them to out 58 | // // until DoSomething returns an error or ctx.Done is closed. 59 | // func Stream(ctx context.Context, out chan<- Value) error { 60 | // for { 61 | // v, err := DoSomething(ctx) 62 | // if err != nil { 63 | // return err 64 | // } 65 | // select { 66 | // case <-ctx.Done(): 67 | // return ctx.Err() 68 | // case out <- v: 69 | // } 70 | // } 71 | // } 72 | // 73 | // See https://blog.golang.org/pipelines for more examples of how to use 74 | // a Done channel for cancelation. 75 | func (c *Context) Done() <-chan struct{} { 76 | return c.ctx.Done() 77 | } 78 | 79 | // Err as string 80 | // If Done is not yet closed, Err returns nil. 81 | // If Done is closed, Err returns a non-nil error explaining why: 82 | // Canceled if the context was canceled 83 | // or DeadlineExceeded if the context's deadline passed. 84 | // After Err returns a non-nil error, successive calls to Err return the same error. 85 | func (c *Context) Err() error { 86 | return c.ctx.Err() 87 | } 88 | 89 | // Value returns the value associated with this context for key, or nil 90 | // if no value is associated with key. Successive calls to Value with 91 | // the same key returns the same result. 92 | // 93 | // Use context values only for request-scoped data that transits 94 | // processes and API boundaries, not for passing optional parameters to 95 | // functions. 96 | // 97 | // A key identifies a specific value in a Context. Functions that wish 98 | // to store values in Context typically allocate a key in a global 99 | // variable then use that key as the argument to context.WithValue and 100 | // Context.Value. A key can be any type that supports equality; 101 | // packages should define keys as an unexported type to avoid 102 | // collisions. 103 | // 104 | // Packages that define a Context key should provide type-safe accessors 105 | // for the values stored using that key: 106 | // 107 | // // Package user defines a User type that's stored in Contexts. 108 | // package user 109 | // 110 | // import "context" 111 | // 112 | // // User is the type of value stored in the Contexts. 113 | // type User struct {...} 114 | // 115 | // // key is an unexported type for keys defined in this package. 116 | // // This prevents collisions with keys defined in other packages. 117 | // type key int 118 | // 119 | // // userKey is the key for user.User values in Contexts. It is 120 | // // unexported; clients use user.NewContext and user.FromContext 121 | // // instead of using this key directly. 122 | // var userKey key 123 | // 124 | // // NewContext returns a new Context that carries value u. 125 | // func NewContext(ctx context.Context, u *User) context.Context { 126 | // return context.WithValue(ctx, userKey, u) 127 | // } 128 | // 129 | // // FromContext returns the User value stored in ctx, if any. 130 | // func FromContext(ctx context.Context) (*User, bool) { 131 | // u, ok := ctx.Value(userKey).(*User) 132 | // return u, ok 133 | // } 134 | func (c *Context) Value(key interface{}) interface{} { 135 | return c.ctx.Value(key) 136 | } 137 | 138 | // Cancel the current context 139 | func (c *Context) Cancel() { 140 | c.cancel() 141 | } 142 | 143 | // WithValue returns a copy of parent in which the value associated with key is 144 | // val. 145 | // 146 | // Use context Values only for request-scoped data that transits processes and 147 | // APIs, not for passing optional parameters to functions. 148 | // 149 | // The provided key must be comparable and should not be of type 150 | // string or any other built-in type to avoid collisions between 151 | // packages using context. Users of WithValue should define their own 152 | // types for keys. To avoid allocating when assigning to an 153 | // interface{}, context keys often have concrete type 154 | // struct{}. Alternatively, exported context key variables' static 155 | // type should be a pointer or interface. 156 | func (c *Context) WithValue(key, val interface{}) { 157 | c.ctx = context.WithValue(c.ctx, key, val) 158 | } 159 | 160 | // Context returns the embed context object 161 | func (c *Context) Context() context.Context { 162 | return c.ctx 163 | } 164 | -------------------------------------------------------------------------------- /externalgrpc/protoc_grpc.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | CURDIR=$(dirname $0) 3 | 4 | pushd $CURDIR 5 | 6 | rm -rf *.go 7 | 8 | RELEASE=1.28.2 9 | AUTOSCALER_PATH="/tmp/autoscaler-cluster-autoscaler-${RELEASE}/cluster-autoscaler" 10 | 11 | rm -rf /tmp/autoscaler-cluster-autoscaler-${RELEASE} 12 | 13 | pushd /tmp 14 | curl -Ls https://github.com/kubernetes/autoscaler/archive/refs/tags/cluster-autoscaler-${RELEASE}.tar.gz -O cluster-autoscaler-${RELEASE}.tar.gz 15 | popd 16 | 17 | tar zxvf /tmp/cluster-autoscaler-${RELEASE}.tar.gz autoscaler-cluster-autoscaler-${RELEASE}/cluster-autoscaler/cloudprovider/externalgrpc/protos 18 | 19 | for FILE in autoscaler-cluster-autoscaler-${RELEASE}/cluster-autoscaler/cloudprovider/externalgrpc/protos/*.go 20 | do 21 | cat ${FILE} | sed 's/package protos/package externalgrpc/' > $(basename $FILE) 22 | done 23 | 24 | rm -rf /tmp/cluster-autoscaler-${RELEASE}.tar.gz autoscaler-cluster-autoscaler-${RELEASE} 25 | 26 | popd -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Fred78290/kubernetes-vmware-autoscaler 2 | 3 | go 1.21 4 | 5 | toolchain go1.21.5 6 | 7 | require ( 8 | github.com/alecthomas/kingpin v2.2.6+incompatible 9 | github.com/linki/instrumented_http v0.3.0 10 | github.com/sirupsen/logrus v1.9.3 11 | github.com/stretchr/testify v1.8.4 12 | github.com/vmware/govmomi v0.34.0 13 | golang.org/x/crypto v0.17.0 14 | google.golang.org/grpc v1.60.1 15 | google.golang.org/protobuf v1.31.0 16 | gopkg.in/yaml.v2 v2.4.0 17 | k8s.io/api v0.29.0 18 | k8s.io/apiextensions-apiserver v0.29.0 19 | k8s.io/apimachinery v0.29.0 20 | k8s.io/client-go v0.29.0 21 | k8s.io/code-generator v0.29.0 22 | ) 23 | 24 | require ( 25 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect 26 | github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect 27 | github.com/beorn7/perks v1.0.1 // indirect 28 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 29 | github.com/davecgh/go-spew v1.1.1 // indirect 30 | github.com/dougm/pretty v0.0.0-20171025230240-2ee9d7453c02 // indirect 31 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect 32 | github.com/evanphx/json-patch v4.12.0+incompatible // indirect 33 | github.com/go-logr/logr v1.3.0 // indirect 34 | github.com/go-openapi/jsonpointer v0.19.6 // indirect 35 | github.com/go-openapi/jsonreference v0.20.2 // indirect 36 | github.com/go-openapi/swag v0.22.3 // indirect 37 | github.com/gogo/protobuf v1.3.2 // indirect 38 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 39 | github.com/golang/protobuf v1.5.3 // indirect 40 | github.com/google/gnostic-models v0.6.8 // indirect 41 | github.com/google/go-cmp v0.6.0 // indirect 42 | github.com/google/gofuzz v1.2.0 // indirect 43 | github.com/google/uuid v1.4.0 // indirect 44 | github.com/imdario/mergo v0.3.6 // indirect 45 | github.com/josharian/intern v1.0.0 // indirect 46 | github.com/json-iterator/go v1.1.12 // indirect 47 | github.com/kr/text v0.2.0 // indirect 48 | github.com/mailru/easyjson v0.7.7 // indirect 49 | github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect 50 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 51 | github.com/modern-go/reflect2 v1.0.2 // indirect 52 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 53 | github.com/pkg/errors v0.9.1 // indirect 54 | github.com/pmezard/go-difflib v1.0.0 // indirect 55 | github.com/prometheus/client_golang v1.16.0 // indirect 56 | github.com/prometheus/client_model v0.4.0 // indirect 57 | github.com/prometheus/common v0.44.0 // indirect 58 | github.com/prometheus/procfs v0.10.1 // indirect 59 | github.com/spf13/pflag v1.0.5 // indirect 60 | golang.org/x/mod v0.12.0 // indirect 61 | golang.org/x/net v0.17.0 // indirect 62 | golang.org/x/oauth2 v0.13.0 // indirect 63 | golang.org/x/sys v0.15.0 // indirect 64 | golang.org/x/term v0.15.0 // indirect 65 | golang.org/x/text v0.14.0 // indirect 66 | golang.org/x/time v0.3.0 // indirect 67 | golang.org/x/tools v0.12.0 // indirect 68 | google.golang.org/appengine v1.6.8 // indirect 69 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect 70 | gopkg.in/inf.v0 v0.9.1 // indirect 71 | gopkg.in/yaml.v3 v3.0.1 // indirect 72 | k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 // indirect 73 | k8s.io/klog/v2 v2.110.1 // indirect 74 | k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect 75 | k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect 76 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect 77 | sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect 78 | sigs.k8s.io/yaml v1.3.0 // indirect 79 | ) 80 | -------------------------------------------------------------------------------- /grpc/grpc.proto: -------------------------------------------------------------------------------- 1 | ../../../../k8s.io/autoscaler/cluster-autoscaler/cloudprovider/grpccloudprovider/grpc.proto -------------------------------------------------------------------------------- /grpc/protoc_grpc.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | CURDIR=$(dirname $0) 3 | PB_RELEASE="25.1" 4 | PB_REL="https://github.com/protocolbuffers/protobuf/releases" 5 | 6 | export PROTOC_DIR="/tmp/protoc-${PB_RELEASE}" 7 | export GOPATH=$PROTOC_DIR 8 | export GO111MODULE=on 9 | export PATH=$PROTOC_DIR/bin:$PATH 10 | 11 | mkdir -p $PROTOC_DIR 12 | 13 | pushd $PROTOC_DIR 14 | 15 | go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.31.0 16 | go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.3.0 17 | 18 | if [ "$(uname)" = "Darwin" ]; then 19 | curl -sLO ${PB_REL}/download/v${PB_RELEASE}/protoc-${PB_RELEASE}-osx-universal_binary.zip 20 | unzip protoc-${PB_RELEASE}-osx-universal_binary.zip 21 | else 22 | curl -sLO ${PB_REL}/download/v${PB_RELEASE}/protoc-${PB_RELEASE}-linux-x86_64.zip 23 | unzip protoc-${PB_RELEASE}-linux-x86_64.zip 24 | fi 25 | 26 | popd 27 | 28 | $PROTOC_DIR/bin/protoc -I . -I vendor --proto_path=grpc/ --go_out=. --go-grpc_out=. grpc.proto 29 | 30 | pushd $CURDIR 31 | cp ./grpccloudprovider/*.go . 32 | rm -rf ./grpccloudprovider 33 | popd 34 | 35 | sudo rm -rf $PROTOC_DIR 36 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright YEAR Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | -------------------------------------------------------------------------------- /hack/custom-boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright YEAR The Kubernetes sample-controller Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | -------------------------------------------------------------------------------- /hack/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | /* 5 | Copyright 2019 The Kubernetes Authors. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | // This package imports things required by build scripts, to force `go mod` to see them as dependencies 21 | package tools 22 | 23 | import _ "k8s.io/code-generator" 24 | -------------------------------------------------------------------------------- /hack/update-codegen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2017 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | set -o pipefail 20 | 21 | SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. 22 | CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${SCRIPT_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)} 23 | 24 | ls ${SCRIPT_ROOT}/../../../ 25 | 26 | # generate the code with: 27 | # --output-base because this script should also be able to run inside the vendor dir of 28 | # k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir 29 | # instead of the $GOPATH directly. For normal projects this can be dropped. 30 | bash "${CODEGEN_PKG}"/generate-groups.sh "deepcopy,client,informer,lister" \ 31 | github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated \ 32 | github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/apis \ 33 | nodemanager:v1alpha1 \ 34 | --output-base "${SCRIPT_ROOT}/../../../" \ 35 | --go-header-file "${SCRIPT_ROOT}"/hack/boilerplate.go.txt 36 | 37 | # To use your own boilerplate text append: 38 | # --go-header-file "${SCRIPT_ROOT}"/hack/custom-boilerplate.go.txt 39 | -------------------------------------------------------------------------------- /hack/verify-codegen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2017 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | set -o pipefail 20 | 21 | SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. 22 | 23 | DIFFROOT="${SCRIPT_ROOT}/pkg" 24 | TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/pkg" 25 | _tmp="${SCRIPT_ROOT}/_tmp" 26 | 27 | cleanup() { 28 | rm -rf "${_tmp}" 29 | } 30 | trap "cleanup" EXIT SIGINT 31 | 32 | cleanup 33 | 34 | mkdir -p "${TMP_DIFFROOT}" 35 | cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}" 36 | 37 | "${SCRIPT_ROOT}/hack/update-codegen.sh" 38 | echo "diffing ${DIFFROOT} against freshly generated codegen" 39 | ret=0 40 | diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$? 41 | cp -a "${TMP_DIFFROOT}"/* "${DIFFROOT}" 42 | if [[ $ret -eq 0 ]] 43 | then 44 | echo "${DIFFROOT} up to date." 45 | else 46 | echo "${DIFFROOT} is out of date. Please run hack/update-codegen.sh" 47 | exit 1 48 | fi 49 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Fred78290. https://github.com/Fred78290/ 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "log" 21 | "os" 22 | 23 | "github.com/Fred78290/kubernetes-vmware-autoscaler/client" 24 | "github.com/Fred78290/kubernetes-vmware-autoscaler/server" 25 | "github.com/Fred78290/kubernetes-vmware-autoscaler/types" 26 | glog "github.com/sirupsen/logrus" 27 | ) 28 | 29 | var phVersion = "v0.0.0-unset" 30 | var phBuildDate = "" 31 | 32 | func main() { 33 | cfg := types.NewConfig() 34 | 35 | if err := cfg.ParseFlags(os.Args[1:], phVersion); err != nil { 36 | log.Fatalf("flag parsing error: %v", err) 37 | } 38 | 39 | ll, err := glog.ParseLevel(cfg.LogLevel) 40 | if err != nil { 41 | glog.Fatalf("failed to parse log level: %v", err) 42 | } 43 | 44 | glog.SetLevel(ll) 45 | 46 | if cfg.LogFormat == "json" { 47 | glog.SetFormatter(&glog.JSONFormatter{}) 48 | } 49 | 50 | glog.Infof("config: %s", cfg) 51 | 52 | if cfg.DisplayVersion { 53 | glog.Infof("The current version is:%s, build at:%s", phVersion, phBuildDate) 54 | } else { 55 | var err error 56 | 57 | glog.Infof("Start controller version: %s, build at:%s", phVersion, phBuildDate) 58 | 59 | generator := client.NewClientGenerator(cfg) 60 | 61 | if _, err = generator.NodeList(); err != nil { 62 | glog.Fatalf("Can't validate config, reason:%s", err) 63 | } 64 | 65 | server.StartServer(generator, cfg) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /masterkube/README.md: -------------------------------------------------------------------------------- 1 | # INFO 2 | 3 | This contain has been moved to a separated projects: [autoscaled-masterkube-vmware](https://github.com/Fred78290/autoscaled-masterkube-vmware). 4 | -------------------------------------------------------------------------------- /pkg/apis/nodemanager/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package nodemanager 18 | 19 | import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 20 | 21 | // GroupName is the group name used in this package 22 | const ( 23 | GroupName = "nodemanager.aldunelabs.com" 24 | GroupVersion = "v1alpha1" 25 | CRDKind = "ManagedNode" 26 | CRDSingular = "managednode" 27 | CRDPlural = "managednodes" 28 | CRDShortName = "mn" 29 | FullCRDName = CRDPlural + "." + GroupName 30 | ) 31 | 32 | const ( 33 | StatusManagedNodeNeedToCreated = iota 34 | StatusManagedNodeCreation 35 | StatusManagedNodeCreated 36 | StatusManagedNodeCreationFailed 37 | StatusManagedNodeDeletion 38 | StatusManagedNodeDeleted 39 | StatusManagedNodeGroupNotFound 40 | StatusManagedNodeNiceLimitReached 41 | ) 42 | 43 | var phStatusManagedNodeReason = []metav1.StatusReason{ 44 | "StatusManagedNodeNeedToCreated", 45 | "StatusManagedNodeCreation", 46 | "StatusManagedNodeCreated", 47 | "StatusManagedNodeCreationFailed", 48 | "StatusManagedNodeDeletion", 49 | "StatusManagedNodeDeleted", 50 | "StatusManagedNodeGroupNotFound", 51 | "StatusManagedNodeNiceLimitReached", 52 | } 53 | 54 | func StatusManagedNodeReason(code int) metav1.StatusReason { 55 | if code >= StatusManagedNodeNeedToCreated && code <= StatusManagedNodeNiceLimitReached { 56 | return phStatusManagedNodeReason[code] 57 | } 58 | 59 | return "StatusManagedNodeUndefined" 60 | } 61 | -------------------------------------------------------------------------------- /pkg/apis/nodemanager/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // +k8s:deepcopy-gen=package 18 | // +groupName=nodemanager.aldunelabs.com 19 | 20 | // Package v1alpha1 is the v1alpha1 version of the API. 21 | package v1alpha1 // import "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/apis/nodemanager/v1alpha1" 22 | -------------------------------------------------------------------------------- /pkg/apis/nodemanager/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | nodemanager "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/apis/nodemanager" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | "k8s.io/apimachinery/pkg/runtime" 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | ) 9 | 10 | // SchemeGroupVersion is group version used to register these objects 11 | var SchemeGroupVersion = schema.GroupVersion{Group: nodemanager.GroupName, Version: nodemanager.GroupVersion} 12 | var SchemeGroupVersionKind = SchemeGroupVersion.WithKind(nodemanager.CRDKind) 13 | 14 | // Kind takes an unqualified kind and returns back a Group qualified GroupKind 15 | func Kind(kind string) schema.GroupKind { 16 | return SchemeGroupVersion.WithKind(kind).GroupKind() 17 | } 18 | 19 | // Resource takes an unqualified resource and returns a Group qualified GroupResource 20 | func Resource(resource string) schema.GroupResource { 21 | return SchemeGroupVersion.WithResource(resource).GroupResource() 22 | } 23 | 24 | var ( 25 | SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 26 | AddToScheme = SchemeBuilder.AddToScheme 27 | ) 28 | 29 | func addKnownTypes(scheme *runtime.Scheme) error { 30 | scheme.AddKnownTypes(SchemeGroupVersion, 31 | &ManagedNode{}, 32 | &ManagedNodeList{}, 33 | ) 34 | 35 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 36 | 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /pkg/apis/nodemanager/v1alpha1/types.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | ) 6 | 7 | // +genclient 8 | // +genclient:nonNamespaced 9 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 10 | 11 | // ManagedNode is a specification for a ManagedNode resource 12 | type ManagedNode struct { 13 | metav1.TypeMeta `json:",inline"` 14 | metav1.ObjectMeta `json:"metadata,omitempty"` 15 | Spec ManagedNodeSpec `json:"spec,omitempty"` 16 | Status ManagedNodeStatus `json:"status,omitempty"` 17 | } 18 | 19 | // NetworkRoutes is a specification for a network route ManagedNode resource 20 | type NetworkRoutes struct { 21 | To string `json:"to,omitempty" yaml:"to,omitempty"` 22 | Via string `json:"via,omitempty" yaml:"via,omitempty"` 23 | Metric int `json:"metric,omitempty" yaml:"metric,omitempty"` 24 | } 25 | 26 | // ManagedNodeNetwork is a specification for a network ManagedNode resource 27 | type ManagedNodeNetwork struct { 28 | NetworkName string `json:"network,omitempty"` 29 | DHCP bool `json:"dhcp,omitempty"` 30 | UseRoutes bool `default:"true" json:"use-dhcp-routes,omitempty" yaml:"use-dhcp-routes,omitempty"` 31 | IPV4Address string `json:"address,omitempty"` 32 | Gateway string `json:"gateway,omitempty"` 33 | Netmask string `json:"netmask,omitempty"` 34 | MacAddress string `json:"mac-address,omitempty" yaml:"mac-address,omitempty"` 35 | Routes []NetworkRoutes `json:"routes,omitempty" yaml:"routes,omitempty"` 36 | } 37 | 38 | // ManagedNodeSpec is the spec for a ManagedNode resource 39 | type ManagedNodeSpec struct { 40 | Nodegroup string `default:"vmware-ca-k8s" json:"nodegroup,omitempty"` 41 | ControlPlane bool `json:"controlPlane,omitempty"` 42 | AllowDeployment bool `json:"allowDeployment,omitempty"` 43 | VCpus int `default:"2" json:"vcpus"` 44 | MemorySize int `default:"2048" json:"memorySizeInMb"` 45 | DiskSize int `default:"10240" json:"diskSizeInMb"` 46 | Labels []string `json:"labels,omitempty"` 47 | Annotations []string `json:"annotations,omitempty"` 48 | NetworkManagement []ManagedNodeNetwork `json:"networks,omitempty"` 49 | } 50 | 51 | // ManagedNodeStatus is the status for a ManagedNode resource 52 | type ManagedNodeStatus struct { 53 | // The last time this status was updated. 54 | LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"` 55 | // The node name created 56 | NodeName string `json:"nodename,omitempty"` 57 | // A human-readable description of the status of this operation. 58 | // +optional 59 | Message string `json:"message,omitempty"` 60 | // A machine-readable description of why this operation is in the 61 | // "Failure" status. If this value is empty there 62 | // is no information available. A Reason clarifies an HTTP status 63 | // code but does not override it. 64 | // +optional 65 | Reason metav1.StatusReason `json:"reason,omitempty"` 66 | // Suggested HTTP return code for this status, 0 if not set. 67 | // +optional 68 | Code int32 `json:"code,omitempty"` 69 | } 70 | 71 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 72 | 73 | // ManagedNodeList is a list of ManagedNode resources 74 | type ManagedNodeList struct { 75 | metav1.TypeMeta `json:",inline"` 76 | metav1.ListMeta `json:"metadata,omitempty"` 77 | 78 | Items []ManagedNode `json:"items"` 79 | } 80 | 81 | func (mn *ManagedNode) GetNodegroup() string { 82 | return mn.Spec.Nodegroup 83 | } 84 | -------------------------------------------------------------------------------- /pkg/apis/nodemanager/v1alpha1/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | // +build !ignore_autogenerated 3 | 4 | /* 5 | Copyright 2023 Frédéric Boltz. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | // Code generated by deepcopy-gen. DO NOT EDIT. 21 | 22 | package v1alpha1 23 | 24 | import ( 25 | runtime "k8s.io/apimachinery/pkg/runtime" 26 | ) 27 | 28 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 29 | func (in *ManagedNode) DeepCopyInto(out *ManagedNode) { 30 | *out = *in 31 | out.TypeMeta = in.TypeMeta 32 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 33 | in.Spec.DeepCopyInto(&out.Spec) 34 | in.Status.DeepCopyInto(&out.Status) 35 | return 36 | } 37 | 38 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagedNode. 39 | func (in *ManagedNode) DeepCopy() *ManagedNode { 40 | if in == nil { 41 | return nil 42 | } 43 | out := new(ManagedNode) 44 | in.DeepCopyInto(out) 45 | return out 46 | } 47 | 48 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 49 | func (in *ManagedNode) DeepCopyObject() runtime.Object { 50 | if c := in.DeepCopy(); c != nil { 51 | return c 52 | } 53 | return nil 54 | } 55 | 56 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 57 | func (in *ManagedNodeList) DeepCopyInto(out *ManagedNodeList) { 58 | *out = *in 59 | out.TypeMeta = in.TypeMeta 60 | in.ListMeta.DeepCopyInto(&out.ListMeta) 61 | if in.Items != nil { 62 | in, out := &in.Items, &out.Items 63 | *out = make([]ManagedNode, len(*in)) 64 | for i := range *in { 65 | (*in)[i].DeepCopyInto(&(*out)[i]) 66 | } 67 | } 68 | return 69 | } 70 | 71 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagedNodeList. 72 | func (in *ManagedNodeList) DeepCopy() *ManagedNodeList { 73 | if in == nil { 74 | return nil 75 | } 76 | out := new(ManagedNodeList) 77 | in.DeepCopyInto(out) 78 | return out 79 | } 80 | 81 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 82 | func (in *ManagedNodeList) DeepCopyObject() runtime.Object { 83 | if c := in.DeepCopy(); c != nil { 84 | return c 85 | } 86 | return nil 87 | } 88 | 89 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 90 | func (in *ManagedNodeNetwork) DeepCopyInto(out *ManagedNodeNetwork) { 91 | *out = *in 92 | if in.Routes != nil { 93 | in, out := &in.Routes, &out.Routes 94 | *out = make([]NetworkRoutes, len(*in)) 95 | copy(*out, *in) 96 | } 97 | return 98 | } 99 | 100 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagedNodeNetwork. 101 | func (in *ManagedNodeNetwork) DeepCopy() *ManagedNodeNetwork { 102 | if in == nil { 103 | return nil 104 | } 105 | out := new(ManagedNodeNetwork) 106 | in.DeepCopyInto(out) 107 | return out 108 | } 109 | 110 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 111 | func (in *ManagedNodeSpec) DeepCopyInto(out *ManagedNodeSpec) { 112 | *out = *in 113 | if in.Labels != nil { 114 | in, out := &in.Labels, &out.Labels 115 | *out = make([]string, len(*in)) 116 | copy(*out, *in) 117 | } 118 | if in.Annotations != nil { 119 | in, out := &in.Annotations, &out.Annotations 120 | *out = make([]string, len(*in)) 121 | copy(*out, *in) 122 | } 123 | if in.NetworkManagement != nil { 124 | in, out := &in.NetworkManagement, &out.NetworkManagement 125 | *out = make([]ManagedNodeNetwork, len(*in)) 126 | for i := range *in { 127 | (*in)[i].DeepCopyInto(&(*out)[i]) 128 | } 129 | } 130 | return 131 | } 132 | 133 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagedNodeSpec. 134 | func (in *ManagedNodeSpec) DeepCopy() *ManagedNodeSpec { 135 | if in == nil { 136 | return nil 137 | } 138 | out := new(ManagedNodeSpec) 139 | in.DeepCopyInto(out) 140 | return out 141 | } 142 | 143 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 144 | func (in *ManagedNodeStatus) DeepCopyInto(out *ManagedNodeStatus) { 145 | *out = *in 146 | in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) 147 | return 148 | } 149 | 150 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagedNodeStatus. 151 | func (in *ManagedNodeStatus) DeepCopy() *ManagedNodeStatus { 152 | if in == nil { 153 | return nil 154 | } 155 | out := new(ManagedNodeStatus) 156 | in.DeepCopyInto(out) 157 | return out 158 | } 159 | 160 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 161 | func (in *NetworkRoutes) DeepCopyInto(out *NetworkRoutes) { 162 | *out = *in 163 | return 164 | } 165 | 166 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkRoutes. 167 | func (in *NetworkRoutes) DeepCopy() *NetworkRoutes { 168 | if in == nil { 169 | return nil 170 | } 171 | out := new(NetworkRoutes) 172 | in.DeepCopyInto(out) 173 | return out 174 | } 175 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/clientset.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package versioned 20 | 21 | import ( 22 | "fmt" 23 | "net/http" 24 | 25 | nodemanagerv1alpha1 "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/clientset/versioned/typed/nodemanager/v1alpha1" 26 | discovery "k8s.io/client-go/discovery" 27 | rest "k8s.io/client-go/rest" 28 | flowcontrol "k8s.io/client-go/util/flowcontrol" 29 | ) 30 | 31 | type Interface interface { 32 | Discovery() discovery.DiscoveryInterface 33 | NodemanagerV1alpha1() nodemanagerv1alpha1.NodemanagerV1alpha1Interface 34 | } 35 | 36 | // Clientset contains the clients for groups. 37 | type Clientset struct { 38 | *discovery.DiscoveryClient 39 | nodemanagerV1alpha1 *nodemanagerv1alpha1.NodemanagerV1alpha1Client 40 | } 41 | 42 | // NodemanagerV1alpha1 retrieves the NodemanagerV1alpha1Client 43 | func (c *Clientset) NodemanagerV1alpha1() nodemanagerv1alpha1.NodemanagerV1alpha1Interface { 44 | return c.nodemanagerV1alpha1 45 | } 46 | 47 | // Discovery retrieves the DiscoveryClient 48 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 49 | if c == nil { 50 | return nil 51 | } 52 | return c.DiscoveryClient 53 | } 54 | 55 | // NewForConfig creates a new Clientset for the given config. 56 | // If config's RateLimiter is not set and QPS and Burst are acceptable, 57 | // NewForConfig will generate a rate-limiter in configShallowCopy. 58 | // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), 59 | // where httpClient was generated with rest.HTTPClientFor(c). 60 | func NewForConfig(c *rest.Config) (*Clientset, error) { 61 | configShallowCopy := *c 62 | 63 | if configShallowCopy.UserAgent == "" { 64 | configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent() 65 | } 66 | 67 | // share the transport between all clients 68 | httpClient, err := rest.HTTPClientFor(&configShallowCopy) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | return NewForConfigAndClient(&configShallowCopy, httpClient) 74 | } 75 | 76 | // NewForConfigAndClient creates a new Clientset for the given config and http client. 77 | // Note the http client provided takes precedence over the configured transport values. 78 | // If config's RateLimiter is not set and QPS and Burst are acceptable, 79 | // NewForConfigAndClient will generate a rate-limiter in configShallowCopy. 80 | func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, error) { 81 | configShallowCopy := *c 82 | if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { 83 | if configShallowCopy.Burst <= 0 { 84 | return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") 85 | } 86 | configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) 87 | } 88 | 89 | var cs Clientset 90 | var err error 91 | cs.nodemanagerV1alpha1, err = nodemanagerv1alpha1.NewForConfigAndClient(&configShallowCopy, httpClient) 92 | if err != nil { 93 | return nil, err 94 | } 95 | 96 | cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient) 97 | if err != nil { 98 | return nil, err 99 | } 100 | return &cs, nil 101 | } 102 | 103 | // NewForConfigOrDie creates a new Clientset for the given config and 104 | // panics if there is an error in the config. 105 | func NewForConfigOrDie(c *rest.Config) *Clientset { 106 | cs, err := NewForConfig(c) 107 | if err != nil { 108 | panic(err) 109 | } 110 | return cs 111 | } 112 | 113 | // New creates a new Clientset for the given RESTClient. 114 | func New(c rest.Interface) *Clientset { 115 | var cs Clientset 116 | cs.nodemanagerV1alpha1 = nodemanagerv1alpha1.New(c) 117 | 118 | cs.DiscoveryClient = discovery.NewDiscoveryClient(c) 119 | return &cs 120 | } 121 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/fake/clientset_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | clientset "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/clientset/versioned" 23 | nodemanagerv1alpha1 "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/clientset/versioned/typed/nodemanager/v1alpha1" 24 | fakenodemanagerv1alpha1 "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/clientset/versioned/typed/nodemanager/v1alpha1/fake" 25 | "k8s.io/apimachinery/pkg/runtime" 26 | "k8s.io/apimachinery/pkg/watch" 27 | "k8s.io/client-go/discovery" 28 | fakediscovery "k8s.io/client-go/discovery/fake" 29 | "k8s.io/client-go/testing" 30 | ) 31 | 32 | // NewSimpleClientset returns a clientset that will respond with the provided objects. 33 | // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, 34 | // without applying any validations and/or defaults. It shouldn't be considered a replacement 35 | // for a real clientset and is mostly useful in simple unit tests. 36 | func NewSimpleClientset(objects ...runtime.Object) *Clientset { 37 | o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) 38 | for _, obj := range objects { 39 | if err := o.Add(obj); err != nil { 40 | panic(err) 41 | } 42 | } 43 | 44 | cs := &Clientset{tracker: o} 45 | cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} 46 | cs.AddReactor("*", "*", testing.ObjectReaction(o)) 47 | cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { 48 | gvr := action.GetResource() 49 | ns := action.GetNamespace() 50 | watch, err := o.Watch(gvr, ns) 51 | if err != nil { 52 | return false, nil, err 53 | } 54 | return true, watch, nil 55 | }) 56 | 57 | return cs 58 | } 59 | 60 | // Clientset implements clientset.Interface. Meant to be embedded into a 61 | // struct to get a default implementation. This makes faking out just the method 62 | // you want to test easier. 63 | type Clientset struct { 64 | testing.Fake 65 | discovery *fakediscovery.FakeDiscovery 66 | tracker testing.ObjectTracker 67 | } 68 | 69 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 70 | return c.discovery 71 | } 72 | 73 | func (c *Clientset) Tracker() testing.ObjectTracker { 74 | return c.tracker 75 | } 76 | 77 | var ( 78 | _ clientset.Interface = &Clientset{} 79 | _ testing.FakeClient = &Clientset{} 80 | ) 81 | 82 | // NodemanagerV1alpha1 retrieves the NodemanagerV1alpha1Client 83 | func (c *Clientset) NodemanagerV1alpha1() nodemanagerv1alpha1.NodemanagerV1alpha1Interface { 84 | return &fakenodemanagerv1alpha1.FakeNodemanagerV1alpha1{Fake: &c.Fake} 85 | } 86 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated fake clientset. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/fake/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | nodemanagerv1alpha1 "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/apis/nodemanager/v1alpha1" 23 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | runtime "k8s.io/apimachinery/pkg/runtime" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 27 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 28 | ) 29 | 30 | var scheme = runtime.NewScheme() 31 | var codecs = serializer.NewCodecFactory(scheme) 32 | 33 | var localSchemeBuilder = runtime.SchemeBuilder{ 34 | nodemanagerv1alpha1.AddToScheme, 35 | } 36 | 37 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 38 | // of clientsets, like in: 39 | // 40 | // import ( 41 | // "k8s.io/client-go/kubernetes" 42 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 43 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 44 | // ) 45 | // 46 | // kclientset, _ := kubernetes.NewForConfig(c) 47 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 48 | // 49 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 50 | // correctly. 51 | var AddToScheme = localSchemeBuilder.AddToScheme 52 | 53 | func init() { 54 | v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) 55 | utilruntime.Must(AddToScheme(scheme)) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package contains the scheme of the automatically generated clientset. 20 | package scheme 21 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/scheme/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package scheme 20 | 21 | import ( 22 | nodemanagerv1alpha1 "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/apis/nodemanager/v1alpha1" 23 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 | runtime "k8s.io/apimachinery/pkg/runtime" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 27 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 28 | ) 29 | 30 | var Scheme = runtime.NewScheme() 31 | var Codecs = serializer.NewCodecFactory(Scheme) 32 | var ParameterCodec = runtime.NewParameterCodec(Scheme) 33 | var localSchemeBuilder = runtime.SchemeBuilder{ 34 | nodemanagerv1alpha1.AddToScheme, 35 | } 36 | 37 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 38 | // of clientsets, like in: 39 | // 40 | // import ( 41 | // "k8s.io/client-go/kubernetes" 42 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 43 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 44 | // ) 45 | // 46 | // kclientset, _ := kubernetes.NewForConfig(c) 47 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 48 | // 49 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 50 | // correctly. 51 | var AddToScheme = localSchemeBuilder.AddToScheme 52 | 53 | func init() { 54 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 55 | utilruntime.Must(AddToScheme(Scheme)) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/nodemanager/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated typed clients. 20 | package v1alpha1 21 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/nodemanager/v1alpha1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // Package fake has the automatically generated clients. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/nodemanager/v1alpha1/fake/fake_managednode.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | "context" 23 | 24 | v1alpha1 "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/apis/nodemanager/v1alpha1" 25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | labels "k8s.io/apimachinery/pkg/labels" 27 | types "k8s.io/apimachinery/pkg/types" 28 | watch "k8s.io/apimachinery/pkg/watch" 29 | testing "k8s.io/client-go/testing" 30 | ) 31 | 32 | // FakeManagedNodes implements ManagedNodeInterface 33 | type FakeManagedNodes struct { 34 | Fake *FakeNodemanagerV1alpha1 35 | } 36 | 37 | var managednodesResource = v1alpha1.SchemeGroupVersion.WithResource("managednodes") 38 | 39 | var managednodesKind = v1alpha1.SchemeGroupVersion.WithKind("ManagedNode") 40 | 41 | // Get takes name of the managedNode, and returns the corresponding managedNode object, and an error if there is any. 42 | func (c *FakeManagedNodes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ManagedNode, err error) { 43 | obj, err := c.Fake. 44 | Invokes(testing.NewRootGetAction(managednodesResource, name), &v1alpha1.ManagedNode{}) 45 | if obj == nil { 46 | return nil, err 47 | } 48 | return obj.(*v1alpha1.ManagedNode), err 49 | } 50 | 51 | // List takes label and field selectors, and returns the list of ManagedNodes that match those selectors. 52 | func (c *FakeManagedNodes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ManagedNodeList, err error) { 53 | obj, err := c.Fake. 54 | Invokes(testing.NewRootListAction(managednodesResource, managednodesKind, opts), &v1alpha1.ManagedNodeList{}) 55 | if obj == nil { 56 | return nil, err 57 | } 58 | 59 | label, _, _ := testing.ExtractFromListOptions(opts) 60 | if label == nil { 61 | label = labels.Everything() 62 | } 63 | list := &v1alpha1.ManagedNodeList{ListMeta: obj.(*v1alpha1.ManagedNodeList).ListMeta} 64 | for _, item := range obj.(*v1alpha1.ManagedNodeList).Items { 65 | if label.Matches(labels.Set(item.Labels)) { 66 | list.Items = append(list.Items, item) 67 | } 68 | } 69 | return list, err 70 | } 71 | 72 | // Watch returns a watch.Interface that watches the requested managedNodes. 73 | func (c *FakeManagedNodes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { 74 | return c.Fake. 75 | InvokesWatch(testing.NewRootWatchAction(managednodesResource, opts)) 76 | } 77 | 78 | // Create takes the representation of a managedNode and creates it. Returns the server's representation of the managedNode, and an error, if there is any. 79 | func (c *FakeManagedNodes) Create(ctx context.Context, managedNode *v1alpha1.ManagedNode, opts v1.CreateOptions) (result *v1alpha1.ManagedNode, err error) { 80 | obj, err := c.Fake. 81 | Invokes(testing.NewRootCreateAction(managednodesResource, managedNode), &v1alpha1.ManagedNode{}) 82 | if obj == nil { 83 | return nil, err 84 | } 85 | return obj.(*v1alpha1.ManagedNode), err 86 | } 87 | 88 | // Update takes the representation of a managedNode and updates it. Returns the server's representation of the managedNode, and an error, if there is any. 89 | func (c *FakeManagedNodes) Update(ctx context.Context, managedNode *v1alpha1.ManagedNode, opts v1.UpdateOptions) (result *v1alpha1.ManagedNode, err error) { 90 | obj, err := c.Fake. 91 | Invokes(testing.NewRootUpdateAction(managednodesResource, managedNode), &v1alpha1.ManagedNode{}) 92 | if obj == nil { 93 | return nil, err 94 | } 95 | return obj.(*v1alpha1.ManagedNode), err 96 | } 97 | 98 | // UpdateStatus was generated because the type contains a Status member. 99 | // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). 100 | func (c *FakeManagedNodes) UpdateStatus(ctx context.Context, managedNode *v1alpha1.ManagedNode, opts v1.UpdateOptions) (*v1alpha1.ManagedNode, error) { 101 | obj, err := c.Fake. 102 | Invokes(testing.NewRootUpdateSubresourceAction(managednodesResource, "status", managedNode), &v1alpha1.ManagedNode{}) 103 | if obj == nil { 104 | return nil, err 105 | } 106 | return obj.(*v1alpha1.ManagedNode), err 107 | } 108 | 109 | // Delete takes name of the managedNode and deletes it. Returns an error if one occurs. 110 | func (c *FakeManagedNodes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { 111 | _, err := c.Fake. 112 | Invokes(testing.NewRootDeleteActionWithOptions(managednodesResource, name, opts), &v1alpha1.ManagedNode{}) 113 | return err 114 | } 115 | 116 | // DeleteCollection deletes a collection of objects. 117 | func (c *FakeManagedNodes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { 118 | action := testing.NewRootDeleteCollectionAction(managednodesResource, listOpts) 119 | 120 | _, err := c.Fake.Invokes(action, &v1alpha1.ManagedNodeList{}) 121 | return err 122 | } 123 | 124 | // Patch applies the patch and returns the patched managedNode. 125 | func (c *FakeManagedNodes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ManagedNode, err error) { 126 | obj, err := c.Fake. 127 | Invokes(testing.NewRootPatchSubresourceAction(managednodesResource, name, pt, data, subresources...), &v1alpha1.ManagedNode{}) 128 | if obj == nil { 129 | return nil, err 130 | } 131 | return obj.(*v1alpha1.ManagedNode), err 132 | } 133 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/nodemanager/v1alpha1/fake/fake_nodemanager_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package fake 20 | 21 | import ( 22 | v1alpha1 "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/clientset/versioned/typed/nodemanager/v1alpha1" 23 | rest "k8s.io/client-go/rest" 24 | testing "k8s.io/client-go/testing" 25 | ) 26 | 27 | type FakeNodemanagerV1alpha1 struct { 28 | *testing.Fake 29 | } 30 | 31 | func (c *FakeNodemanagerV1alpha1) ManagedNodes() v1alpha1.ManagedNodeInterface { 32 | return &FakeManagedNodes{c} 33 | } 34 | 35 | // RESTClient returns a RESTClient that is used to communicate 36 | // with API server by this client implementation. 37 | func (c *FakeNodemanagerV1alpha1) RESTClient() rest.Interface { 38 | var ret *rest.RESTClient 39 | return ret 40 | } 41 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/nodemanager/v1alpha1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | type ManagedNodeExpansion interface{} 22 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/nodemanager/v1alpha1/managednode.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | "context" 23 | "time" 24 | 25 | v1alpha1 "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/apis/nodemanager/v1alpha1" 26 | scheme "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/clientset/versioned/scheme" 27 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 | types "k8s.io/apimachinery/pkg/types" 29 | watch "k8s.io/apimachinery/pkg/watch" 30 | rest "k8s.io/client-go/rest" 31 | ) 32 | 33 | // ManagedNodesGetter has a method to return a ManagedNodeInterface. 34 | // A group's client should implement this interface. 35 | type ManagedNodesGetter interface { 36 | ManagedNodes() ManagedNodeInterface 37 | } 38 | 39 | // ManagedNodeInterface has methods to work with ManagedNode resources. 40 | type ManagedNodeInterface interface { 41 | Create(ctx context.Context, managedNode *v1alpha1.ManagedNode, opts v1.CreateOptions) (*v1alpha1.ManagedNode, error) 42 | Update(ctx context.Context, managedNode *v1alpha1.ManagedNode, opts v1.UpdateOptions) (*v1alpha1.ManagedNode, error) 43 | UpdateStatus(ctx context.Context, managedNode *v1alpha1.ManagedNode, opts v1.UpdateOptions) (*v1alpha1.ManagedNode, error) 44 | Delete(ctx context.Context, name string, opts v1.DeleteOptions) error 45 | DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error 46 | Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ManagedNode, error) 47 | List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ManagedNodeList, error) 48 | Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) 49 | Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ManagedNode, err error) 50 | ManagedNodeExpansion 51 | } 52 | 53 | // managedNodes implements ManagedNodeInterface 54 | type managedNodes struct { 55 | client rest.Interface 56 | } 57 | 58 | // newManagedNodes returns a ManagedNodes 59 | func newManagedNodes(c *NodemanagerV1alpha1Client) *managedNodes { 60 | return &managedNodes{ 61 | client: c.RESTClient(), 62 | } 63 | } 64 | 65 | // Get takes name of the managedNode, and returns the corresponding managedNode object, and an error if there is any. 66 | func (c *managedNodes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ManagedNode, err error) { 67 | result = &v1alpha1.ManagedNode{} 68 | err = c.client.Get(). 69 | Resource("managednodes"). 70 | Name(name). 71 | VersionedParams(&options, scheme.ParameterCodec). 72 | Do(ctx). 73 | Into(result) 74 | return 75 | } 76 | 77 | // List takes label and field selectors, and returns the list of ManagedNodes that match those selectors. 78 | func (c *managedNodes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ManagedNodeList, err error) { 79 | var timeout time.Duration 80 | if opts.TimeoutSeconds != nil { 81 | timeout = time.Duration(*opts.TimeoutSeconds) * time.Second 82 | } 83 | result = &v1alpha1.ManagedNodeList{} 84 | err = c.client.Get(). 85 | Resource("managednodes"). 86 | VersionedParams(&opts, scheme.ParameterCodec). 87 | Timeout(timeout). 88 | Do(ctx). 89 | Into(result) 90 | return 91 | } 92 | 93 | // Watch returns a watch.Interface that watches the requested managedNodes. 94 | func (c *managedNodes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { 95 | var timeout time.Duration 96 | if opts.TimeoutSeconds != nil { 97 | timeout = time.Duration(*opts.TimeoutSeconds) * time.Second 98 | } 99 | opts.Watch = true 100 | return c.client.Get(). 101 | Resource("managednodes"). 102 | VersionedParams(&opts, scheme.ParameterCodec). 103 | Timeout(timeout). 104 | Watch(ctx) 105 | } 106 | 107 | // Create takes the representation of a managedNode and creates it. Returns the server's representation of the managedNode, and an error, if there is any. 108 | func (c *managedNodes) Create(ctx context.Context, managedNode *v1alpha1.ManagedNode, opts v1.CreateOptions) (result *v1alpha1.ManagedNode, err error) { 109 | result = &v1alpha1.ManagedNode{} 110 | err = c.client.Post(). 111 | Resource("managednodes"). 112 | VersionedParams(&opts, scheme.ParameterCodec). 113 | Body(managedNode). 114 | Do(ctx). 115 | Into(result) 116 | return 117 | } 118 | 119 | // Update takes the representation of a managedNode and updates it. Returns the server's representation of the managedNode, and an error, if there is any. 120 | func (c *managedNodes) Update(ctx context.Context, managedNode *v1alpha1.ManagedNode, opts v1.UpdateOptions) (result *v1alpha1.ManagedNode, err error) { 121 | result = &v1alpha1.ManagedNode{} 122 | err = c.client.Put(). 123 | Resource("managednodes"). 124 | Name(managedNode.Name). 125 | VersionedParams(&opts, scheme.ParameterCodec). 126 | Body(managedNode). 127 | Do(ctx). 128 | Into(result) 129 | return 130 | } 131 | 132 | // UpdateStatus was generated because the type contains a Status member. 133 | // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). 134 | func (c *managedNodes) UpdateStatus(ctx context.Context, managedNode *v1alpha1.ManagedNode, opts v1.UpdateOptions) (result *v1alpha1.ManagedNode, err error) { 135 | result = &v1alpha1.ManagedNode{} 136 | err = c.client.Put(). 137 | Resource("managednodes"). 138 | Name(managedNode.Name). 139 | SubResource("status"). 140 | VersionedParams(&opts, scheme.ParameterCodec). 141 | Body(managedNode). 142 | Do(ctx). 143 | Into(result) 144 | return 145 | } 146 | 147 | // Delete takes name of the managedNode and deletes it. Returns an error if one occurs. 148 | func (c *managedNodes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { 149 | return c.client.Delete(). 150 | Resource("managednodes"). 151 | Name(name). 152 | Body(&opts). 153 | Do(ctx). 154 | Error() 155 | } 156 | 157 | // DeleteCollection deletes a collection of objects. 158 | func (c *managedNodes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { 159 | var timeout time.Duration 160 | if listOpts.TimeoutSeconds != nil { 161 | timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second 162 | } 163 | return c.client.Delete(). 164 | Resource("managednodes"). 165 | VersionedParams(&listOpts, scheme.ParameterCodec). 166 | Timeout(timeout). 167 | Body(&opts). 168 | Do(ctx). 169 | Error() 170 | } 171 | 172 | // Patch applies the patch and returns the patched managedNode. 173 | func (c *managedNodes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ManagedNode, err error) { 174 | result = &v1alpha1.ManagedNode{} 175 | err = c.client.Patch(pt). 176 | Resource("managednodes"). 177 | Name(name). 178 | SubResource(subresources...). 179 | VersionedParams(&opts, scheme.ParameterCodec). 180 | Body(data). 181 | Do(ctx). 182 | Into(result) 183 | return 184 | } 185 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/nodemanager/v1alpha1/nodemanager_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | "net/http" 23 | 24 | v1alpha1 "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/apis/nodemanager/v1alpha1" 25 | "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/clientset/versioned/scheme" 26 | rest "k8s.io/client-go/rest" 27 | ) 28 | 29 | type NodemanagerV1alpha1Interface interface { 30 | RESTClient() rest.Interface 31 | ManagedNodesGetter 32 | } 33 | 34 | // NodemanagerV1alpha1Client is used to interact with features provided by the nodemanager.aldunelabs.com group. 35 | type NodemanagerV1alpha1Client struct { 36 | restClient rest.Interface 37 | } 38 | 39 | func (c *NodemanagerV1alpha1Client) ManagedNodes() ManagedNodeInterface { 40 | return newManagedNodes(c) 41 | } 42 | 43 | // NewForConfig creates a new NodemanagerV1alpha1Client for the given config. 44 | // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), 45 | // where httpClient was generated with rest.HTTPClientFor(c). 46 | func NewForConfig(c *rest.Config) (*NodemanagerV1alpha1Client, error) { 47 | config := *c 48 | if err := setConfigDefaults(&config); err != nil { 49 | return nil, err 50 | } 51 | httpClient, err := rest.HTTPClientFor(&config) 52 | if err != nil { 53 | return nil, err 54 | } 55 | return NewForConfigAndClient(&config, httpClient) 56 | } 57 | 58 | // NewForConfigAndClient creates a new NodemanagerV1alpha1Client for the given config and http client. 59 | // Note the http client provided takes precedence over the configured transport values. 60 | func NewForConfigAndClient(c *rest.Config, h *http.Client) (*NodemanagerV1alpha1Client, error) { 61 | config := *c 62 | if err := setConfigDefaults(&config); err != nil { 63 | return nil, err 64 | } 65 | client, err := rest.RESTClientForConfigAndClient(&config, h) 66 | if err != nil { 67 | return nil, err 68 | } 69 | return &NodemanagerV1alpha1Client{client}, nil 70 | } 71 | 72 | // NewForConfigOrDie creates a new NodemanagerV1alpha1Client for the given config and 73 | // panics if there is an error in the config. 74 | func NewForConfigOrDie(c *rest.Config) *NodemanagerV1alpha1Client { 75 | client, err := NewForConfig(c) 76 | if err != nil { 77 | panic(err) 78 | } 79 | return client 80 | } 81 | 82 | // New creates a new NodemanagerV1alpha1Client for the given RESTClient. 83 | func New(c rest.Interface) *NodemanagerV1alpha1Client { 84 | return &NodemanagerV1alpha1Client{c} 85 | } 86 | 87 | func setConfigDefaults(config *rest.Config) error { 88 | gv := v1alpha1.SchemeGroupVersion 89 | config.GroupVersion = &gv 90 | config.APIPath = "/apis" 91 | config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() 92 | 93 | if config.UserAgent == "" { 94 | config.UserAgent = rest.DefaultKubernetesUserAgent() 95 | } 96 | 97 | return nil 98 | } 99 | 100 | // RESTClient returns a RESTClient that is used to communicate 101 | // with API server by this client implementation. 102 | func (c *NodemanagerV1alpha1Client) RESTClient() rest.Interface { 103 | if c == nil { 104 | return nil 105 | } 106 | return c.restClient 107 | } 108 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package externalversions 20 | 21 | import ( 22 | reflect "reflect" 23 | sync "sync" 24 | time "time" 25 | 26 | versioned "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/clientset/versioned" 27 | internalinterfaces "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/informers/externalversions/internalinterfaces" 28 | nodemanager "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/informers/externalversions/nodemanager" 29 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 | runtime "k8s.io/apimachinery/pkg/runtime" 31 | schema "k8s.io/apimachinery/pkg/runtime/schema" 32 | cache "k8s.io/client-go/tools/cache" 33 | ) 34 | 35 | // SharedInformerOption defines the functional option type for SharedInformerFactory. 36 | type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory 37 | 38 | type sharedInformerFactory struct { 39 | client versioned.Interface 40 | namespace string 41 | tweakListOptions internalinterfaces.TweakListOptionsFunc 42 | lock sync.Mutex 43 | defaultResync time.Duration 44 | customResync map[reflect.Type]time.Duration 45 | transform cache.TransformFunc 46 | 47 | informers map[reflect.Type]cache.SharedIndexInformer 48 | // startedInformers is used for tracking which informers have been started. 49 | // This allows Start() to be called multiple times safely. 50 | startedInformers map[reflect.Type]bool 51 | // wg tracks how many goroutines were started. 52 | wg sync.WaitGroup 53 | // shuttingDown is true when Shutdown has been called. It may still be running 54 | // because it needs to wait for goroutines. 55 | shuttingDown bool 56 | } 57 | 58 | // WithCustomResyncConfig sets a custom resync period for the specified informer types. 59 | func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { 60 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 61 | for k, v := range resyncConfig { 62 | factory.customResync[reflect.TypeOf(k)] = v 63 | } 64 | return factory 65 | } 66 | } 67 | 68 | // WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. 69 | func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { 70 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 71 | factory.tweakListOptions = tweakListOptions 72 | return factory 73 | } 74 | } 75 | 76 | // WithNamespace limits the SharedInformerFactory to the specified namespace. 77 | func WithNamespace(namespace string) SharedInformerOption { 78 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 79 | factory.namespace = namespace 80 | return factory 81 | } 82 | } 83 | 84 | // WithTransform sets a transform on all informers. 85 | func WithTransform(transform cache.TransformFunc) SharedInformerOption { 86 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 87 | factory.transform = transform 88 | return factory 89 | } 90 | } 91 | 92 | // NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. 93 | func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { 94 | return NewSharedInformerFactoryWithOptions(client, defaultResync) 95 | } 96 | 97 | // NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. 98 | // Listers obtained via this SharedInformerFactory will be subject to the same filters 99 | // as specified here. 100 | // Deprecated: Please use NewSharedInformerFactoryWithOptions instead 101 | func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { 102 | return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) 103 | } 104 | 105 | // NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. 106 | func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { 107 | factory := &sharedInformerFactory{ 108 | client: client, 109 | namespace: v1.NamespaceAll, 110 | defaultResync: defaultResync, 111 | informers: make(map[reflect.Type]cache.SharedIndexInformer), 112 | startedInformers: make(map[reflect.Type]bool), 113 | customResync: make(map[reflect.Type]time.Duration), 114 | } 115 | 116 | // Apply all options 117 | for _, opt := range options { 118 | factory = opt(factory) 119 | } 120 | 121 | return factory 122 | } 123 | 124 | func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { 125 | f.lock.Lock() 126 | defer f.lock.Unlock() 127 | 128 | if f.shuttingDown { 129 | return 130 | } 131 | 132 | for informerType, informer := range f.informers { 133 | if !f.startedInformers[informerType] { 134 | f.wg.Add(1) 135 | // We need a new variable in each loop iteration, 136 | // otherwise the goroutine would use the loop variable 137 | // and that keeps changing. 138 | informer := informer 139 | go func() { 140 | defer f.wg.Done() 141 | informer.Run(stopCh) 142 | }() 143 | f.startedInformers[informerType] = true 144 | } 145 | } 146 | } 147 | 148 | func (f *sharedInformerFactory) Shutdown() { 149 | f.lock.Lock() 150 | f.shuttingDown = true 151 | f.lock.Unlock() 152 | 153 | // Will return immediately if there is nothing to wait for. 154 | f.wg.Wait() 155 | } 156 | 157 | func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { 158 | informers := func() map[reflect.Type]cache.SharedIndexInformer { 159 | f.lock.Lock() 160 | defer f.lock.Unlock() 161 | 162 | informers := map[reflect.Type]cache.SharedIndexInformer{} 163 | for informerType, informer := range f.informers { 164 | if f.startedInformers[informerType] { 165 | informers[informerType] = informer 166 | } 167 | } 168 | return informers 169 | }() 170 | 171 | res := map[reflect.Type]bool{} 172 | for informType, informer := range informers { 173 | res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) 174 | } 175 | return res 176 | } 177 | 178 | // InformerFor returns the SharedIndexInformer for obj using an internal 179 | // client. 180 | func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { 181 | f.lock.Lock() 182 | defer f.lock.Unlock() 183 | 184 | informerType := reflect.TypeOf(obj) 185 | informer, exists := f.informers[informerType] 186 | if exists { 187 | return informer 188 | } 189 | 190 | resyncPeriod, exists := f.customResync[informerType] 191 | if !exists { 192 | resyncPeriod = f.defaultResync 193 | } 194 | 195 | informer = newFunc(f.client, resyncPeriod) 196 | informer.SetTransform(f.transform) 197 | f.informers[informerType] = informer 198 | 199 | return informer 200 | } 201 | 202 | // SharedInformerFactory provides shared informers for resources in all known 203 | // API group versions. 204 | // 205 | // It is typically used like this: 206 | // 207 | // ctx, cancel := context.Background() 208 | // defer cancel() 209 | // factory := NewSharedInformerFactory(client, resyncPeriod) 210 | // defer factory.WaitForStop() // Returns immediately if nothing was started. 211 | // genericInformer := factory.ForResource(resource) 212 | // typedInformer := factory.SomeAPIGroup().V1().SomeType() 213 | // factory.Start(ctx.Done()) // Start processing these informers. 214 | // synced := factory.WaitForCacheSync(ctx.Done()) 215 | // for v, ok := range synced { 216 | // if !ok { 217 | // fmt.Fprintf(os.Stderr, "caches failed to sync: %v", v) 218 | // return 219 | // } 220 | // } 221 | // 222 | // // Creating informers can also be created after Start, but then 223 | // // Start must be called again: 224 | // anotherGenericInformer := factory.ForResource(resource) 225 | // factory.Start(ctx.Done()) 226 | type SharedInformerFactory interface { 227 | internalinterfaces.SharedInformerFactory 228 | 229 | // Start initializes all requested informers. They are handled in goroutines 230 | // which run until the stop channel gets closed. 231 | Start(stopCh <-chan struct{}) 232 | 233 | // Shutdown marks a factory as shutting down. At that point no new 234 | // informers can be started anymore and Start will return without 235 | // doing anything. 236 | // 237 | // In addition, Shutdown blocks until all goroutines have terminated. For that 238 | // to happen, the close channel(s) that they were started with must be closed, 239 | // either before Shutdown gets called or while it is waiting. 240 | // 241 | // Shutdown may be called multiple times, even concurrently. All such calls will 242 | // block until all goroutines have terminated. 243 | Shutdown() 244 | 245 | // WaitForCacheSync blocks until all started informers' caches were synced 246 | // or the stop channel gets closed. 247 | WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool 248 | 249 | // ForResource gives generic access to a shared informer of the matching type. 250 | ForResource(resource schema.GroupVersionResource) (GenericInformer, error) 251 | 252 | // InformerFor returns the SharedIndexInformer for obj using an internal 253 | // client. 254 | InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer 255 | 256 | Nodemanager() nodemanager.Interface 257 | } 258 | 259 | func (f *sharedInformerFactory) Nodemanager() nodemanager.Interface { 260 | return nodemanager.New(f, f.namespace, f.tweakListOptions) 261 | } 262 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/generic.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package externalversions 20 | 21 | import ( 22 | "fmt" 23 | 24 | v1alpha1 "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/apis/nodemanager/v1alpha1" 25 | schema "k8s.io/apimachinery/pkg/runtime/schema" 26 | cache "k8s.io/client-go/tools/cache" 27 | ) 28 | 29 | // GenericInformer is type of SharedIndexInformer which will locate and delegate to other 30 | // sharedInformers based on type 31 | type GenericInformer interface { 32 | Informer() cache.SharedIndexInformer 33 | Lister() cache.GenericLister 34 | } 35 | 36 | type genericInformer struct { 37 | informer cache.SharedIndexInformer 38 | resource schema.GroupResource 39 | } 40 | 41 | // Informer returns the SharedIndexInformer. 42 | func (f *genericInformer) Informer() cache.SharedIndexInformer { 43 | return f.informer 44 | } 45 | 46 | // Lister returns the GenericLister. 47 | func (f *genericInformer) Lister() cache.GenericLister { 48 | return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) 49 | } 50 | 51 | // ForResource gives generic access to a shared informer of the matching type 52 | // TODO extend this to unknown resources with a client pool 53 | func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { 54 | switch resource { 55 | // Group=nodemanager.aldunelabs.com, Version=v1alpha1 56 | case v1alpha1.SchemeGroupVersion.WithResource("managednodes"): 57 | return &genericInformer{resource: resource.GroupResource(), informer: f.Nodemanager().V1alpha1().ManagedNodes().Informer()}, nil 58 | 59 | } 60 | 61 | return nil, fmt.Errorf("no informer found for %v", resource) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package internalinterfaces 20 | 21 | import ( 22 | time "time" 23 | 24 | versioned "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/clientset/versioned" 25 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | runtime "k8s.io/apimachinery/pkg/runtime" 27 | cache "k8s.io/client-go/tools/cache" 28 | ) 29 | 30 | // NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. 31 | type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer 32 | 33 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle 34 | type SharedInformerFactory interface { 35 | Start(stopCh <-chan struct{}) 36 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer 37 | } 38 | 39 | // TweakListOptionsFunc is a function that transforms a v1.ListOptions. 40 | type TweakListOptionsFunc func(*v1.ListOptions) 41 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/nodemanager/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package nodemanager 20 | 21 | import ( 22 | internalinterfaces "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/informers/externalversions/internalinterfaces" 23 | v1alpha1 "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/informers/externalversions/nodemanager/v1alpha1" 24 | ) 25 | 26 | // Interface provides access to each of this group's versions. 27 | type Interface interface { 28 | // V1alpha1 provides access to shared informers for resources in V1alpha1. 29 | V1alpha1() v1alpha1.Interface 30 | } 31 | 32 | type group struct { 33 | factory internalinterfaces.SharedInformerFactory 34 | namespace string 35 | tweakListOptions internalinterfaces.TweakListOptionsFunc 36 | } 37 | 38 | // New returns a new Interface. 39 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 40 | return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 41 | } 42 | 43 | // V1alpha1 returns a new v1alpha1.Interface. 44 | func (g *group) V1alpha1() v1alpha1.Interface { 45 | return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/nodemanager/v1alpha1/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | internalinterfaces "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/informers/externalversions/internalinterfaces" 23 | ) 24 | 25 | // Interface provides access to all the informers in this group version. 26 | type Interface interface { 27 | // ManagedNodes returns a ManagedNodeInformer. 28 | ManagedNodes() ManagedNodeInformer 29 | } 30 | 31 | type version struct { 32 | factory internalinterfaces.SharedInformerFactory 33 | namespace string 34 | tweakListOptions internalinterfaces.TweakListOptionsFunc 35 | } 36 | 37 | // New returns a new Interface. 38 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 39 | return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 40 | } 41 | 42 | // ManagedNodes returns a ManagedNodeInformer. 43 | func (v *version) ManagedNodes() ManagedNodeInformer { 44 | return &managedNodeInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} 45 | } 46 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/nodemanager/v1alpha1/managednode.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by informer-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | "context" 23 | time "time" 24 | 25 | nodemanagerv1alpha1 "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/apis/nodemanager/v1alpha1" 26 | versioned "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/clientset/versioned" 27 | internalinterfaces "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/informers/externalversions/internalinterfaces" 28 | v1alpha1 "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/listers/nodemanager/v1alpha1" 29 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 | runtime "k8s.io/apimachinery/pkg/runtime" 31 | watch "k8s.io/apimachinery/pkg/watch" 32 | cache "k8s.io/client-go/tools/cache" 33 | ) 34 | 35 | // ManagedNodeInformer provides access to a shared informer and lister for 36 | // ManagedNodes. 37 | type ManagedNodeInformer interface { 38 | Informer() cache.SharedIndexInformer 39 | Lister() v1alpha1.ManagedNodeLister 40 | } 41 | 42 | type managedNodeInformer struct { 43 | factory internalinterfaces.SharedInformerFactory 44 | tweakListOptions internalinterfaces.TweakListOptionsFunc 45 | } 46 | 47 | // NewManagedNodeInformer constructs a new informer for ManagedNode type. 48 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 49 | // one. This reduces memory footprint and number of connections to the server. 50 | func NewManagedNodeInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { 51 | return NewFilteredManagedNodeInformer(client, resyncPeriod, indexers, nil) 52 | } 53 | 54 | // NewFilteredManagedNodeInformer constructs a new informer for ManagedNode type. 55 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 56 | // one. This reduces memory footprint and number of connections to the server. 57 | func NewFilteredManagedNodeInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { 58 | return cache.NewSharedIndexInformer( 59 | &cache.ListWatch{ 60 | ListFunc: func(options v1.ListOptions) (runtime.Object, error) { 61 | if tweakListOptions != nil { 62 | tweakListOptions(&options) 63 | } 64 | return client.NodemanagerV1alpha1().ManagedNodes().List(context.TODO(), options) 65 | }, 66 | WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { 67 | if tweakListOptions != nil { 68 | tweakListOptions(&options) 69 | } 70 | return client.NodemanagerV1alpha1().ManagedNodes().Watch(context.TODO(), options) 71 | }, 72 | }, 73 | &nodemanagerv1alpha1.ManagedNode{}, 74 | resyncPeriod, 75 | indexers, 76 | ) 77 | } 78 | 79 | func (f *managedNodeInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { 80 | return NewFilteredManagedNodeInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) 81 | } 82 | 83 | func (f *managedNodeInformer) Informer() cache.SharedIndexInformer { 84 | return f.factory.InformerFor(&nodemanagerv1alpha1.ManagedNode{}, f.defaultInformer) 85 | } 86 | 87 | func (f *managedNodeInformer) Lister() v1alpha1.ManagedNodeLister { 88 | return v1alpha1.NewManagedNodeLister(f.Informer().GetIndexer()) 89 | } 90 | -------------------------------------------------------------------------------- /pkg/generated/listers/nodemanager/v1alpha1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by lister-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | // ManagedNodeListerExpansion allows custom methods to be added to 22 | // ManagedNodeLister. 23 | type ManagedNodeListerExpansion interface{} 24 | -------------------------------------------------------------------------------- /pkg/generated/listers/nodemanager/v1alpha1/managednode.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Frédéric Boltz. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by lister-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | v1alpha1 "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/apis/nodemanager/v1alpha1" 23 | "k8s.io/apimachinery/pkg/api/errors" 24 | "k8s.io/apimachinery/pkg/labels" 25 | "k8s.io/client-go/tools/cache" 26 | ) 27 | 28 | // ManagedNodeLister helps list ManagedNodes. 29 | // All objects returned here must be treated as read-only. 30 | type ManagedNodeLister interface { 31 | // List lists all ManagedNodes in the indexer. 32 | // Objects returned here must be treated as read-only. 33 | List(selector labels.Selector) (ret []*v1alpha1.ManagedNode, err error) 34 | // Get retrieves the ManagedNode from the index for a given name. 35 | // Objects returned here must be treated as read-only. 36 | Get(name string) (*v1alpha1.ManagedNode, error) 37 | ManagedNodeListerExpansion 38 | } 39 | 40 | // managedNodeLister implements the ManagedNodeLister interface. 41 | type managedNodeLister struct { 42 | indexer cache.Indexer 43 | } 44 | 45 | // NewManagedNodeLister returns a new ManagedNodeLister. 46 | func NewManagedNodeLister(indexer cache.Indexer) ManagedNodeLister { 47 | return &managedNodeLister{indexer: indexer} 48 | } 49 | 50 | // List lists all ManagedNodes in the indexer. 51 | func (s *managedNodeLister) List(selector labels.Selector) (ret []*v1alpha1.ManagedNode, err error) { 52 | err = cache.ListAll(s.indexer, selector, func(m interface{}) { 53 | ret = append(ret, m.(*v1alpha1.ManagedNode)) 54 | }) 55 | return ret, err 56 | } 57 | 58 | // Get retrieves the ManagedNode from the index for a given name. 59 | func (s *managedNodeLister) Get(name string) (*v1alpha1.ManagedNode, error) { 60 | obj, exists, err := s.indexer.GetByKey(name) 61 | if err != nil { 62 | return nil, err 63 | } 64 | if !exists { 65 | return nil, errors.NewNotFound(v1alpha1.Resource("managednode"), name) 66 | } 67 | return obj.(*v1alpha1.ManagedNode), nil 68 | } 69 | -------------------------------------------------------------------------------- /pkg/signals/signal.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package signals 18 | 19 | import ( 20 | "os" 21 | "os/signal" 22 | ) 23 | 24 | var onlyOneSignalHandler = make(chan struct{}) 25 | 26 | // SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned 27 | // which is closed on one of these signals. If a second signal is caught, the program 28 | // is terminated with exit code 1. 29 | func SetupSignalHandler() (stopCh <-chan struct{}) { 30 | close(onlyOneSignalHandler) // panics when called twice 31 | 32 | stop := make(chan struct{}) 33 | c := make(chan os.Signal, 2) 34 | signal.Notify(c, shutdownSignals...) 35 | go func() { 36 | <-c 37 | close(stop) 38 | <-c 39 | os.Exit(1) // second signal. Exit directly. 40 | }() 41 | 42 | return stop 43 | } 44 | -------------------------------------------------------------------------------- /pkg/signals/signal_posix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | /* 5 | Copyright 2017 The Kubernetes Authors. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | package signals 21 | 22 | import ( 23 | "os" 24 | "syscall" 25 | ) 26 | 27 | var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM} 28 | -------------------------------------------------------------------------------- /pkg/signals/signal_windows.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package signals 18 | 19 | import ( 20 | "os" 21 | ) 22 | 23 | var shutdownSignals = []os.Signal{os.Interrupt} 24 | -------------------------------------------------------------------------------- /push_image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2017 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | IMAGE_TO_PUSH=$1 18 | if [ -z $IMAGE_TO_PUSH ]; then 19 | echo No image passed 20 | exit 1 21 | fi 22 | 23 | docker_push_cmd=("docker") 24 | if [[ "${IMAGE_TO_PUSH}" == "gcr.io/"* ]] || [[ "${IMAGE_TO_PUSH}" == "staging-k8s.gcr.io/"* ]] ; then 25 | docker_push_cmd=("gcloud" "docker" "--") 26 | fi 27 | 28 | echo "About to push image $IMAGE_TO_PUSH" 29 | read -r -p "Are you sure? [y/N] " response 30 | if [[ "$response" =~ ^([yY])+$ ]]; then 31 | "${docker_push_cmd[@]}" pull $IMAGE_TO_PUSH 32 | if [ $? -eq 0 ]; then 33 | echo $IMAGE_TO_PUSH already exists 34 | exit 1 35 | fi 36 | "${docker_push_cmd[@]}" push $IMAGE_TO_PUSH 37 | else 38 | echo Aborted 39 | exit 1 40 | fi 41 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright 2017 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # A wrapper script trapping SIGTERM (docker stop) and passing the signal to 18 | # cluster-autoscaler binary. 19 | 20 | if [ -z "$LOG_OUTPUT" ]; then 21 | LOG_OUTPUT="/var/log/vsphere-autoscaler.log" 22 | fi 23 | 24 | ./vsphere-autoscaler $@ 1>>$LOG_OUTPUT 2>&1 & 25 | pid="$!" 26 | trap "kill -15 $pid" 15 27 | 28 | # We need a loop here, because receiving signal breaks out of wait. 29 | # kill -0 doesn't send any signal, but it still checks if the process is running. 30 | while kill -0 $pid > /dev/null 2>&1; do 31 | wait $pid 32 | done 33 | exit "$?" 34 | -------------------------------------------------------------------------------- /scripts/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | SRCDIR=$(dirname $0) 3 | CONFIG=$1 4 | CONFIG_DIR=${SRCDIR}/../.config/$1 5 | 6 | rm -rf /tmp/vmware.sock 7 | sudo mkdir -p /var/run/cluster-autoscaler/ 8 | sudo chown $USER /var/run/cluster-autoscaler/ 9 | rm -f /var/run/cluster-autoscaler/vmware.sock 10 | 11 | AUTOSCALER_VMWARE=${HOME}/Projects/GitHub/autoscaled-masterkube-vmware 12 | 13 | if [ -n "${CONFIG}" ]; then 14 | mkdir -p "${CONFIG_DIR}" 15 | 16 | cp ${AUTOSCALER_VMWARE}/cluster/${CONFIG}/config ${CONFIG_DIR}/config 17 | 18 | cat ${AUTOSCALER_VMWARE}/config/${CONFIG}/config/kubernetes-vmware-autoscaler.json | jq \ 19 | --arg ETCD_SSL_DIR "${AUTOSCALER_VMWARE}/cluster/${CONFIG}/etcd" \ 20 | --arg PKI_DIR "${AUTOSCALER_VMWARE}/cluster/${CONFIG}/kubernetes/pki" \ 21 | --arg SSH_KEY "${HOME}/.ssh/id_rsa" \ 22 | '. | .listen = "/var/run/cluster-autoscaler/vmware.sock" | .network = "unix" | ."src-etcd-ssl-dir" = $ETCD_SSL_DIR | ."kubernetes-pki-srcdir" = $PKI_DIR | ."ssh-infos"."ssh-private-key" = $SSH_KEY' > ${CONFIG_DIR}/kubernetes-vmware-autoscaler.json 23 | fi 24 | -------------------------------------------------------------------------------- /scripts/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | VERBOSE= 5 | 6 | go clean -testcache 7 | go mod vendor 8 | 9 | export Test_AuthMethodKey=NO 10 | export Test_Sudo=NO 11 | export Test_CIDR=YES 12 | export Test_getVM=YES 13 | export Test_listVM=YES 14 | export Test_createVM=YES 15 | export Test_statusVM=YES 16 | export Test_powerOnVM=YES 17 | export Test_powerOffVM=YES 18 | export Test_shutdownGuest=YES 19 | export Test_deleteVM=YES 20 | 21 | function cleanup { 22 | echo "Kill vcsim" 23 | kill $GOVC_SIM_PID 24 | } 25 | 26 | trap cleanup EXIT 27 | 28 | echo "Launch vcsim" 29 | vcsim -pg 2 & 30 | GOVC_SIM_PID=$! 31 | 32 | echo "Run vsphere test" 33 | go test --test.short $VERBOSE -race ./vsphere 34 | 35 | kill $GOVC_SIM_PID &> /dev/null 36 | 37 | vcsim -pg 2 & 38 | GOVC_SIM_PID=$! 39 | 40 | echo "Run server test" 41 | 42 | export TestServer=YES 43 | export TestServer_NodeGroups=YES 44 | export TestServer_NodeGroupForNode=YES 45 | export TestServer_HasInstance=YES 46 | export TestServer_Pricing=YES 47 | export TestServer_GetAvailableMachineTypes=YES 48 | export TestServer_NewNodeGroup=YES 49 | export TestServer_GetResourceLimiter=YES 50 | export TestServer_Cleanup=YES 51 | export TestServer_Refresh=YES 52 | export TestServer_TargetSize=YES 53 | export TestServer_IncreaseSize=YES 54 | export TestServer_DecreaseTargetSize=YES 55 | export TestServer_DeleteNodes=YES 56 | export TestServer_Id=YES 57 | export TestServer_Debug=YES 58 | export TestServer_Nodes=YES 59 | export TestServer_TemplateNodeInfo=YES 60 | export TestServer_Exist=YES 61 | export TestServer_Create=YES 62 | export TestServer_Delete=YES 63 | export TestServer_Autoprovisioned=YES 64 | export TestServer_Belongs=YES 65 | export TestServer_NodePrice=YES 66 | export TestServer_PodPrice=YES 67 | 68 | go test --test.short $VERBOSE -race ./server -run Test_Server 69 | 70 | kill $GOVC_SIM_PID &> /dev/null 71 | 72 | vcsim -pg 2 & 73 | GOVC_SIM_PID=$! 74 | 75 | echo "Run nodegroup test" 76 | 77 | export TestNodegroup=YES 78 | export TestNodeGroup_launchVM=YES 79 | export TestNodeGroup_stopVM=YES 80 | export TestNodeGroup_startVM=YES 81 | export TestNodeGroup_statusVM=YES 82 | export TestNodeGroup_deleteVM=YES 83 | export TestNodeGroupGroup_addNode=YES 84 | export TestNodeGroupGroup_deleteNode=YES 85 | export TestNodeGroupGroup_deleteNodeGroup=YES 86 | 87 | go test --test.short $VERBOSE -race ./server -run Test_Nodegroup 88 | 89 | kill $GOVC_SIM_PID &> /dev/null 90 | -------------------------------------------------------------------------------- /server/all_test.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/Fred78290/kubernetes-vmware-autoscaler/utils" 7 | ) 8 | 9 | func Test_Nodegroup(t *testing.T) { 10 | if utils.ShouldTestFeature("TestNodegroup") { 11 | test := createTestNodegroup(t) 12 | 13 | if utils.ShouldTestFeature("TestNodeGroup_launchVM") { 14 | t.Run("TestNodeGroup_launchVM", func(t *testing.T) { 15 | test.launchVM() 16 | }) 17 | } 18 | 19 | if utils.ShouldTestFeature("TestNodeGroup_stopVM") { 20 | t.Run("TestNodeGroup_stopVM", func(t *testing.T) { 21 | test.stopVM() 22 | }) 23 | } 24 | 25 | if utils.ShouldTestFeature("TestNodeGroup_startVM") { 26 | t.Run("TestNodeGroup_startVM", func(t *testing.T) { 27 | test.startVM() 28 | }) 29 | } 30 | 31 | if utils.ShouldTestFeature("TestNodeGroup_statusVM") { 32 | t.Run("TestNodeGroup_statusVM", func(t *testing.T) { 33 | test.statusVM() 34 | }) 35 | } 36 | 37 | if utils.ShouldTestFeature("TestNodeGroup_deleteVM") { 38 | t.Run("TestNodeGroup_deleteVM", func(t *testing.T) { 39 | test.deleteVM() 40 | }) 41 | } 42 | 43 | if utils.ShouldTestFeature("TestNodeGroupGroup_addNode") { 44 | t.Run("TestNodeGroupGroup_addNode", func(t *testing.T) { 45 | test.addNode() 46 | }) 47 | } 48 | 49 | if utils.ShouldTestFeature("TestNodeGroupGroup_deleteNode") { 50 | t.Run("TestNodeGroupGroup_deleteNode", func(t *testing.T) { 51 | test.deleteNode() 52 | }) 53 | } 54 | 55 | if utils.ShouldTestFeature("TestNodeGroupGroup_deleteNodeGroup") { 56 | t.Run("TestNodeGroupGroup_deleteNodeGroup", func(t *testing.T) { 57 | test.deleteNodeGroup() 58 | }) 59 | } 60 | } 61 | } 62 | 63 | func Test_Server(t *testing.T) { 64 | if utils.ShouldTestFeature("TestServer") { 65 | test := createServerTest(t) 66 | 67 | if utils.ShouldTestFeature("TestServer_NodeGroups") { 68 | t.Run("TestServer_NodeGroups", func(t *testing.T) { 69 | test.NodeGroups() 70 | }) 71 | } 72 | 73 | if utils.ShouldTestFeature("TestServer_NodeGroupForNode") { 74 | t.Run("TestServer_NodeGroupForNode", func(t *testing.T) { 75 | test.NodeGroupForNode() 76 | }) 77 | } 78 | 79 | if utils.ShouldTestFeature("TestServer_HasInstance") { 80 | t.Run("TestServer_HasInstance", func(t *testing.T) { 81 | test.HasInstance() 82 | }) 83 | } 84 | 85 | if utils.ShouldTestFeature("TestServer_Pricing") { 86 | t.Run("TestServer_Pricing", func(t *testing.T) { 87 | test.Pricing() 88 | }) 89 | } 90 | 91 | if utils.ShouldTestFeature("TestServer_GetAvailableMachineTypes") { 92 | t.Run("TestServer_GetAvailableMachineTypes", func(t *testing.T) { 93 | test.GetAvailableMachineTypes() 94 | }) 95 | } 96 | 97 | if utils.ShouldTestFeature("TestServer_NewNodeGroup") { 98 | t.Run("TestServer_NewNodeGroup", func(t *testing.T) { 99 | test.NewNodeGroup() 100 | }) 101 | } 102 | 103 | if utils.ShouldTestFeature("TestServer_GetResourceLimiter") { 104 | t.Run("TestServer_GetResourceLimiter", func(t *testing.T) { 105 | test.GetResourceLimiter() 106 | }) 107 | } 108 | 109 | if utils.ShouldTestFeature("TestServer_Refresh") { 110 | t.Run("TestServer_Refresh", func(t *testing.T) { 111 | test.Refresh() 112 | }) 113 | } 114 | 115 | if utils.ShouldTestFeature("TestServer_TargetSize") { 116 | t.Run("TestServer_TargetSize", func(t *testing.T) { 117 | test.TargetSize() 118 | }) 119 | } 120 | 121 | if utils.ShouldTestFeature("TestServer_IncreaseSize") { 122 | t.Run("TestServer_IncreaseSize", func(t *testing.T) { 123 | test.IncreaseSize() 124 | }) 125 | } 126 | 127 | if utils.ShouldTestFeature("TestServer_DecreaseTargetSize") { 128 | t.Run("TestServer_DecreaseTargetSize", func(t *testing.T) { 129 | test.DecreaseTargetSize() 130 | }) 131 | } 132 | 133 | if utils.ShouldTestFeature("TestServer_DeleteNodes") { 134 | t.Run("TestServer_DeleteNodes", func(t *testing.T) { 135 | test.DeleteNodes() 136 | }) 137 | } 138 | 139 | if utils.ShouldTestFeature("TestServer_Id") { 140 | t.Run("TestServer_Id", func(t *testing.T) { 141 | test.Id() 142 | }) 143 | } 144 | 145 | if utils.ShouldTestFeature("TestServer_Debug") { 146 | t.Run("TestServer_Debug", func(t *testing.T) { 147 | test.Debug() 148 | }) 149 | } 150 | 151 | if utils.ShouldTestFeature("TestServer_Nodes") { 152 | t.Run("TestServer_Nodes", func(t *testing.T) { 153 | test.Nodes() 154 | }) 155 | } 156 | 157 | if utils.ShouldTestFeature("TestServer_TemplateNodeInfo") { 158 | t.Run("TestServer_TemplateNodeInfo", func(t *testing.T) { 159 | test.TemplateNodeInfo() 160 | }) 161 | } 162 | 163 | if utils.ShouldTestFeature("TestServer_Exist") { 164 | t.Run("TestServer_Exist", func(t *testing.T) { 165 | test.Exist() 166 | }) 167 | } 168 | 169 | if utils.ShouldTestFeature("TestServer_Delete") { 170 | t.Run("TestServer_Delete", func(t *testing.T) { 171 | test.Delete() 172 | }) 173 | } 174 | 175 | if utils.ShouldTestFeature("TestServer_Create") { 176 | t.Run("TestServer_Create", func(t *testing.T) { 177 | test.Create() 178 | }) 179 | } 180 | 181 | if utils.ShouldTestFeature("TestServer_Autoprovisioned") { 182 | t.Run("TestServer_Autoprovisioned", func(t *testing.T) { 183 | test.Autoprovisioned() 184 | }) 185 | } 186 | 187 | if utils.ShouldTestFeature("TestServer_Belongs") { 188 | t.Run("TestServer_Belongs", func(t *testing.T) { 189 | test.Belongs() 190 | }) 191 | } 192 | 193 | if utils.ShouldTestFeature("TestServer_NodePrice") { 194 | t.Run("TestServer_NodePrice", func(t *testing.T) { 195 | test.NodePrice() 196 | }) 197 | } 198 | 199 | if utils.ShouldTestFeature("TestServer_PodPrice") { 200 | t.Run("TestServer_PodPrice", func(t *testing.T) { 201 | test.PodPrice() 202 | }) 203 | } 204 | 205 | if utils.ShouldTestFeature("TestServer_Cleanup") { 206 | t.Run("TestServer_Cleanup", func(t *testing.T) { 207 | test.Cleanup() 208 | }) 209 | } 210 | 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /server/nodegroup_test.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "testing" 7 | 8 | "github.com/Fred78290/kubernetes-vmware-autoscaler/constantes" 9 | managednodeClientset "github.com/Fred78290/kubernetes-vmware-autoscaler/pkg/generated/clientset/versioned" 10 | "github.com/Fred78290/kubernetes-vmware-autoscaler/types" 11 | "github.com/Fred78290/kubernetes-vmware-autoscaler/utils" 12 | "github.com/Fred78290/kubernetes-vmware-autoscaler/vsphere" 13 | "github.com/stretchr/testify/assert" 14 | apiv1 "k8s.io/api/core/v1" 15 | apiextension "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 16 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 17 | "k8s.io/client-go/kubernetes" 18 | ) 19 | 20 | type baseTest struct { 21 | testConfig *vsphere.Configuration 22 | t *testing.T 23 | } 24 | 25 | type nodegroupTest struct { 26 | baseTest 27 | } 28 | 29 | type autoScalerServerNodeGroupTest struct { 30 | AutoScalerServerNodeGroup 31 | baseTest 32 | } 33 | 34 | func (ng *autoScalerServerNodeGroupTest) createTestNode(nodeName string, desiredState ...AutoScalerServerNodeState) *AutoScalerServerNode { 35 | var state AutoScalerServerNodeState = AutoScalerServerNodeStateNotCreated 36 | 37 | if len(desiredState) > 0 { 38 | state = desiredState[0] 39 | } 40 | 41 | node := &AutoScalerServerNode{ 42 | NodeGroupID: testGroupID, 43 | NodeName: nodeName, 44 | VMUUID: testVMUUID, 45 | CRDUID: testCRDUID, 46 | Memory: ng.Machine.Memory, 47 | CPU: ng.Machine.Vcpu, 48 | Disk: ng.Machine.Disk, 49 | IPAddress: "127.0.0.1", 50 | State: state, 51 | NodeType: AutoScalerServerNodeAutoscaled, 52 | NodeIndex: 1, 53 | VSphereConfig: ng.testConfig, 54 | serverConfig: ng.configuration, 55 | } 56 | 57 | if vmuuid := node.findInstanceUUID(); len(vmuuid) > 0 { 58 | node.VMUUID = vmuuid 59 | } 60 | 61 | ng.Nodes[nodeName] = node 62 | ng.RunningNodes[len(ng.RunningNodes)+1] = ServerNodeStateRunning 63 | 64 | return node 65 | } 66 | 67 | func (m *nodegroupTest) launchVM() { 68 | ng, testNode, err := m.newTestNode(launchVMName) 69 | 70 | if assert.NoError(m.t, err) { 71 | if err := testNode.launchVM(m, ng.NodeLabels, ng.SystemLabels); err != nil { 72 | m.t.Errorf("AutoScalerNode.launchVM() error = %v", err) 73 | } 74 | } 75 | } 76 | 77 | func (m *nodegroupTest) startVM() { 78 | _, testNode, err := m.newTestNode(launchVMName) 79 | 80 | if assert.NoError(m.t, err) { 81 | if err := testNode.startVM(m); err != nil { 82 | m.t.Errorf("AutoScalerNode.startVM() error = %v", err) 83 | } 84 | } 85 | } 86 | 87 | func (m *nodegroupTest) stopVM() { 88 | _, testNode, err := m.newTestNode(launchVMName) 89 | 90 | if assert.NoError(m.t, err) { 91 | if err := testNode.stopVM(m); err != nil { 92 | m.t.Errorf("AutoScalerNode.stopVM() error = %v", err) 93 | } 94 | } 95 | } 96 | 97 | func (m *nodegroupTest) deleteVM() { 98 | _, testNode, err := m.newTestNode(launchVMName) 99 | 100 | if assert.NoError(m.t, err) { 101 | if err := testNode.deleteVM(m); err != nil { 102 | m.t.Errorf("AutoScalerNode.deleteVM() error = %v", err) 103 | } 104 | } 105 | } 106 | 107 | func (m *nodegroupTest) statusVM() { 108 | _, testNode, err := m.newTestNode(launchVMName) 109 | 110 | if assert.NoError(m.t, err) { 111 | if got, err := testNode.statusVM(); err != nil { 112 | m.t.Errorf("AutoScalerNode.statusVM() error = %v", err) 113 | } else if got != AutoScalerServerNodeStateRunning { 114 | m.t.Errorf("AutoScalerNode.statusVM() = %v, want %v", got, AutoScalerServerNodeStateRunning) 115 | } 116 | } 117 | } 118 | 119 | func (m *nodegroupTest) addNode() { 120 | ng, err := m.newTestNodeGroup() 121 | 122 | if assert.NoError(m.t, err) { 123 | if _, err := ng.addNodes(m, 1); err != nil { 124 | m.t.Errorf("AutoScalerServerNodeGroup.addNode() error = %v", err) 125 | } 126 | } 127 | } 128 | 129 | func (m *nodegroupTest) deleteNode() { 130 | ng, testNode, err := m.newTestNode(launchVMName) 131 | 132 | if assert.NoError(m.t, err) { 133 | if err := ng.deleteNodeByName(m, testNode.NodeName); err != nil { 134 | m.t.Errorf("AutoScalerServerNodeGroup.deleteNode() error = %v", err) 135 | } 136 | } 137 | } 138 | 139 | func (m *nodegroupTest) deleteNodeGroup() { 140 | ng, err := m.newTestNodeGroup() 141 | 142 | if assert.NoError(m.t, err) { 143 | if err := ng.deleteNodeGroup(m); err != nil { 144 | m.t.Errorf("AutoScalerServerNodeGroup.deleteNodeGroup() error = %v", err) 145 | } 146 | } 147 | } 148 | 149 | func (m *baseTest) KubeClient() (kubernetes.Interface, error) { 150 | return nil, nil 151 | } 152 | 153 | func (m *baseTest) NodeManagerClient() (managednodeClientset.Interface, error) { 154 | return nil, nil 155 | } 156 | 157 | func (m *baseTest) ApiExtentionClient() (apiextension.Interface, error) { 158 | return nil, nil 159 | } 160 | 161 | func (m *baseTest) PodList(nodeName string, podFilter types.PodFilterFunc) ([]apiv1.Pod, error) { 162 | return nil, nil 163 | } 164 | 165 | func (m *baseTest) NodeList() (*apiv1.NodeList, error) { 166 | node := apiv1.Node{ 167 | ObjectMeta: metav1.ObjectMeta{ 168 | Name: testNodeName, 169 | UID: testCRDUID, 170 | Annotations: map[string]string{ 171 | constantes.AnnotationNodeGroupName: testGroupID, 172 | constantes.AnnotationNodeIndex: "0", 173 | constantes.AnnotationInstanceID: testVMUUID, 174 | constantes.AnnotationNodeAutoProvisionned: "true", 175 | constantes.AnnotationScaleDownDisabled: "false", 176 | constantes.AnnotationNodeManaged: "false", 177 | }, 178 | }, 179 | } 180 | 181 | return &apiv1.NodeList{ 182 | Items: []apiv1.Node{ 183 | node, 184 | }, 185 | }, nil 186 | } 187 | 188 | func (m *baseTest) UncordonNode(nodeName string) error { 189 | return nil 190 | } 191 | 192 | func (m *baseTest) CordonNode(nodeName string) error { 193 | return nil 194 | } 195 | 196 | func (m *baseTest) SetProviderID(nodeName, providerID string) error { 197 | return nil 198 | } 199 | 200 | func (m *baseTest) MarkDrainNode(nodeName string) error { 201 | return nil 202 | } 203 | 204 | func (m *baseTest) DrainNode(nodeName string, ignoreDaemonSet, deleteLocalData bool) error { 205 | return nil 206 | } 207 | 208 | func (m *baseTest) GetNode(nodeName string) (*apiv1.Node, error) { 209 | node := &apiv1.Node{ 210 | ObjectMeta: metav1.ObjectMeta{ 211 | Name: nodeName, 212 | UID: testCRDUID, 213 | Annotations: map[string]string{ 214 | constantes.AnnotationNodeGroupName: testGroupID, 215 | constantes.AnnotationNodeIndex: "0", 216 | constantes.AnnotationInstanceID: findInstanceID(m.testConfig, nodeName), 217 | constantes.AnnotationNodeAutoProvisionned: "true", 218 | constantes.AnnotationScaleDownDisabled: "false", 219 | constantes.AnnotationNodeManaged: "false", 220 | }, 221 | }, 222 | } 223 | 224 | return node, nil 225 | } 226 | 227 | func (m *baseTest) DeleteNode(nodeName string) error { 228 | return nil 229 | } 230 | 231 | func (m *baseTest) AnnoteNode(nodeName string, annotations map[string]string) error { 232 | return nil 233 | } 234 | 235 | func (m *baseTest) LabelNode(nodeName string, labels map[string]string) error { 236 | return nil 237 | } 238 | 239 | func (m *baseTest) TaintNode(nodeName string, taints ...apiv1.Taint) error { 240 | return nil 241 | } 242 | 243 | func (m *baseTest) WaitNodeToBeReady(nodeName string) error { 244 | return nil 245 | } 246 | 247 | func (m *baseTest) newTestNodeNamedWithState(nodeName string, state AutoScalerServerNodeState) (*autoScalerServerNodeGroupTest, *AutoScalerServerNode, error) { 248 | 249 | if ng, err := m.newTestNodeGroup(); err == nil { 250 | vm := ng.createTestNode(nodeName, state) 251 | 252 | return ng, vm, err 253 | } else { 254 | return nil, nil, err 255 | } 256 | } 257 | 258 | func (m *baseTest) newTestNode(name ...string) (*autoScalerServerNodeGroupTest, *AutoScalerServerNode, error) { 259 | nodeName := testNodeName 260 | 261 | if len(name) > 0 { 262 | nodeName = name[0] 263 | } 264 | 265 | return m.newTestNodeNamedWithState(nodeName, AutoScalerServerNodeStateNotCreated) 266 | } 267 | 268 | func (m *baseTest) newTestNodeGroup() (*autoScalerServerNodeGroupTest, error) { 269 | config, err := m.newTestConfig() 270 | 271 | if err == nil { 272 | if machine, ok := config.Machines[config.DefaultMachineType]; ok { 273 | ng := &autoScalerServerNodeGroupTest{ 274 | baseTest: baseTest{ 275 | t: m.t, 276 | testConfig: m.testConfig, 277 | }, 278 | AutoScalerServerNodeGroup: AutoScalerServerNodeGroup{ 279 | AutoProvision: true, 280 | ServiceIdentifier: config.ServiceIdentifier, 281 | NodeGroupIdentifier: testGroupID, 282 | ProvisionnedNodeNamePrefix: config.ProvisionnedNodeNamePrefix, 283 | ManagedNodeNamePrefix: config.ManagedNodeNamePrefix, 284 | ControlPlaneNamePrefix: config.ControlPlaneNamePrefix, 285 | Status: NodegroupCreated, 286 | MinNodeSize: config.MinNode, 287 | MaxNodeSize: config.MaxNode, 288 | SystemLabels: types.KubernetesLabel{}, 289 | Nodes: make(map[string]*AutoScalerServerNode), 290 | RunningNodes: make(map[int]ServerNodeState), 291 | pendingNodes: make(map[string]*AutoScalerServerNode), 292 | configuration: config, 293 | Machine: machine, 294 | NodeLabels: config.NodeLabels, 295 | }, 296 | } 297 | 298 | return ng, err 299 | } 300 | 301 | m.t.Fatalf("Unable to find machine definition for type: %s", config.DefaultMachineType) 302 | } 303 | 304 | return nil, err 305 | } 306 | 307 | func (m *baseTest) getConfFile() string { 308 | if config := os.Getenv("TEST_CONFIG"); config != "" { 309 | return config 310 | } 311 | 312 | return "../test/config.json" 313 | } 314 | 315 | func (m *baseTest) newTestConfig() (*types.AutoScalerServerConfig, error) { 316 | var config types.AutoScalerServerConfig 317 | 318 | if configStr, err := os.ReadFile(m.getConfFile()); err != nil { 319 | return nil, err 320 | } else { 321 | if err = json.Unmarshal(configStr, &config); err == nil { 322 | m.testConfig = config.GetVSphereConfiguration(testGroupID) 323 | m.testConfig.TestMode = true 324 | config.SSH.TestMode = true 325 | } 326 | 327 | return &config, err 328 | } 329 | } 330 | 331 | func (m *baseTest) ssh() { 332 | config, err := m.newTestConfig() 333 | 334 | if assert.NoError(m.t, err) { 335 | if _, err = utils.Sudo(config.SSH, "127.0.0.1", 1, "ls"); err != nil { 336 | m.t.Errorf("SSH error = %v", err) 337 | } 338 | } 339 | } 340 | 341 | func findInstanceID(vsphere *vsphere.Configuration, nodeName string) string { 342 | if vmUUID, err := vsphere.UUID(nodeName); err == nil { 343 | return vmUUID 344 | } 345 | 346 | return testVMUUID 347 | } 348 | 349 | func Test_SSH(t *testing.T) { 350 | createTestNodegroup(t).ssh() 351 | } 352 | 353 | func createTestNodegroup(t *testing.T) *nodegroupTest { 354 | return &nodegroupTest{ 355 | baseTest: baseTest{ 356 | t: t, 357 | }, 358 | } 359 | } 360 | 361 | func TestNodeGroup_launchVM(t *testing.T) { 362 | createTestNodegroup(t).launchVM() 363 | } 364 | 365 | func TestNodeGroup_startVM(t *testing.T) { 366 | createTestNodegroup(t).startVM() 367 | } 368 | 369 | func TestNodeGroup_stopVM(t *testing.T) { 370 | createTestNodegroup(t).stopVM() 371 | } 372 | 373 | func TestNodeGroup_deleteVM(t *testing.T) { 374 | createTestNodegroup(t).deleteVM() 375 | } 376 | 377 | func TestNodeGroup_statusVM(t *testing.T) { 378 | createTestNodegroup(t).statusVM() 379 | } 380 | 381 | func TestNodeGroupGroup_addNode(t *testing.T) { 382 | createTestNodegroup(t).addNode() 383 | } 384 | 385 | func TestNodeGroupGroup_deleteNode(t *testing.T) { 386 | createTestNodegroup(t).deleteNode() 387 | } 388 | 389 | func TestNodeGroupGroup_deleteNodeGroup(t *testing.T) { 390 | createTestNodegroup(t).deleteNodeGroup() 391 | } 392 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=Fred78290_kubernetes-vmware-autoscaler 2 | sonar.projectName=kubernetes-vmware-autoscaler 3 | sonar.organization=fred78290-github 4 | sonar.projectVersion=v1.28.4 5 | 6 | # ===================================================== 7 | # Meta-data for the project 8 | # ===================================================== 9 | 10 | sonar.links.homepage=https://github.com/Fred78290/kubernetes-vmware-autoscaler 11 | sonar.links.ci=https://travis-ci.org/Fred78290/kubernetes-vmware-autoscaler 12 | sonar.links.scm=https://github.com/Fred78290/kubernetes-vmware-autoscaler 13 | sonar.links.issue=https://github.com/Fred78290/kubernetes-vmware-autoscaler/issues 14 | 15 | # ===================================================== 16 | # Exclude c/cpp/objc files 17 | # ===================================================== 18 | 19 | sonar.c.file.suffixes=- 20 | sonar.cpp.file.suffixes=- 21 | sonar.objc.file.suffixes=- 22 | 23 | # ===================================================== 24 | # Properties that will be shared amongst all modules 25 | # ===================================================== 26 | 27 | # SQ standard properties 28 | sonar.sources=. 29 | sonar.go.exclusions=**/vendor/**,**/grpc/**,**/out/**,**test** 30 | -------------------------------------------------------------------------------- /test/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "use-external-etcd": false, 3 | "src-etcd-ssl-dir": "/etc/etcd/ssl", 4 | "dst-etcd-ssl-dir": "/etc/kubernetes/pki/etcd", 5 | "kubernetes-pki-srcdir": "/etc/kubernetes/pki", 6 | "kubernetes-pki-dstdir": "/etc/kubernetes/pki", 7 | "network": "unix", 8 | "listen": "/var/run/cluster-autoscaler/vmware.sock", 9 | "secret": "vmware", 10 | "minNode": 0, 11 | "maxNode": 9, 12 | "maxNode-per-cycle": 2, 13 | "node-name-prefix": "autoscaled", 14 | "managed-name-prefix": "managed", 15 | "controlplane-name-prefix": "master", 16 | "nodePrice": 0, 17 | "podPrice": 0, 18 | "image": "DC0_H0_VM0", 19 | "optionals": { 20 | "pricing": false, 21 | "getAvailableMachineTypes": false, 22 | "newNodeGroup": false, 23 | "templateNodeInfo": false, 24 | "createNodeGroup": false, 25 | "deleteNodeGroup": false 26 | }, 27 | "kubeadm": { 28 | "address": "192.168.1.20:6443", 29 | "token": "XXX.YYYYYY", 30 | "ca": "sha256:aff09a080fd113f0b5013c8af5f78608599ad1e053efdb59e9948d34269588db", 31 | "extras-args": [ 32 | "--ignore-preflight-errors=All" 33 | ] 34 | }, 35 | "default-machine": "large", 36 | "machines": { 37 | "tiny": { 38 | "memsize": 2048, 39 | "vcpus": 2, 40 | "disksize": 10240 41 | }, 42 | "small": { 43 | "memsize": 4096, 44 | "vcpus": 2, 45 | "disksize": 20480 46 | }, 47 | "medium": { 48 | "memsize": 4096, 49 | "vcpus": 4, 50 | "disksize": 20480 51 | }, 52 | "large": { 53 | "memsize": 8192, 54 | "vcpus": 4, 55 | "disksize": 51200 56 | }, 57 | "xlarge": { 58 | "memsize": 16384, 59 | "vcpus": 4, 60 | "disksize": 102400 61 | }, 62 | "2xlarge": { 63 | "memsize": 16384, 64 | "vcpus": 8, 65 | "disksize": 102400 66 | }, 67 | "4xlarge": { 68 | "memsize": 32768, 69 | "vcpus": 8, 70 | "disksize": 102400 71 | } 72 | }, 73 | "cloud-init": { 74 | "package_update": false, 75 | "package_upgrade": false 76 | }, 77 | "ssh-infos": { 78 | "user": "kubernetes", 79 | "ssh-private-key": "~/.ssh/id_rsa" 80 | }, 81 | "vmware": { 82 | "vmware-ca-k8s": { 83 | "url": "https://127.0.0.1:8989/sdk", 84 | "uid": "user", 85 | "password": "pass", 86 | "insecure": true, 87 | "dc": "DC0", 88 | "datastore": "LocalDS_0", 89 | "resource-pool": "/DC0/host/DC0_H0/Resources", 90 | "vmFolder": "", 91 | "timeout": 300, 92 | "template-name": "DC0_H0_VM0", 93 | "template": false, 94 | "linked": false, 95 | "customization": "", 96 | "network": { 97 | "domain": "sample.com", 98 | "dns": { 99 | "search": [ 100 | "sample.com" 101 | ], 102 | "nameserver": [ 103 | "8.8.8.8" 104 | ] 105 | }, 106 | "interfaces": [ 107 | { 108 | "primary": false, 109 | "exists": true, 110 | "network": "VM Network", 111 | "adapter": "vmxnet3", 112 | "mac-address": "generate", 113 | "nic": "eth0", 114 | "dhcp": true, 115 | "use-dhcp-routes": true 116 | }, 117 | { 118 | "primary": true, 119 | "exists": true, 120 | "network": "DC0_DVPG0", 121 | "adapter": "vmxnet3", 122 | "mac-address": "generate", 123 | "nic": "eth1", 124 | "dhcp": false, 125 | "address": "192.168.1.26", 126 | "gateway": "192.168.1.1", 127 | "netmask": "255.255.255.0" 128 | } 129 | ] 130 | } 131 | } 132 | } 133 | } -------------------------------------------------------------------------------- /test/vsphere.json: -------------------------------------------------------------------------------- 1 | { 2 | "url": "https://127.0.0.1:8989/sdk", 3 | "uid": "user", 4 | "password": "pass", 5 | "insecure": true, 6 | "dc": "DC0", 7 | "datastore": "LocalDS_0", 8 | "resource-pool": "/DC0/host/DC0_H0/Resources", 9 | "vmFolder": "", 10 | "timeout": 6000, 11 | "template-name": "DC0_H0_VM0", 12 | "template": false, 13 | "linked": false, 14 | "customization": "", 15 | "ssh": { 16 | "user": "~", 17 | "ssh-private-key": "~/.ssh/id_rsa" 18 | }, 19 | "cloud-init": { 20 | "package_update": false, 21 | "package_upgrade": false 22 | }, 23 | "old-vm": "DC0_H0_VM0", 24 | "new-vm": { 25 | "name": "vm-autoscaled-test", 26 | "annotation": "autoscaled VM", 27 | "memory": 4096, 28 | "cpus": 4, 29 | "disk": 10240, 30 | "network": { 31 | "domain": "sample.com", 32 | "dns": { 33 | "search": [ 34 | "sample.com" 35 | ], 36 | "nameserver": [ 37 | "8.8.8.8" 38 | ] 39 | }, 40 | "interfaces": [ 41 | { 42 | "primary": false, 43 | "exists": true, 44 | "network": "VM Network", 45 | "adapter": "vmxnet3", 46 | "mac-address": "generate", 47 | "nic": "eth0", 48 | "dhcp": true, 49 | "use-dhcp-routes": true 50 | }, 51 | { 52 | "primary": true, 53 | "exists": true, 54 | "network": "DC0_DVPG0", 55 | "adapter": "vmxnet3", 56 | "mac-address": "generate", 57 | "nic": "eth1", 58 | "dhcp": false, 59 | "address": "192.168.1.26", 60 | "gateway": "192.168.1.1", 61 | "netmask": "255.255.255.0" 62 | } 63 | ] 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /utils/communicator.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "os/exec" 8 | "strings" 9 | "time" 10 | 11 | "github.com/Fred78290/kubernetes-vmware-autoscaler/types" 12 | glog "github.com/sirupsen/logrus" 13 | "golang.org/x/crypto/ssh" 14 | ) 15 | 16 | // AuthMethodFromPrivateKeyFile read public key 17 | func AuthMethodFromPrivateKeyFile(file string) (ssh.AuthMethod, error) { 18 | var buffer []byte 19 | var err error 20 | var key ssh.Signer 21 | 22 | if buffer, err = os.ReadFile(file); err != nil { 23 | glog.Errorf("Can't read key file:%s, reason:%v", file, err) 24 | } else if key, err = ssh.ParsePrivateKey(buffer); err != nil { 25 | glog.Errorf("Can't parse key file:%s, reason:%v", file, err) 26 | } else { 27 | return ssh.PublicKeys(key), nil 28 | } 29 | 30 | return nil, err 31 | } 32 | 33 | // AuthMethodFromPrivateKey read public key 34 | func AuthMethodFromPrivateKey(key string) (ssh.AuthMethod, error) { 35 | var pub ssh.Signer 36 | var err error 37 | var signer ssh.Signer 38 | 39 | if pub, err = ssh.ParsePrivateKey([]byte(key)); err != nil { 40 | glog.Errorf("AuthMethodFromPublicKey fatal error:%v", err) 41 | } else if signer, err = ssh.NewSignerFromKey(pub); err != nil { 42 | glog.Errorf("AuthMethodFromPublicKey fatal error:%v", err) 43 | } else { 44 | return ssh.PublicKeys(signer), nil 45 | } 46 | 47 | return nil, err 48 | } 49 | 50 | // Shell execute local command ignore output 51 | func Shell(args ...string) error { 52 | var stdout bytes.Buffer 53 | var stderr bytes.Buffer 54 | 55 | cmd := exec.Command(args[0], args[1:]...) 56 | 57 | cmd.Stdout = &stdout 58 | cmd.Stderr = &stderr 59 | 60 | if err := cmd.Run(); err != nil { 61 | return fmt.Errorf("%s, %s", err.Error(), strings.TrimSpace(stderr.String())) 62 | } 63 | 64 | return nil 65 | } 66 | 67 | // Scp copy file 68 | func Scp(connect *types.AutoScalerServerSSH, host, src, dst string) error { 69 | 70 | if connect.TestMode { 71 | return nil 72 | } 73 | 74 | return Shell("scp", 75 | "-i", connect.GetAuthKeys(), 76 | "-o", "StrictHostKeyChecking=no", 77 | "-o", "UserKnownHostsFile=/dev/null", 78 | "-p", 79 | "-r", 80 | src, 81 | fmt.Sprintf("%s@%s:%s", connect.GetUserName(), host, dst)) 82 | } 83 | 84 | // Sudo exec ssh command as sudo 85 | func Sudo(connect *types.AutoScalerServerSSH, host string, timeoutInSeconds time.Duration, command ...string) (string, error) { 86 | var sshConfig *ssh.ClientConfig 87 | var err error 88 | var method ssh.AuthMethod 89 | 90 | if connect.TestMode { 91 | return "", nil 92 | } 93 | 94 | if len(connect.Password) > 0 { 95 | method = ssh.Password(connect.Password) 96 | } else if method, err = AuthMethodFromPrivateKeyFile(connect.GetAuthKeys()); err != nil { 97 | return "", err 98 | } 99 | 100 | sshConfig = &ssh.ClientConfig{ 101 | Timeout: timeoutInSeconds * time.Second, 102 | User: connect.GetUserName(), 103 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 104 | Auth: []ssh.AuthMethod{ 105 | method, 106 | }, 107 | } 108 | 109 | connection, err := ssh.Dial("tcp", fmt.Sprintf("%s:22", host), sshConfig) 110 | if err != nil { 111 | return "", fmt.Errorf("failed to dial: %s", err) 112 | } 113 | 114 | session, err := connection.NewSession() 115 | if err != nil { 116 | return "", fmt.Errorf("failed to create session: %s", err) 117 | } 118 | 119 | defer session.Close() 120 | 121 | modes := ssh.TerminalModes{ 122 | ssh.ECHO: 0, // disable echoing 123 | ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud 124 | ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud 125 | } 126 | 127 | if err := session.RequestPty("xterm", 80, 200, modes); err != nil { 128 | return "", fmt.Errorf("request for pseudo terminal failed: %s", err) 129 | } 130 | 131 | var stdout bytes.Buffer 132 | var out []byte 133 | 134 | for _, cmd := range command { 135 | glog.Debugf("Shell:%s", cmd) 136 | 137 | if out, err = session.CombinedOutput(fmt.Sprintf("sudo %s", cmd)); err != nil { 138 | if out != nil { 139 | stdout.Write(out) 140 | } else { 141 | stdout.Write([]byte(fmt.Sprintf("An error occured during su command: %s, error:%v", cmd, err))) 142 | } 143 | break 144 | } else { 145 | stdout.Write(out) 146 | } 147 | } 148 | 149 | if glog.GetLevel() == glog.DebugLevel && err != nil { 150 | glog.Debugf("sudo command:%s, output:%s, error:%v", strings.Join(command, ","), stdout.String(), err) 151 | } 152 | 153 | return strings.TrimSpace(stdout.String()), err 154 | } 155 | -------------------------------------------------------------------------------- /utils/podfilters.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/Fred78290/kubernetes-vmware-autoscaler/types" 9 | apiv1 "k8s.io/api/core/v1" 10 | apierrors "k8s.io/apimachinery/pkg/api/errors" 11 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | "k8s.io/client-go/kubernetes" 13 | ) 14 | 15 | const ( 16 | kindDaemonSet = "DaemonSet" 17 | kindStatefulSet = "StatefulSet" 18 | ) 19 | 20 | // MirrorPodFilter returns true if the supplied pod is not a mirror pod, i.e. a 21 | // pod created by a manifest on the node rather than the API server. 22 | func MirrorPodFilter(p apiv1.Pod) (bool, error) { 23 | _, mirrorPod := p.GetAnnotations()[apiv1.MirrorPodAnnotationKey] 24 | return !mirrorPod, nil 25 | } 26 | 27 | // LocalStoragePodFilter returns true if the supplied pod does not have local 28 | // storage, i.e. does not use any 'empty dir' volumes. 29 | func LocalStoragePodFilter(p apiv1.Pod) (bool, error) { 30 | for _, v := range p.Spec.Volumes { 31 | if v.EmptyDir != nil { 32 | return false, nil 33 | } 34 | } 35 | return true, nil 36 | } 37 | 38 | // UnreplicatedPodFilter returns true if the pod is replicated, i.e. is managed 39 | // by a controller (deployment, daemonset, statefulset, etc) of some sort. 40 | func UnreplicatedPodFilter(p apiv1.Pod) (bool, error) { 41 | // We're fine with 'evicting' unreplicated pods that aren't actually running. 42 | if p.Status.Phase == apiv1.PodSucceeded || p.Status.Phase == apiv1.PodFailed { 43 | return true, nil 44 | } 45 | if metav1.GetControllerOf(&p) == nil { 46 | return false, nil 47 | } 48 | return true, nil 49 | } 50 | 51 | // NewDaemonSetPodFilter returns a FilterFunc that returns true if the supplied 52 | // pod is not managed by an extant DaemonSet. 53 | func NewDaemonSetPodFilter(ctx context.Context, client kubernetes.Interface) types.PodFilterFunc { 54 | return func(p apiv1.Pod) (bool, error) { 55 | c := metav1.GetControllerOf(&p) 56 | if c == nil || c.Kind != kindDaemonSet { 57 | return true, nil 58 | } 59 | 60 | // Pods pass the filter if they were created by a DaemonSet that no 61 | // longer exists. 62 | if _, err := client.AppsV1().DaemonSets(p.GetNamespace()).Get(ctx, c.Name, metav1.GetOptions{}); err != nil { 63 | if apierrors.IsNotFound(err) { 64 | return true, nil 65 | } 66 | return false, fmt.Errorf("cannot get DaemonSet %s/%s, reason: %v", p.GetNamespace(), c.Name, err) 67 | } 68 | return false, nil 69 | } 70 | } 71 | 72 | // NewStatefulSetPodFilter returns a FilterFunc that returns true if the supplied 73 | // pod is not managed by an extant StatefulSet. 74 | func NewStatefulSetPodFilter(ctx context.Context, client kubernetes.Interface) types.PodFilterFunc { 75 | return func(p apiv1.Pod) (bool, error) { 76 | c := metav1.GetControllerOf(&p) 77 | if c == nil || c.Kind != kindStatefulSet { 78 | return true, nil 79 | } 80 | 81 | // Pods pass the filter if they were created by a StatefulSet that no 82 | // longer exists. 83 | if _, err := client.AppsV1().StatefulSets(p.GetNamespace()).Get(ctx, c.Name, metav1.GetOptions{}); err != nil { 84 | if apierrors.IsNotFound(err) { 85 | return true, nil 86 | } 87 | return false, fmt.Errorf("cannot get StatefulSet %s/%s, reason: %v", p.GetNamespace(), c.Name, err) 88 | } 89 | return false, nil 90 | } 91 | } 92 | 93 | // UnprotectedPodFilter returns a FilterFunc that returns true if the 94 | // supplied pod does not have any of the user-specified annotations for 95 | // protection from eviction 96 | func UnprotectedPodFilter(annotations ...string) types.PodFilterFunc { 97 | return func(p apiv1.Pod) (bool, error) { 98 | var filter bool 99 | for _, annot := range annotations { 100 | // Try to split the annotation into key-value pairs 101 | kv := strings.SplitN(annot, "=", 2) 102 | if len(kv) < 2 { 103 | // If the annotation is a single string, then simply check for 104 | // the existence of the annotation key 105 | _, filter = p.GetAnnotations()[kv[0]] 106 | } else { 107 | // If the annotation is a key-value pair, then check if the 108 | // value for the pod annotation matches that of the 109 | // user-specified value 110 | v, ok := p.GetAnnotations()[kv[0]] 111 | filter = ok && v == kv[1] 112 | } 113 | if filter { 114 | return false, nil 115 | } 116 | } 117 | return true, nil 118 | } 119 | } 120 | 121 | // NewPodFilters returns a FilterFunc that returns true if all of the supplied 122 | // FilterFuncs return true. 123 | func NewPodFilters(filters ...types.PodFilterFunc) types.PodFilterFunc { 124 | return func(p apiv1.Pod) (bool, error) { 125 | for _, fn := range filters { 126 | passes, err := fn(p) 127 | if err != nil { 128 | return false, fmt.Errorf("cannot apply filters, reason: %v", err) 129 | } 130 | if !passes { 131 | return false, nil 132 | } 133 | } 134 | return true, nil 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "syscall" 7 | "time" 8 | 9 | "github.com/Fred78290/kubernetes-vmware-autoscaler/types" 10 | 11 | "github.com/Fred78290/kubernetes-vmware-autoscaler/context" 12 | "gopkg.in/yaml.v2" 13 | apiv1 "k8s.io/api/core/v1" 14 | "k8s.io/apimachinery/pkg/util/wait" 15 | ) 16 | 17 | // ShouldTestFeature check if test must be done 18 | func ShouldTestFeature(name string) bool { 19 | if feature := os.Getenv(name); feature != "" { 20 | return feature != "NO" 21 | } 22 | 23 | return true 24 | } 25 | 26 | // MergeKubernetesLabel merge kubernetes map in one 27 | func MergeKubernetesLabel(labels ...types.KubernetesLabel) types.KubernetesLabel { 28 | merged := types.KubernetesLabel{} 29 | 30 | for _, label := range labels { 31 | for k, v := range label { 32 | merged[k] = v 33 | } 34 | } 35 | 36 | return merged 37 | } 38 | 39 | // NodeFromJSON deserialize a string to apiv1.Node 40 | func NodeFromJSON(s string) (*apiv1.Node, error) { 41 | data := &apiv1.Node{} 42 | 43 | err := json.Unmarshal([]byte(s), &data) 44 | 45 | return data, err 46 | } 47 | 48 | // ToYAML serialize interface to yaml 49 | func ToYAML(v interface{}) string { 50 | if v == nil { 51 | return "" 52 | } 53 | 54 | b, _ := yaml.Marshal(v) 55 | 56 | return string(b) 57 | } 58 | 59 | // ToJSON serialize interface to json 60 | func ToJSON(v interface{}) string { 61 | if v == nil { 62 | return "" 63 | } 64 | 65 | b, _ := json.Marshal(v) 66 | 67 | return string(b) 68 | } 69 | 70 | func DirExistAndReadable(name string) bool { 71 | if len(name) == 0 { 72 | return false 73 | } 74 | 75 | if entry, err := os.Stat(name); err != nil { 76 | return false 77 | } else if entry.IsDir() { 78 | 79 | if files, err := os.ReadDir(name); err == nil { 80 | for _, file := range files { 81 | if entry, err := file.Info(); err == nil { 82 | if !entry.IsDir() { 83 | fm := entry.Mode() 84 | sys := entry.Sys().(*syscall.Stat_t) 85 | 86 | if (fm&(1<<2) != 0) || ((fm&(1<<5)) != 0 && os.Getegid() == int(sys.Gid)) || ((fm&(1<<8)) != 0 && (os.Geteuid() == int(sys.Uid))) { 87 | continue 88 | } 89 | } else if DirExistAndReadable(name + "/" + entry.Name()) { 90 | continue 91 | } 92 | } 93 | 94 | return false 95 | } 96 | 97 | return true 98 | } 99 | } 100 | 101 | return false 102 | } 103 | 104 | func FileExistAndReadable(name string) bool { 105 | if len(name) == 0 { 106 | return false 107 | } 108 | 109 | if entry, err := os.Stat(name); err != nil { 110 | if os.IsNotExist(err) { 111 | return false 112 | } 113 | } else { 114 | fm := entry.Mode() 115 | sys := entry.Sys().(*syscall.Stat_t) 116 | 117 | if (fm&(1<<2) != 0) || ((fm&(1<<5)) != 0 && os.Getegid() == int(sys.Gid)) || ((fm&(1<<8)) != 0 && (os.Geteuid() == int(sys.Uid))) { 118 | return true 119 | } 120 | } 121 | 122 | return false 123 | } 124 | 125 | // FileExists Check if FileExists 126 | func FileExists(name string) bool { 127 | if len(name) == 0 { 128 | return false 129 | } 130 | 131 | if _, err := os.Stat(name); err != nil { 132 | if os.IsNotExist(err) { 133 | return false 134 | } 135 | } 136 | 137 | return true 138 | } 139 | 140 | // MinInt min(a,b) 141 | func MinInt(x, y int) int { 142 | if x < y { 143 | return x 144 | } 145 | return y 146 | } 147 | 148 | // MaxInt max(a,b) 149 | func MaxInt(x, y int) int { 150 | if x > y { 151 | return x 152 | } 153 | return y 154 | } 155 | 156 | // MinInt64 min(a,b) 157 | func MinInt64(x, y int64) int64 { 158 | if x < y { 159 | return x 160 | } 161 | return y 162 | } 163 | 164 | // MaxInt64 max(a,b) 165 | func MaxInt64(x, y int64) int64 { 166 | if x > y { 167 | return x 168 | } 169 | return y 170 | } 171 | 172 | func NewRequestContext(requestTimeout time.Duration) *context.Context { 173 | return context.NewContext(time.Duration(requestTimeout.Seconds())) 174 | } 175 | 176 | // Values returns the values of the map m. 177 | // The values will be in an indeterminate order. 178 | func Values[M ~map[K]V, K comparable, V any](m M) []V { 179 | r := make([]V, 0, len(m)) 180 | for _, v := range m { 181 | r = append(r, v) 182 | } 183 | return r 184 | } 185 | 186 | func PollImmediate(interval, timeout time.Duration, condition wait.ConditionFunc) error { 187 | if timeout == 0 { 188 | return wait.PollImmediateInfinite(interval, condition) 189 | } else { 190 | return wait.PollImmediate(interval, timeout, condition) 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /vsphere/autostart.go: -------------------------------------------------------------------------------- 1 | package vsphere 2 | 3 | import ( 4 | "github.com/Fred78290/kubernetes-vmware-autoscaler/context" 5 | "github.com/vmware/govmomi/vim25/methods" 6 | "github.com/vmware/govmomi/vim25/types" 7 | ) 8 | 9 | type HostAutoStartManager struct { 10 | Ref types.ManagedObjectReference 11 | Datacenter *Datacenter 12 | } 13 | 14 | func (h *HostAutoStartManager) SetAutoStart(ctx *context.Context, datastore, name string, startOrder int) error { 15 | var err error 16 | var ds *Datastore 17 | var vm *VirtualMachine 18 | 19 | dc := h.Datacenter 20 | 21 | if ds, err = dc.GetDatastore(ctx, datastore); err == nil { 22 | if vm, err = ds.VirtualMachine(ctx, name); err == nil { 23 | powerInfo := []types.AutoStartPowerInfo{{ 24 | Key: vm.Ref, 25 | StartOrder: int32(startOrder), 26 | StartDelay: -1, 27 | WaitForHeartbeat: types.AutoStartWaitHeartbeatSettingSystemDefault, 28 | StartAction: "powerOn", 29 | StopDelay: -1, 30 | StopAction: "systemDefault", 31 | }} 32 | 33 | req := types.ReconfigureAutostart{ 34 | This: h.Ref, 35 | Spec: types.HostAutoStartManagerConfig{ 36 | PowerInfo: powerInfo, 37 | }, 38 | } 39 | 40 | _, err = methods.ReconfigureAutostart(ctx, dc.VimClient(), &req) 41 | } 42 | } 43 | 44 | return err 45 | } 46 | -------------------------------------------------------------------------------- /vsphere/client.go: -------------------------------------------------------------------------------- 1 | package vsphere 2 | 3 | import ( 4 | "github.com/Fred78290/kubernetes-vmware-autoscaler/context" 5 | "github.com/vmware/govmomi" 6 | "github.com/vmware/govmomi/find" 7 | "github.com/vmware/govmomi/object" 8 | "github.com/vmware/govmomi/vim25" 9 | "github.com/vmware/govmomi/vim25/types" 10 | ) 11 | 12 | // Client Client wrapper 13 | type Client struct { 14 | Client *govmomi.Client 15 | Configuration *Configuration 16 | } 17 | 18 | // GetClient return client 19 | func (c *Client) GetClient() *govmomi.Client { 20 | return c.Client 21 | } 22 | 23 | // VimClient return vim25 client 24 | func (c *Client) VimClient() *vim25.Client { 25 | return c.Client.Client 26 | } 27 | 28 | // VirtualMachine map govmomi VirtualMachine 29 | func (c *Client) VirtualMachine(ref types.ManagedObjectReference) *object.VirtualMachine { 30 | return object.NewVirtualMachine(c.VimClient(), ref) 31 | } 32 | 33 | // Datastore create govmomi Datastore object 34 | func (c *Client) Datastore(ref types.ManagedObjectReference) *object.Datastore { 35 | return object.NewDatastore(c.VimClient(), ref) 36 | } 37 | 38 | // Datacenter return a Datacenter 39 | func (c *Client) Datacenter(ref types.ManagedObjectReference) *object.Datacenter { 40 | return object.NewDatacenter(c.VimClient(), ref) 41 | } 42 | 43 | // GetDatacenter return the datacenter 44 | func (c *Client) GetDatacenter(ctx *context.Context, name string) (*Datacenter, error) { 45 | var dc *object.Datacenter 46 | var err error 47 | 48 | f := find.NewFinder(c.Client.Client, true) 49 | 50 | if dc, err = f.Datacenter(ctx, name); err == nil { 51 | 52 | return &Datacenter{ 53 | Ref: dc.Reference(), 54 | Name: name, 55 | Client: c, 56 | }, nil 57 | } 58 | 59 | return nil, err 60 | } 61 | -------------------------------------------------------------------------------- /vsphere/datacenter.go: -------------------------------------------------------------------------------- 1 | package vsphere 2 | 3 | import ( 4 | "github.com/Fred78290/kubernetes-vmware-autoscaler/context" 5 | glog "github.com/sirupsen/logrus" 6 | "github.com/vmware/govmomi/find" 7 | "github.com/vmware/govmomi/object" 8 | "github.com/vmware/govmomi/vim25" 9 | "github.com/vmware/govmomi/vim25/mo" 10 | "github.com/vmware/govmomi/vim25/types" 11 | ) 12 | 13 | var datacenterKey = contextKey("datacenter") 14 | 15 | // Datacenter represent a datacenter 16 | type Datacenter struct { 17 | Ref types.ManagedObjectReference 18 | Name string 19 | Client *Client 20 | } 21 | 22 | // Datacenter return a Datacenter 23 | func (dc *Datacenter) Datacenter(ctx *context.Context) *object.Datacenter { 24 | if d := ctx.Value(datacenterKey); d != nil { 25 | return d.(*object.Datacenter) 26 | } 27 | 28 | f := find.NewFinder(dc.VimClient(), true) 29 | 30 | d, err := f.Datacenter(ctx, dc.Name) 31 | 32 | if err != nil { 33 | glog.Fatalf("Can't find datacenter:%s", dc.Name) 34 | } 35 | 36 | ctx.WithValue(datacenterKey, d) 37 | 38 | return d 39 | } 40 | 41 | // VimClient return the VIM25 client 42 | func (dc *Datacenter) VimClient() *vim25.Client { 43 | return dc.Client.VimClient() 44 | } 45 | 46 | // NewFinder create a finder 47 | func (dc *Datacenter) NewFinder(ctx *context.Context) *find.Finder { 48 | d := object.NewDatacenter(dc.VimClient(), dc.Ref) 49 | f := find.NewFinder(dc.VimClient(), true) 50 | f.SetDatacenter(d) 51 | 52 | return f 53 | } 54 | 55 | // GetDatastore retrieve named datastore 56 | func (dc *Datacenter) GetDatastore(ctx *context.Context, name string) (*Datastore, error) { 57 | var ds *object.Datastore 58 | var err error 59 | 60 | f := dc.NewFinder(ctx) 61 | 62 | if ds, err = f.Datastore(ctx, name); err == nil { 63 | return &Datastore{ 64 | Ref: ds.Reference(), 65 | Name: name, 66 | Datacenter: dc, 67 | }, nil 68 | } 69 | 70 | return nil, err 71 | } 72 | 73 | // GetHostAutoStartManager return the HostAutoStartManager 74 | func (dc *Datacenter) GetHostAutoStartManager(ctx *context.Context, esxi string) (*HostAutoStartManager, error) { 75 | var host *object.HostSystem 76 | var err error 77 | var mhs mo.HostSystem 78 | var mhas mo.HostAutoStartManager 79 | 80 | if host, err = dc.GetHostSystem(ctx, esxi); err == nil { 81 | if err = host.Properties(ctx, host.Reference(), []string{"configManager.autoStartManager"}, &mhs); err == nil { 82 | if err = host.Properties(ctx, *mhs.ConfigManager.AutoStartManager, nil, &mhas); err == nil { 83 | return &HostAutoStartManager{ 84 | Ref: mhas.Self, 85 | Datacenter: dc, 86 | }, nil 87 | } 88 | } 89 | } 90 | 91 | return nil, err 92 | } 93 | 94 | // GetHostSystem return the default hostsystem 95 | func (dc *Datacenter) GetHostSystem(ctx *context.Context, esxi string) (*object.HostSystem, error) { 96 | f := dc.NewFinder(ctx) 97 | 98 | return f.HostSystemOrDefault(ctx, esxi) 99 | } 100 | -------------------------------------------------------------------------------- /vsphere/datastore.go: -------------------------------------------------------------------------------- 1 | package vsphere 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path" 7 | "strings" 8 | 9 | "github.com/Fred78290/kubernetes-vmware-autoscaler/context" 10 | glog "github.com/sirupsen/logrus" 11 | 12 | "github.com/vmware/govmomi/govc/flags" 13 | "github.com/vmware/govmomi/object" 14 | "github.com/vmware/govmomi/vim25" 15 | "github.com/vmware/govmomi/vim25/mo" 16 | "github.com/vmware/govmomi/vim25/types" 17 | ) 18 | 19 | // Key type for storing flag instances in a context.Context. 20 | type contextKey string 21 | 22 | var outputKey = contextKey("output") 23 | var datastoreKey = contextKey("datastore") 24 | 25 | // Datastore datastore wrapper 26 | type Datastore struct { 27 | Ref types.ManagedObjectReference 28 | Name string 29 | Datacenter *Datacenter 30 | } 31 | 32 | type listOutput struct { 33 | rs []types.HostDatastoreBrowserSearchResults 34 | recurse bool 35 | all bool 36 | slash bool 37 | } 38 | 39 | func arrayContains(arrayOfString []string, value string) bool { 40 | for _, v := range arrayOfString { 41 | if v == value { 42 | return true 43 | } 44 | } 45 | 46 | return false 47 | } 48 | 49 | func (o *listOutput) add(r types.HostDatastoreBrowserSearchResults) { 50 | if o.recurse && !o.all { 51 | // filter out ".hidden" directories 52 | path := strings.SplitN(r.FolderPath, " ", 2) 53 | if len(path) == 2 { 54 | path = strings.Split(path[1], "/") 55 | if path[0] == "." { 56 | path = path[1:] 57 | } 58 | 59 | for _, p := range path { 60 | if len(p) != 0 && p[0] == '.' { 61 | return 62 | } 63 | } 64 | } 65 | } 66 | 67 | res := r 68 | res.File = nil 69 | 70 | for _, f := range r.File { 71 | if f.GetFileInfo().Path[0] == '.' && !o.all { 72 | continue 73 | } 74 | 75 | if o.slash { 76 | if d, ok := f.(*types.FolderFileInfo); ok { 77 | d.Path += "/" 78 | } 79 | } 80 | 81 | res.File = append(res.File, f) 82 | } 83 | 84 | o.rs = append(o.rs, res) 85 | } 86 | 87 | // Datastore create govmomi Datastore object 88 | func (ds *Datastore) Datastore(ctx *context.Context) *object.Datastore { 89 | if d := ctx.Value(datastoreKey); d != nil { 90 | return d.(*object.Datastore) 91 | } 92 | 93 | f := ds.Datacenter.NewFinder(ctx) 94 | 95 | d, err := f.ObjectReference(ctx, ds.Ref) 96 | 97 | if err != nil { 98 | glog.Fatalf("Can't find datastore:%s", ds.Name) 99 | } 100 | // d := object.NewDatastore(ds.Datacenter.VimClient(), ds.Ref) 101 | 102 | ctx.WithValue(datastoreKey, d) 103 | 104 | return d.(*object.Datastore) 105 | } 106 | 107 | // VimClient return the VIM25 client 108 | func (ds *Datastore) VimClient() *vim25.Client { 109 | return ds.Datacenter.VimClient() 110 | } 111 | 112 | func (ds *Datastore) findVM(ctx *context.Context, name string) (*object.VirtualMachine, error) { 113 | key := fmt.Sprintf("[%s] %s", ds.Name, name) 114 | 115 | if v := ctx.Value(key); v != nil { 116 | return v.(*object.VirtualMachine), nil 117 | } 118 | 119 | f := ds.Datacenter.NewFinder(ctx) 120 | 121 | vm, err := f.VirtualMachine(ctx, name) 122 | 123 | if err == nil { 124 | ctx.WithValue(key, vm) 125 | ctx.WithValue(vm.Reference().String(), vm) 126 | } 127 | 128 | return vm, err 129 | } 130 | 131 | func (ds *Datastore) resourcePool(ctx *context.Context, name string) (*object.ResourcePool, error) { 132 | f := ds.Datacenter.NewFinder(ctx) 133 | 134 | return f.ResourcePoolOrDefault(ctx, name) 135 | } 136 | 137 | func (ds *Datastore) vmFolder(ctx *context.Context, name string) (*object.Folder, error) { 138 | f := ds.Datacenter.NewFinder(ctx) 139 | 140 | if len(name) != 0 { 141 | es, err := f.ManagedObjectList(ctx, name) 142 | 143 | if err != nil { 144 | return nil, err 145 | } 146 | 147 | for _, e := range es { 148 | switch o := e.Object.(type) { 149 | case mo.Folder: 150 | if arrayContains(o.ChildType, "VirtualMachine") { 151 | 152 | folder := object.NewFolder(ds.VimClient(), o.Reference()) 153 | folder.InventoryPath = e.Path 154 | 155 | return folder, err 156 | } 157 | } 158 | } 159 | 160 | return nil, fmt.Errorf("folder %s not found", name) 161 | } 162 | 163 | return f.DefaultFolder(ctx) 164 | } 165 | 166 | func (ds *Datastore) output(ctx *context.Context) *flags.OutputFlag { 167 | if v := ctx.Value(outputKey); v != nil { 168 | return v.(*flags.OutputFlag) 169 | } 170 | 171 | v := &flags.OutputFlag{Out: os.Stdout} 172 | ctx.WithValue(outputKey, v) 173 | 174 | return v 175 | } 176 | 177 | // CreateVirtualMachine create a new virtual machine 178 | func (ds *Datastore) CreateVirtualMachine(ctx *context.Context, name, templateName, vmFolder, resourceName string, template bool, linkedClone bool, network *Network, customization string, nodeIndex int) (*VirtualMachine, error) { 179 | var templateVM *object.VirtualMachine 180 | var folder *object.Folder 181 | var resourcePool *object.ResourcePool 182 | var task *object.Task 183 | var err error 184 | var vm *VirtualMachine 185 | 186 | // config := ds.Datacenter.Client.Configuration 187 | output := ds.output(ctx) 188 | 189 | if templateVM, err = ds.findVM(ctx, templateName); err == nil { 190 | 191 | logger := output.ProgressLogger(fmt.Sprintf("Cloning %s to %s...", templateVM.InventoryPath, name)) 192 | defer logger.Wait() 193 | 194 | if folder, err = ds.vmFolder(ctx, vmFolder); err == nil { 195 | if resourcePool, err = ds.resourcePool(ctx, resourceName); err == nil { 196 | // prepare virtual device config spec for network card 197 | configSpecs := []types.BaseVirtualDeviceConfigSpec{} 198 | 199 | if network != nil { 200 | if devices, err := templateVM.Device(ctx); err == nil { 201 | for _, inf := range network.Interfaces { 202 | if inf.NeedToReconfigure(nodeIndex) { 203 | // In case we dont find the preconfigured net card, we add it 204 | inf.Existing = false 205 | 206 | // Find the preconfigured device 207 | for _, device := range devices { 208 | // It's an ether device? 209 | if ethernet, ok := device.(types.BaseVirtualEthernetCard); ok { 210 | // Match my network? 211 | if match, err := inf.MatchInterface(ctx, ds.Datacenter, ethernet.GetVirtualEthernetCard()); match && err == nil { 212 | 213 | // Change the mac address 214 | if inf.ChangeAddress(ethernet.GetVirtualEthernetCard(), nodeIndex) { 215 | configSpecs = append(configSpecs, &types.VirtualDeviceConfigSpec{ 216 | Operation: types.VirtualDeviceConfigSpecOperationEdit, 217 | Device: device, 218 | }) 219 | } 220 | 221 | // Ok don't need to add one 222 | inf.Existing = true 223 | 224 | break 225 | } else if err != nil { 226 | return vm, err 227 | } 228 | } 229 | } 230 | } 231 | } 232 | } else { 233 | return vm, err 234 | } 235 | } 236 | 237 | folderref := folder.Reference() 238 | poolref := resourcePool.Reference() 239 | 240 | cloneSpec := &types.VirtualMachineCloneSpec{ 241 | PowerOn: false, 242 | Template: template, 243 | } 244 | 245 | relocateSpec := types.VirtualMachineRelocateSpec{ 246 | DeviceChange: configSpecs, 247 | Folder: &folderref, 248 | Pool: &poolref, 249 | } 250 | 251 | if linkedClone { 252 | relocateSpec.DiskMoveType = string(types.VirtualMachineRelocateDiskMoveOptionsMoveAllDiskBackingsAndAllowSharing) 253 | } 254 | 255 | cloneSpec.Location = relocateSpec 256 | cloneSpec.Location.Datastore = &ds.Ref 257 | 258 | // check if customization specification requested 259 | if len(customization) > 0 { 260 | // get the customization spec manager 261 | customizationSpecManager := object.NewCustomizationSpecManager(ds.VimClient()) 262 | // check if customization specification exists 263 | exists, err := customizationSpecManager.DoesCustomizationSpecExist(ctx, customization) 264 | 265 | if err != nil { 266 | return nil, err 267 | } 268 | 269 | if !exists { 270 | return nil, fmt.Errorf("customization specification %s does not exists", customization) 271 | } 272 | 273 | // get the customization specification 274 | customSpecItem, err := customizationSpecManager.GetCustomizationSpec(ctx, customization) 275 | if err != nil { 276 | return nil, err 277 | } 278 | customSpec := customSpecItem.Spec 279 | // set the customization 280 | cloneSpec.Customization = &customSpec 281 | } 282 | 283 | if task, err = templateVM.Clone(ctx, folder, name, *cloneSpec); err == nil { 284 | var info *types.TaskInfo 285 | 286 | if info, err = task.WaitForResult(ctx, logger); err == nil { 287 | vm = &VirtualMachine{ 288 | Ref: info.Result.(types.ManagedObjectReference), 289 | Name: name, 290 | Datastore: ds, 291 | } 292 | } 293 | } 294 | } 295 | } 296 | 297 | } 298 | 299 | return vm, err 300 | } 301 | 302 | // VirtualMachine retrieve the specified virtual machine 303 | func (ds *Datastore) VirtualMachine(ctx *context.Context, name string) (*VirtualMachine, error) { 304 | 305 | vm, err := ds.findVM(ctx, name) 306 | 307 | if err == nil { 308 | return &VirtualMachine{ 309 | Ref: vm.Reference(), 310 | Name: name, 311 | Datastore: ds, 312 | }, nil 313 | } 314 | 315 | return nil, err 316 | } 317 | 318 | // ListPath return object list matching path 319 | func (ds *Datastore) ListPath(ctx *context.Context, b *object.HostDatastoreBrowser, path string, spec types.HostDatastoreBrowserSearchSpec, recurse bool) ([]types.HostDatastoreBrowserSearchResults, error) { 320 | path = ds.Datastore(ctx).Path(path) 321 | 322 | search := b.SearchDatastore 323 | if recurse { 324 | search = b.SearchDatastoreSubFolders 325 | } 326 | 327 | task, err := search(ctx, path, &spec) 328 | if err != nil { 329 | return nil, err 330 | } 331 | 332 | info, err := task.WaitForResult(ctx, nil) 333 | if err != nil { 334 | return nil, err 335 | } 336 | 337 | switch r := info.Result.(type) { 338 | case types.HostDatastoreBrowserSearchResults: 339 | return []types.HostDatastoreBrowserSearchResults{r}, nil 340 | case types.ArrayOfHostDatastoreBrowserSearchResults: 341 | return r.HostDatastoreBrowserSearchResults, nil 342 | default: 343 | panic(fmt.Sprintf("unknown result type: %T", r)) 344 | } 345 | } 346 | 347 | func isInvalid(err error) bool { 348 | if f, ok := err.(types.HasFault); ok { 349 | switch f.Fault().(type) { 350 | case *types.InvalidArgument: 351 | return true 352 | } 353 | } 354 | 355 | return false 356 | } 357 | 358 | // List list VM inside the datastore 359 | func (ds *Datastore) List(ctx *context.Context) ([]*VirtualMachine, error) { 360 | var browser *object.HostDatastoreBrowser 361 | var err error 362 | var vms []*VirtualMachine 363 | 364 | if browser, err = ds.Datastore(ctx).Browser(ctx); err == nil { 365 | 366 | spec := types.HostDatastoreBrowserSearchSpec{ 367 | MatchPattern: []string{"*"}, 368 | } 369 | 370 | fromPath := object.DatastorePath{ 371 | Datastore: ds.Name, 372 | Path: "", 373 | } 374 | 375 | arg := fromPath.Path 376 | 377 | result := &listOutput{ 378 | rs: make([]types.HostDatastoreBrowserSearchResults, 0), 379 | recurse: false, 380 | all: false, 381 | slash: true, 382 | } 383 | 384 | for i := 0; ; i++ { 385 | var r []types.HostDatastoreBrowserSearchResults 386 | 387 | r, err = ds.ListPath(ctx, browser, arg, spec, false) 388 | 389 | if err != nil { 390 | // Treat the argument as a match pattern if not found as directory 391 | if i == 0 && types.IsFileNotFound(err) || isInvalid(err) { 392 | spec.MatchPattern[0] = path.Base(arg) 393 | arg = path.Dir(arg) 394 | continue 395 | } 396 | 397 | return nil, err 398 | } 399 | 400 | for n := range r { 401 | result.add(r[n]) 402 | } 403 | 404 | break 405 | } 406 | 407 | f := ds.Datacenter.NewFinder(ctx) 408 | 409 | vms = make([]*VirtualMachine, 0, len(result.rs)) 410 | 411 | for _, item := range result.rs { 412 | // Find virtual machines in datacenter 413 | for _, file := range item.File { 414 | info := file.GetFileInfo() 415 | vm, err := f.VirtualMachine(ctx, info.Path) 416 | 417 | if err == nil { 418 | vms = append(vms, &VirtualMachine{ 419 | Ref: vm.Reference(), 420 | Name: vm.Name(), 421 | Datastore: ds, 422 | }) 423 | } 424 | } 425 | } 426 | } 427 | 428 | return vms, err 429 | } 430 | -------------------------------------------------------------------------------- /vsphere/vsphere_test.go: -------------------------------------------------------------------------------- 1 | package vsphere_test 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "testing" 7 | "time" 8 | 9 | glog "github.com/sirupsen/logrus" 10 | "github.com/stretchr/testify/assert" 11 | 12 | "github.com/Fred78290/kubernetes-vmware-autoscaler/context" 13 | "github.com/Fred78290/kubernetes-vmware-autoscaler/types" 14 | "github.com/Fred78290/kubernetes-vmware-autoscaler/utils" 15 | "github.com/Fred78290/kubernetes-vmware-autoscaler/vsphere" 16 | ) 17 | 18 | type ConfigurationTest struct { 19 | vsphere.Configuration 20 | CloudInit interface{} `json:"cloud-init"` 21 | SSH types.AutoScalerServerSSH `json:"ssh"` 22 | VM string `json:"old-vm"` 23 | New *NewVirtualMachineConf `json:"new-vm"` 24 | inited bool 25 | } 26 | 27 | type NewVirtualMachineConf struct { 28 | Name string 29 | Annotation string 30 | Memory int 31 | CPUS int 32 | Disk int 33 | Network *vsphere.Network 34 | } 35 | 36 | var testConfig ConfigurationTest 37 | var confName = "../test/vsphere.json" 38 | 39 | func loadFromJson(fileName string) *ConfigurationTest { 40 | if !testConfig.inited { 41 | if configStr, err := os.ReadFile(fileName); err != nil { 42 | glog.Fatalf("failed to open config file:%s, error:%v", fileName, err) 43 | } else { 44 | err = json.Unmarshal(configStr, &testConfig) 45 | 46 | if err != nil { 47 | glog.Fatalf("failed to decode config file:%s, error:%v", fileName, err) 48 | } 49 | } 50 | } 51 | 52 | return &testConfig 53 | } 54 | 55 | func Test_AuthMethodKey(t *testing.T) { 56 | if utils.ShouldTestFeature("Test_AuthMethodKey") { 57 | config := loadFromJson(confName) 58 | 59 | _, err := utils.AuthMethodFromPrivateKeyFile(config.SSH.GetAuthKeys()) 60 | 61 | if assert.NoError(t, err) { 62 | t.Log("OK") 63 | } 64 | } 65 | } 66 | 67 | func Test_Sudo(t *testing.T) { 68 | if utils.ShouldTestFeature("Test_Sudo") { 69 | config := loadFromJson(confName) 70 | 71 | out, err := utils.Sudo(&config.SSH, "localhost", 30*time.Second, "ls") 72 | 73 | if assert.NoError(t, err) { 74 | t.Log(out) 75 | } 76 | } 77 | } 78 | 79 | func Test_CIDR(t *testing.T) { 80 | if utils.ShouldTestFeature("Test_CIDR") { 81 | 82 | cidr := vsphere.ToCIDR("10.65.4.201", "255.255.255.0") 83 | 84 | if assert.Equal(t, cidr, "10.65.4.201/24") { 85 | cidr := vsphere.ToCIDR("10.65.4.201", "") 86 | assert.Equal(t, cidr, "10.65.4.201/8") 87 | } 88 | } 89 | } 90 | 91 | func Test_getVM(t *testing.T) { 92 | if utils.ShouldTestFeature("Test_getVM") { 93 | config := loadFromJson(confName) 94 | 95 | ctx := context.NewContext(config.Timeout) 96 | defer ctx.Cancel() 97 | 98 | vm, err := config.VirtualMachineWithContext(ctx, config.VM) 99 | 100 | if assert.NoError(t, err) { 101 | if assert.NotNil(t, vm) { 102 | status, err := vm.Status(ctx) 103 | 104 | if assert.NoErrorf(t, err, "Can't get status of VM") { 105 | t.Logf("The power of vm is:%v", status.Powered) 106 | } 107 | } 108 | } 109 | } 110 | } 111 | 112 | func Test_listVM(t *testing.T) { 113 | if utils.ShouldTestFeature("Test_listVM") { 114 | config := loadFromJson(confName) 115 | 116 | ctx := context.NewContext(config.Timeout) 117 | defer ctx.Cancel() 118 | 119 | vms, err := config.VirtualMachineListWithContext(ctx) 120 | 121 | if assert.NoError(t, err) { 122 | if assert.NotNil(t, vms) { 123 | for _, vm := range vms { 124 | status, err := vm.Status(ctx) 125 | 126 | if assert.NoErrorf(t, err, "Can't get status of VM") { 127 | t.Logf("The power of vm %s is:%v", vm.Name, status.Powered) 128 | } 129 | } 130 | } 131 | } 132 | } 133 | } 134 | 135 | func Test_createVM(t *testing.T) { 136 | if utils.ShouldTestFeature("Test_createVM") { 137 | config := loadFromJson(confName) 138 | 139 | _, err := config.Create(config.New.Name, config.SSH.GetUserName(), config.SSH.GetAuthKeys(), config.CloudInit, config.New.Network, config.New.Annotation, false, config.New.Memory, config.New.CPUS, config.New.Disk, 0) 140 | 141 | if assert.NoError(t, err, "Can't create VM") { 142 | t.Logf("VM created") 143 | } 144 | } 145 | } 146 | 147 | func Test_statusVM(t *testing.T) { 148 | if utils.ShouldTestFeature("Test_statusVM") { 149 | config := loadFromJson(confName) 150 | 151 | status, err := config.Status(config.New.Name) 152 | 153 | if assert.NoError(t, err, "Can't get status VM") { 154 | t.Logf("The power of vm %s is:%v", config.New.Name, status.Powered) 155 | } 156 | } 157 | } 158 | 159 | func Test_powerOnVM(t *testing.T) { 160 | if utils.ShouldTestFeature("Test_powerOnVM") { 161 | config := loadFromJson(confName) 162 | 163 | if status, err := config.Status(config.New.Name); assert.NoError(t, err, "Can't get status on VM") && status.Powered == false { 164 | err = config.PowerOn(config.New.Name) 165 | 166 | if assert.NoError(t, err, "Can't power on VM") { 167 | ipaddr, err := config.WaitForIP(config.New.Name) 168 | 169 | if assert.NoError(t, err, "Can't get IP") { 170 | t.Logf("VM powered with IP:%s", ipaddr) 171 | } 172 | } 173 | } 174 | } 175 | } 176 | 177 | func Test_powerOffVM(t *testing.T) { 178 | if utils.ShouldTestFeature("Test_powerOffVM") { 179 | config := loadFromJson(confName) 180 | 181 | if status, err := config.Status(config.New.Name); assert.NoError(t, err, "Can't get status on VM") && status.Powered { 182 | err = config.PowerOff(config.New.Name) 183 | 184 | if assert.NoError(t, err, "Can't power off VM") { 185 | t.Logf("VM shutdown") 186 | } 187 | } 188 | } 189 | } 190 | 191 | func Test_shutdownGuest(t *testing.T) { 192 | if utils.ShouldTestFeature("Test_shutdownGuest") { 193 | config := loadFromJson(confName) 194 | 195 | if status, err := config.Status(config.New.Name); assert.NoError(t, err, "Can't get status on VM") && status.Powered { 196 | err = config.ShutdownGuest(config.New.Name) 197 | 198 | if assert.NoError(t, err, "Can't power off VM") { 199 | t.Logf("VM shutdown") 200 | } 201 | } 202 | } 203 | } 204 | 205 | func Test_deleteVM(t *testing.T) { 206 | if utils.ShouldTestFeature("Test_deleteVM") { 207 | config := loadFromJson(confName) 208 | 209 | err := config.Delete(config.New.Name) 210 | 211 | if assert.NoError(t, err, "Can't delete VM") { 212 | t.Logf("VM deleted") 213 | } 214 | } 215 | } 216 | --------------------------------------------------------------------------------