├── .circleci └── config.yml ├── .github └── workflows │ ├── zz_generated.add-team-labels.yaml │ ├── zz_generated.add-to-project-board.yaml │ ├── zz_generated.check_values_schema.yaml │ ├── zz_generated.create_release.yaml │ ├── zz_generated.create_release_pr.yaml │ └── zz_generated.gitleaks.yaml ├── .gitignore ├── .nancy-ignore ├── .nancy-ignore.generated ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── CODEOWNERS ├── CONTRIBUTING.md ├── DCO ├── Dockerfile ├── LICENSE ├── Makefile ├── Makefile.gen.app.mk ├── Makefile.gen.go.mk ├── README.md ├── SECURITY.md ├── client ├── azure_client_set.go ├── error.go ├── factory.go ├── organization_factory.go ├── response.go ├── response_test.go └── senddecorator │ ├── configurator.go │ ├── error.go │ ├── metrics.go │ ├── ratelimit_circuitbreaker.go │ └── ratelimit_circuitbreaker_test.go ├── docs ├── azure-node-pools-crds.svg ├── azure-node-pools.md ├── azure_authentication.md ├── circleci_environment.png ├── circleci_options.png ├── cluster-upgrade-to-nodepools.plantuml ├── cluster-upgrade-to-nodepools.svg ├── debug_e2etests.md ├── delegatedResourceManagement.json ├── delegatedResourceManagement.parameters.json ├── instances-states-v12.svg ├── kubectl.png ├── rerun.png ├── resourcegroupname.png ├── running_e2etests_locally.md └── ssh.png ├── flag ├── flag.go └── service │ ├── azure │ ├── azure.go │ ├── hostcluster │ │ ├── host_cluster.go │ │ └── tenant │ │ │ └── tenant.go │ ├── msi │ │ └── msi.go │ └── template │ │ ├── template.go │ │ └── uri │ │ └── uri.go │ ├── cluster │ ├── calico │ │ └── calico.go │ ├── cluster.go │ ├── docker │ │ ├── daemon │ │ │ └── daemon.go │ │ └── docker.go │ ├── etcd │ │ └── etcd.go │ └── kubernetes │ │ ├── api │ │ └── api.go │ │ ├── ingress │ │ └── ingress.go │ │ ├── kubelet │ │ └── kubelet.go │ │ ├── kubernetes.go │ │ └── ssh │ │ └── ssh.go │ ├── debug │ └── debug.go │ ├── installation │ ├── guest │ │ ├── guest.go │ │ └── ipam │ │ │ ├── ipam.go │ │ │ └── network │ │ │ └── network.go │ ├── installation.go │ └── tenant │ │ ├── kubernetes │ │ ├── api │ │ │ ├── api.go │ │ │ └── auth │ │ │ │ ├── auth.go │ │ │ │ └── provider │ │ │ │ ├── oidc │ │ │ │ └── oidc.go │ │ │ │ └── provider.go │ │ └── kubernetes.go │ │ └── tenant.go │ ├── registry │ └── registry.go │ ├── sentry │ └── sentry.go │ ├── service.go │ └── tenant │ ├── ignition │ └── ignition.go │ ├── ssh │ └── ssh.go │ └── tenant.go ├── go.mod ├── go.sum ├── helm └── azure-operator │ ├── Chart.yaml │ ├── templates │ ├── _helpers.tpl │ ├── _resource.tpl │ ├── configmap.yaml │ ├── deployment.yaml │ ├── network-policy.yaml │ ├── psp.yaml │ ├── pss-exceptions.yaml │ ├── rbac.yaml │ ├── secret.yaml │ ├── service-account.yaml │ ├── service.yaml │ └── vpa.yaml │ ├── values.schema.json │ └── values.yaml ├── integration └── test │ └── crmapping │ ├── crmapping_test.go │ ├── error.go │ └── testdata │ ├── case-0-create-AzureConfig_AzureCluster.golden │ ├── case-0-create-AzureConfig_AzureConfig.golden │ ├── case-0-create-AzureConfig_AzureMachine.golden │ ├── case-0-create-AzureConfig_Cluster.golden │ ├── case-0-migrate-AzureConfig_AzureCluster.golden │ ├── case-0-migrate-AzureConfig_AzureConfig.golden │ ├── case-0-migrate-AzureConfig_AzureMachine.golden │ ├── case-0-migrate-AzureConfig_Cluster.golden │ ├── namespace-cluster.yaml │ ├── namespace-giantswarm.yaml │ ├── namespace-org.yaml │ ├── simple_azurecluster.yaml │ ├── simple_azureconfig.yaml │ ├── simple_azuremachine.yaml │ └── simple_cluster.yaml ├── main.go ├── pkg ├── annotation │ └── annotation.go ├── azureconditions │ ├── deploymentchecker.go │ └── error.go ├── backpressure │ ├── backpressure.go │ └── backpressure_test.go ├── checksum │ ├── checksum.go │ ├── checksum_test.go │ └── error.go ├── credential │ ├── credential.go │ ├── credential_test.go │ ├── error.go │ └── spec.go ├── drainer │ ├── drainer.go │ ├── error.go │ └── key.go ├── employees │ ├── employees.go │ ├── employees_test.go │ └── error.go ├── handler │ ├── ipam │ │ ├── azure_config_checker.go │ │ ├── azure_config_network_range_getter.go │ │ ├── azure_config_persister.go │ │ ├── azure_machinepool_network_range_getter.go │ │ ├── azure_machinepool_subnet_checker.go │ │ ├── azure_machinepool_subnet_collector.go │ │ ├── azure_machinepool_subnet_persister.go │ │ ├── azure_machinepool_subnet_releaser.go │ │ ├── create.go │ │ ├── create_test.go │ │ ├── delete.go │ │ ├── error.go │ │ ├── nop_releaser.go │ │ ├── resource.go │ │ ├── spec.go │ │ ├── test_checker.go │ │ ├── test_collector.go │ │ ├── test_network_range_getter.go │ │ ├── test_persister.go │ │ └── virtualnetwork_collector.go │ ├── nodes │ │ ├── draining.go │ │ ├── error.go │ │ ├── instances.go │ │ ├── nodes.go │ │ ├── resource.go │ │ ├── scalestrategy │ │ │ ├── incremental.go │ │ │ ├── interface.go │ │ │ ├── quick.go │ │ │ ├── staircase.go │ │ │ └── staircase_test.go │ │ ├── state │ │ │ ├── error.go │ │ │ ├── funcs.go │ │ │ ├── funcs_test.go │ │ │ └── types.go │ │ ├── status.go │ │ └── upgrade.go │ └── release │ │ ├── create.go │ │ ├── create_test.go │ │ ├── delete.go │ │ ├── error.go │ │ └── resource.go ├── helpers │ ├── api.go │ ├── azuremachine.go │ ├── error.go │ └── vmss │ │ ├── templates.go │ │ └── types.go ├── httputil │ ├── error.go │ ├── parser.go │ └── parser_test.go ├── label │ └── label.go ├── locker │ ├── error.go │ ├── kubelock_locker.go │ ├── mutex_locker.go │ └── spec.go ├── machinepoolmigration │ ├── checker.go │ ├── error.go │ └── references.go ├── mock │ └── mock_tenantcluster │ │ └── factory.go ├── normalize │ ├── normalize.go │ └── normalize_test.go ├── project │ ├── project.go │ └── version_bundle.go └── tenantcluster │ ├── cached.go │ ├── error.go │ ├── factory.go │ └── spec.go ├── renovate.json5 ├── scripts ├── flowchart.sh ├── flowchart.template.html ├── list-releases.sh ├── migrate_master_azs.sh └── update-cluster.sh ├── server ├── endpoint │ ├── endpoint.go │ └── error.go ├── error.go └── server.go └── service ├── collector ├── azure_api_metrics.go ├── error.go └── spec.go ├── controller ├── azurecluster │ ├── controller.go │ ├── error.go │ └── handler │ │ ├── azureclusterconditions │ │ ├── conditionready.go │ │ ├── conditionvnetpeeringready.go │ │ ├── create.go │ │ ├── delete.go │ │ ├── error.go │ │ └── resource.go │ │ ├── azureclusterconfig │ │ ├── create.go │ │ ├── delete.go │ │ ├── error.go │ │ └── resource.go │ │ ├── azureclusteridentity │ │ ├── create.go │ │ ├── delete.go │ │ ├── error.go │ │ ├── naming.go │ │ └── resource.go │ │ ├── azureclusterupgrade │ │ ├── error.go │ │ └── resource.go │ │ ├── azureconfig │ │ ├── apiserver.go │ │ ├── availability_zones.go │ │ ├── availability_zones_test.go │ │ ├── create.go │ │ ├── delete.go │ │ ├── equality.go │ │ ├── error.go │ │ └── resource.go │ │ └── subnet │ │ ├── error.go │ │ ├── resource.go │ │ └── template │ │ ├── main.json │ │ └── template.go ├── azureconfig │ ├── controller.go │ ├── error.go │ └── handler │ │ ├── azureconfigfinalizer │ │ ├── error.go │ │ └── resource.go │ │ ├── blobobject │ │ ├── create.go │ │ ├── current.go │ │ ├── delete.go │ │ ├── desired.go │ │ ├── error.go │ │ ├── mock.go │ │ ├── resource.go │ │ ├── types.go │ │ ├── update.go │ │ └── update_test.go │ │ ├── capzcrs │ │ ├── create.go │ │ ├── create_test.go │ │ ├── delete.go │ │ ├── error.go │ │ ├── resource.go │ │ └── testdata │ │ │ ├── azurecluster_with_partial_vnet.yaml │ │ │ ├── azurecluster_with_subnets.yaml │ │ │ ├── azurecluster_without_vnet.yaml │ │ │ ├── case-0-Simple-AzureConfig-migration-to-create-CAPI-CAPZ-CRs_AzureCluster.golden │ │ │ ├── case-0-Simple-AzureConfig-migration-to-create-CAPI-CAPZ-CRs_AzureMachine.golden │ │ │ ├── case-0-Simple-AzureConfig-migration-to-create-CAPI-CAPZ-CRs_Cluster.golden │ │ │ ├── case-1-Simple-AzureConfig-reconciliation-to-verify-existing-CAPI-CAPZ-CRs_AzureCluster.golden │ │ │ ├── case-1-Simple-AzureConfig-reconciliation-to-verify-existing-CAPI-CAPZ-CRs_AzureMachine.golden │ │ │ ├── case-1-Simple-AzureConfig-reconciliation-to-verify-existing-CAPI-CAPZ-CRs_Cluster.golden │ │ │ ├── case-2-Modified-AzureConfig-reconciliation-to-update-existing-CAPI-CAPZ-CRs_AzureCluster.golden │ │ │ ├── case-2-Modified-AzureConfig-reconciliation-to-update-existing-CAPI-CAPZ-CRs_AzureMachine.golden │ │ │ ├── case-2-Modified-AzureConfig-reconciliation-to-update-existing-CAPI-CAPZ-CRs_Cluster.golden │ │ │ ├── case-3-Ensure-that-AzureCluster-Subnets-are-not-wiped_AzureCluster.golden │ │ │ ├── case-3-Ensure-that-AzureCluster-Subnets-are-not-wiped_AzureMachine.golden │ │ │ ├── case-3-Ensure-that-AzureCluster-Subnets-are-not-wiped_Cluster.golden │ │ │ ├── case-4-Ensure-that-AzureCluster-VNET-gets-updated_AzureCluster.golden │ │ │ ├── case-4-Ensure-that-AzureCluster-VNET-gets-updated_AzureMachine.golden │ │ │ ├── case-4-Ensure-that-AzureCluster-VNET-gets-updated_Cluster.golden │ │ │ ├── case-5-Ensure-that-AzureCluster-partial-VNET-gets-updated_AzureCluster.golden │ │ │ ├── case-5-Ensure-that-AzureCluster-partial-VNET-gets-updated_AzureMachine.golden │ │ │ ├── case-5-Ensure-that-AzureCluster-partial-VNET-gets-updated_Cluster.golden │ │ │ ├── modified_azureconfig.yaml │ │ │ ├── simple_azurecluster.yaml │ │ │ ├── simple_azureconfig.yaml │ │ │ ├── simple_azuremachine.yaml │ │ │ └── simple_cluster.yaml │ │ ├── clusterid │ │ ├── error.go │ │ └── resource.go │ │ ├── containerurl │ │ ├── create.go │ │ ├── delete.go │ │ ├── error.go │ │ └── resource.go │ │ ├── deployment │ │ ├── checksum.go │ │ ├── deployment.go │ │ ├── error.go │ │ ├── natgw.go │ │ ├── resource.go │ │ ├── serviceendpoints.go │ │ └── template │ │ │ ├── main.json │ │ │ └── template.go │ │ ├── dnsrecord │ │ ├── conv.go │ │ ├── create.go │ │ ├── current.go │ │ ├── delete.go │ │ ├── desired.go │ │ ├── error.go │ │ ├── resource.go │ │ ├── types.go │ │ └── update.go │ │ ├── encryptionkey │ │ ├── create.go │ │ ├── delete.go │ │ ├── error.go │ │ └── resource.go │ │ ├── endpoints │ │ ├── create.go │ │ ├── create_test.go │ │ ├── current.go │ │ ├── delete.go │ │ ├── delete_test.go │ │ ├── desired.go │ │ ├── error.go │ │ ├── nic.go │ │ ├── resource.go │ │ ├── update.go │ │ └── update_test.go │ │ ├── masters │ │ ├── create.go │ │ ├── create_cluster_upgrade_requirement_check.go │ │ ├── create_deployment_completed.go │ │ ├── create_deployment_initialized.go │ │ ├── create_deployment_uninitialized.go │ │ ├── create_empty.go │ │ ├── create_master_instances_upgrading.go │ │ ├── create_provisioning_successful.go │ │ ├── delete.go │ │ ├── deployment.go │ │ ├── error.go │ │ ├── instances.go │ │ ├── resource.go │ │ ├── status.go │ │ └── template │ │ │ ├── main.json │ │ │ └── template.go │ │ ├── namespace │ │ ├── create.go │ │ ├── create_test.go │ │ ├── current.go │ │ ├── current_test.go │ │ ├── delete.go │ │ ├── delete_test.go │ │ ├── desired.go │ │ ├── desired_test.go │ │ ├── error.go │ │ ├── resource.go │ │ └── update.go │ │ ├── resourcegroup │ │ ├── error.go │ │ ├── resource.go │ │ └── spec.go │ │ ├── service │ │ ├── create.go │ │ ├── create_test.go │ │ ├── current.go │ │ ├── delete.go │ │ ├── delete_test.go │ │ ├── desired.go │ │ ├── desired_test.go │ │ ├── error.go │ │ ├── resource.go │ │ ├── resource_test.go │ │ └── update.go │ │ ├── storageclassmigrator │ │ ├── error.go │ │ └── resource.go │ │ ├── vnetpeering │ │ ├── create.go │ │ ├── delete.go │ │ ├── error.go │ │ └── resource.go │ │ └── workermigration │ │ ├── create.go │ │ ├── create_test.go │ │ ├── delete.go │ │ ├── error.go │ │ ├── internal │ │ ├── azure │ │ │ ├── api.go │ │ │ ├── error.go │ │ │ └── spec.go │ │ └── mock_azure │ │ │ └── api.go │ │ ├── resource.go │ │ ├── security_groups.go │ │ └── testdata │ │ ├── azurecluster.yaml │ │ ├── azureconfig.yaml │ │ ├── azuremachinepool.yaml │ │ ├── cluster.yaml │ │ ├── drainerconfigs.yaml │ │ ├── machinepool.yaml │ │ ├── namespace.yaml │ │ └── spark.yaml ├── azuremachine │ ├── controller.go │ ├── error.go │ └── handler │ │ ├── azuremachineconditions │ │ ├── conditionready.go │ │ ├── conditionsubnetready.go │ │ ├── conditionvmssready.go │ │ ├── create.go │ │ ├── delete.go │ │ ├── error.go │ │ └── resource.go │ │ └── azuremachinemetadata │ │ ├── create.go │ │ ├── delete.go │ │ ├── error.go │ │ └── resource.go ├── azuremachinepool │ ├── controller.go │ ├── error.go │ └── handler │ │ ├── azuremachinepoolconditions │ │ ├── conditionready.go │ │ ├── conditionsubnetready.go │ │ ├── conditionvmssready.go │ │ ├── create.go │ │ ├── delete.go │ │ ├── error.go │ │ └── resource.go │ │ ├── cloudconfigblob │ │ ├── create.go │ │ ├── delete.go │ │ ├── error.go │ │ └── resource.go │ │ ├── nodepool │ │ ├── cluster_autoscaler.go │ │ ├── cordon_old_workers.go │ │ ├── create.go │ │ ├── create_deployment_uninitialized.go │ │ ├── create_scale_workers.go │ │ ├── create_terminate_old_workers.go │ │ ├── create_wait_for_nodes_to_become_ready.go │ │ ├── delete.go │ │ ├── deployment.go │ │ ├── drain_old_workers.go │ │ ├── error.go │ │ ├── resource.go │ │ ├── status.go │ │ └── template │ │ │ ├── errors.go │ │ │ ├── main.json │ │ │ ├── parameters.go │ │ │ └── template.go │ │ └── spark │ │ ├── create.go │ │ ├── create_test.go │ │ ├── delete.go │ │ ├── delete_test.go │ │ ├── error.go │ │ ├── resource.go │ │ └── testdata │ │ ├── case-0-Spark_Secret.golden │ │ ├── encryption_key_secret.yaml │ │ ├── simple_azurecluster.yaml │ │ ├── simple_azuremachinepool.yaml │ │ ├── simple_cluster.yaml │ │ ├── simple_machinepool.yaml │ │ ├── simple_release.yaml │ │ └── simple_spark.yaml ├── blobclient │ ├── blob_client.go │ └── error.go ├── cloudconfig │ ├── base_extension.go │ ├── cloud_config.go │ ├── error.go │ ├── interface.go │ ├── master_template.go │ ├── types.go │ └── worker_template.go ├── cluster │ ├── controller.go │ ├── error.go │ └── handler │ │ ├── clusterdependents │ │ ├── error.go │ │ └── resource.go │ │ ├── clusterownerreference │ │ ├── controlplane.go │ │ ├── error.go │ │ ├── resource.go │ │ └── resource_test.go │ │ ├── clusterreleaseversion │ │ ├── checkcreationprogress.go │ │ ├── checkupgradeprogress.go │ │ ├── create.go │ │ ├── delete.go │ │ ├── error.go │ │ └── resource.go │ │ └── clusterupgrade │ │ ├── checks.go │ │ ├── create.go │ │ ├── delete.go │ │ ├── error.go │ │ └── resource.go ├── controllercontext │ ├── context.go │ └── error.go ├── debugger │ ├── debugger.go │ └── error.go ├── encrypter │ ├── encrypter.go │ ├── encrypter_test.go │ ├── error.go │ └── interface.go ├── internal │ ├── vmsku │ │ ├── error.go │ │ └── vmsku.go │ ├── vmsscheck │ │ ├── error.go │ │ └── vmss.go │ └── workerpool │ │ ├── pool.go │ │ ├── pool_test.go │ │ └── spec.go ├── key │ ├── common.go │ ├── error.go │ ├── key.go │ ├── key_test.go │ └── spec.go ├── machinepool │ ├── controller.go │ ├── error.go │ └── handler │ │ ├── machinepooldependents │ │ ├── error.go │ │ └── resource.go │ │ ├── machinepoolownerreference │ │ ├── error.go │ │ ├── resource.go │ │ └── resource_test.go │ │ ├── machinepoolupgrade │ │ ├── error.go │ │ ├── lastdeployedreleaseversion.go │ │ └── resource.go │ │ └── nodestatus │ │ ├── create.go │ │ ├── create_test.go │ │ ├── delete.go │ │ ├── error.go │ │ └── resource.go ├── setting │ └── types.go ├── templates │ ├── ignition │ │ ├── azure-udev-rules.go │ │ ├── azure_cni_nat_rules.go │ │ ├── certificate_decrypter_unit.go │ │ ├── cloud_provider_conf.go │ │ ├── default_storage_class.go │ │ ├── docker_mount_unit.go │ │ ├── etcd_mount_unit.go │ │ ├── kubelet_mount_unit.go │ │ ├── small.go │ │ └── vnic_configuration_unit.go │ ├── templates.go │ └── templates_test.go └── unhealthynode │ ├── controller.go │ ├── error.go │ └── handler │ └── terminateunhealthynode │ ├── create.go │ ├── delete.go │ ├── error.go │ ├── metrics.go │ └── resource.go ├── error.go ├── network ├── network.go ├── network_test.go └── subnets.go ├── service.go └── unittest └── default_k8sclient.go /.github/workflows/zz_generated.gitleaks.yaml: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT. Generated with: 2 | # 3 | # devctl@6.14.0 4 | # 5 | name: gitleaks 6 | 7 | on: [pull_request] 8 | 9 | jobs: 10 | gitleaks: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | fetch-depth: '0' 16 | - name: gitleaks-action 17 | uses: giantswarm/gitleaks-action@main 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | azure-operator 2 | !helm/azure-operator 3 | TODO 4 | vendor 5 | 6 | .e2e-harness 7 | 8 | /examples/local/*.yaml 9 | !/examples/local/*.tmpl.yaml 10 | 11 | integration/test/**/*-e2e 12 | azure-operator-*.tgz 13 | integration/test/**/logs/*.txt 14 | 15 | scripts/*.tar.gz 16 | 17 | telepresence.log 18 | *.flowchart.generated.html 19 | 20 | # binaries 21 | /azure-operator-v*-*-linux 22 | /azure-operator-v*-*-linux-* 23 | /azure-operator-v*-*-darwin 24 | /azure-operator-v*-*-darwin-* 25 | -------------------------------------------------------------------------------- /.nancy-ignore: -------------------------------------------------------------------------------- 1 | CVE-2022-29153 2 | CVE-2022-24687 3 | CVE-2021-23772 4 | sonatype-2021-1485 5 | 6 | # pkg:golang/github.com/labstack/echo/v4@v4.5.0 7 | sonatype-2022-5436 8 | 9 | CVE-2021-41803 10 | CVE-2022-23471 11 | CVE-2022-42709 12 | CVE-2022-42708 13 | CVE-2022-32149 14 | sonatype-2022-6522 15 | CVE-2023-25173 16 | CVE-2023-25153 17 | # pkg:golang/k8s.io/apiserver@v0.22.5 18 | CVE-2020-8561 19 | 20 | # pkg:golang/github.com/opencontainers/runc@v1.1.3 21 | CVE-2023-28642 22 | CVE-2023-27561 23 | CVE-2023-25809 24 | CVE-2023-29401 25 | 26 | # pkg:golang/google.golang.org/grpc@v1.50.1 27 | CVE-2023-32731 28 | 29 | #pkg:golang/golang.org/x/net@v0.9.0 30 | CVE-2023-3978 until=2023-11-30 31 | -------------------------------------------------------------------------------- /.nancy-ignore.generated: -------------------------------------------------------------------------------- 1 | # This file is generated by https://github.com/giantswarm/github 2 | # Repository specific ignores should be added to .nancy-ignore 3 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | minimum_pre_commit_version: '2.17' 2 | repos: 3 | # shell scripts 4 | - repo: https://github.com/detailyang/pre-commit-shell 5 | rev: 1.0.5 6 | hooks: 7 | - id: shell-lint 8 | args: [ --format=json ] 9 | 10 | - repo: https://github.com/pre-commit/pre-commit-hooks 11 | rev: v4.5.0 12 | hooks: 13 | - id: check-added-large-files 14 | # check for unresolved merge conflicts 15 | - id: check-merge-conflict 16 | - id: check-shebang-scripts-are-executable 17 | - id: detect-private-key 18 | - id: end-of-file-fixer 19 | - id: mixed-line-ending 20 | - id: trailing-whitespace 21 | 22 | - repo: https://github.com/dnephin/pre-commit-golang 23 | rev: v0.5.1 24 | hooks: 25 | - id: go-fmt 26 | - id: go-mod-tidy 27 | - id: golangci-lint 28 | # timeout is needed for CI 29 | args: [ -E, gosec, -E, goconst, -E, govet, --timeout, 300s ] 30 | - id: go-imports 31 | args: [ -local, github.com/giantswarm/azure-operator ] 32 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # generated by giantswarm/github actions - changes will be overwritten 2 | * @giantswarm/team-phoenix 3 | -------------------------------------------------------------------------------- /DCO: -------------------------------------------------------------------------------- 1 | Developer Certificate of Origin 2 | Version 1.1 3 | 4 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 5 | 660 York Street, Suite 102, 6 | San Francisco, CA 94110 USA 7 | 8 | Everyone is permitted to copy and distribute verbatim copies of this 9 | license document, but changing it is not allowed. 10 | 11 | 12 | Developer's Certificate of Origin 1.1 13 | 14 | By making a contribution to this project, I certify that: 15 | 16 | (a) The contribution was created in whole or in part by me and I 17 | have the right to submit it under the open source license 18 | indicated in the file; or 19 | 20 | (b) The contribution is based upon previous work that, to the best 21 | of my knowledge, is covered under an appropriate open source 22 | license and I have the right under that license to submit that 23 | work with modifications, whether created in whole or in part 24 | by me, under the same open source license (unless I am 25 | permitted to submit under a different license), as indicated 26 | in the file; or 27 | 28 | (c) The contribution was provided directly to me by some other 29 | person who certified (a), (b) or (c) and I have not modified 30 | it. 31 | 32 | (d) I understand and agree that this project and the contribution 33 | are public and that a record of the contribution (including all 34 | personal information I submit with it, including my sign-off) is 35 | maintained indefinitely and may be redistributed consistent with 36 | this project or the open source license(s) involved. 37 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM quay.io/giantswarm/golang:1.20.3 AS builder 2 | ENV GO111MODULE=on 3 | COPY go.mod /etc/go.mod 4 | RUN git clone --depth 1 --branch $(cat /etc/go.mod | grep k8scloudconfig | awk '{print $2}') https://github.com/giantswarm/k8scloudconfig.git && cp -r k8scloudconfig /opt/k8scloudconfig 5 | 6 | FROM alpine:3.17.3 7 | 8 | RUN apk add --update ca-certificates \ 9 | && rm -rf /var/cache/apk/* 10 | 11 | RUN mkdir -p /opt/ignition 12 | COPY --from=builder /opt/k8scloudconfig /opt/ignition 13 | 14 | ADD ./azure-operator /azure-operator 15 | 16 | ENTRYPOINT ["/azure-operator"] 17 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT. Generated with: 2 | # 3 | # devctl@6.14.0 4 | # 5 | 6 | include Makefile.*.mk 7 | 8 | ##@ General 9 | 10 | # The help target prints out all targets with their descriptions organized 11 | # beneath their categories. The categories are represented by '##@' and the 12 | # target descriptions by '##'. The awk commands is responsible for reading the 13 | # entire set of makefiles included in this invocation, looking for lines of the 14 | # file as xyz: ## something, and then pretty-format the target and help. Then, 15 | # if there's a line with ##@ something, that gets pretty-printed as a category. 16 | # More info on the usage of ANSI control characters for terminal formatting: 17 | # https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters 18 | # More info on the awk command: 19 | # http://linuxcommand.org/lc3_adv_awk.php 20 | 21 | .PHONY: help 22 | help: ## Display this help. 23 | @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z%\\\/_0-9-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) 24 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | Please visit https://www.giantswarm.io/responsible-disclosure for information on reporting security issues. 6 | -------------------------------------------------------------------------------- /client/error.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | 16 | var credentialsNotFoundError = µerror.Error{ 17 | Kind: "credentialsNotFoundError", 18 | } 19 | 20 | // IsCredentialsNotFoundError asserts credentialsNotFoundError. 21 | func IsCredentialsNotFoundError(err error) bool { 22 | return microerror.Cause(err) == credentialsNotFoundError 23 | } 24 | 25 | var tooManyCredentialsError = µerror.Error{ 26 | Kind: "tooManyCredentialsError", 27 | } 28 | 29 | // IsTooManyCredentials asserts tooManyCredentialsError. 30 | func IsTooManyCredentials(err error) bool { 31 | return microerror.Cause(err) == tooManyCredentialsError 32 | } 33 | -------------------------------------------------------------------------------- /client/response.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/Azure/go-autorest/autorest" 7 | ) 8 | 9 | // ResponseWasNotFound returns true if the response code from the Azure API 10 | // was a 404. 11 | func ResponseWasNotFound(resp autorest.Response) bool { 12 | if resp.Response != nil && resp.StatusCode == http.StatusNotFound { 13 | return true 14 | } 15 | 16 | return false 17 | } 18 | -------------------------------------------------------------------------------- /client/response_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "net/http" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/Azure/go-autorest/autorest" 9 | "github.com/google/go-cmp/cmp" 10 | ) 11 | 12 | func Test_ResponseWasNotFound(t *testing.T) { 13 | testCases := []struct { 14 | name string 15 | response *http.Response 16 | expectedResult bool 17 | }{ 18 | { 19 | name: "case 0: Azure API returns 404", 20 | response: &http.Response{ 21 | StatusCode: http.StatusNotFound, 22 | }, 23 | expectedResult: true, 24 | }, 25 | { 26 | name: "case 1: Azure API responds normally", 27 | response: &http.Response{ 28 | StatusCode: http.StatusOK, 29 | }, 30 | expectedResult: false, 31 | }, 32 | { 33 | name: "case 2: Internal server error", 34 | response: &http.Response{ 35 | StatusCode: http.StatusInternalServerError, 36 | }, 37 | expectedResult: false, 38 | }, 39 | { 40 | name: "case 3: Reponse is nil (response does not exist)", 41 | response: nil, 42 | expectedResult: false, 43 | }, 44 | } 45 | 46 | for i, tc := range testCases { 47 | t.Run(strconv.Itoa(i), func(t *testing.T) { 48 | resp := autorest.Response{ 49 | Response: tc.response, 50 | } 51 | notFoundResult := ResponseWasNotFound(resp) 52 | if !cmp.Equal(notFoundResult, tc.expectedResult) { 53 | t.Fatalf("\n\n%s\n", cmp.Diff(tc.expectedResult, notFoundResult)) 54 | 55 | } 56 | }) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /client/senddecorator/configurator.go: -------------------------------------------------------------------------------- 1 | package senddecorator 2 | 3 | import ( 4 | "github.com/Azure/go-autorest/autorest" 5 | ) 6 | 7 | // WrapClient accepts variable number of SendDecorators that wrap the given 8 | // autorest Client. 9 | // 10 | // Existing SendDecorators are preserved, but moved to end of slice. 11 | func WrapClient(c *autorest.Client, decorators ...autorest.SendDecorator) { 12 | // NOTE: Order matters here since these decorators are executed in 13 | // order. See: https://godoc.org/github.com/Azure/go-autorest/autorest#Client 14 | c.SendDecorators = append(decorators, c.SendDecorators...) 15 | } 16 | -------------------------------------------------------------------------------- /client/senddecorator/error.go: -------------------------------------------------------------------------------- 1 | package senddecorator 2 | 3 | import "github.com/giantswarm/microerror" 4 | 5 | var tooManyRequestsError = µerror.Error{ 6 | Kind: "tooManyRequestsError", 7 | } 8 | 9 | // IsTooManyRequests asserts tooManyRequestsError. 10 | func IsTooManyRequests(err error) bool { 11 | return microerror.Cause(err) == tooManyRequestsError 12 | } 13 | -------------------------------------------------------------------------------- /client/senddecorator/ratelimit_circuitbreaker_test.go: -------------------------------------------------------------------------------- 1 | package senddecorator 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | 7 | "github.com/google/go-cmp/cmp" 8 | ) 9 | 10 | func Test_removeElementFromSlice(t *testing.T) { 11 | testCases := []struct { 12 | name string 13 | xs []int 14 | x int 15 | expectedXs []int 16 | }{ 17 | { 18 | 19 | name: "case 0: simple case, remove number from slice", 20 | xs: []int{0, 1, 2, 3}, 21 | x: 2, 22 | expectedXs: []int{0, 1, 3}, 23 | }, 24 | { 25 | 26 | name: "case 1: remove number from empty slice", 27 | xs: []int{}, 28 | x: 2, 29 | expectedXs: []int{}, 30 | }, 31 | { 32 | name: "case 2: remove number from nil slice", 33 | xs: nil, 34 | x: 2, 35 | expectedXs: nil, 36 | }, 37 | { 38 | name: "case 3: remove first number from slice", 39 | xs: []int{0, 1, 2, 3, 4}, 40 | x: 0, 41 | expectedXs: []int{1, 2, 3, 4}, 42 | }, 43 | { 44 | name: "case 4: remove last number from slice", 45 | xs: []int{0, 1, 2, 3, 4}, 46 | x: 4, 47 | expectedXs: []int{0, 1, 2, 3}, 48 | }, 49 | } 50 | 51 | for i, tc := range testCases { 52 | t.Run(strconv.Itoa(i), func(t *testing.T) { 53 | t.Log(tc.name) 54 | 55 | xs := removeElementFromSlice(tc.xs, tc.x) 56 | 57 | if !cmp.Equal(xs, tc.expectedXs) { 58 | t.Fatalf("\n\n%s\n", cmp.Diff(tc.expectedXs, xs)) 59 | } 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /docs/azure-node-pools.md: -------------------------------------------------------------------------------- 1 | ## Azure Node Pools CRD Structure 2 | 3 | ![Azure Node Pools CRD Structure](azure-node-pools-crds.svg) 4 | 5 | ## Cluster upgrade process to node pools cluster in Azure 6 | 7 | Following diagram describes the upgrade process in high level. 8 | 9 | ![Node Pools Upgrade State Diagram](cluster-upgrade-to-nodepools.svg) 10 | -------------------------------------------------------------------------------- /docs/circleci_environment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giantswarm/azure-operator/8a49ffc9e4545c423b2de34d592116229b171f36/docs/circleci_environment.png -------------------------------------------------------------------------------- /docs/circleci_options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giantswarm/azure-operator/8a49ffc9e4545c423b2de34d592116229b171f36/docs/circleci_options.png -------------------------------------------------------------------------------- /docs/delegatedResourceManagement.parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "mspOfferName": { 6 | "value": "Giant Swarm" 7 | }, 8 | "mspOfferDescription": { 9 | "value": "Giant Swarm - Beyond Managed Kubernetes" 10 | }, 11 | "managedByTenantId": { 12 | "value": "" 13 | }, 14 | "authorizations": { 15 | "value": [ 16 | { 17 | "principalId": "", 18 | "principalIdDisplayName": "Giant Swarm Support", 19 | "roleDefinitionId": "b24988ac-6180-42a0-ab88-20f7382dd24c" 20 | } 21 | ] 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docs/kubectl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giantswarm/azure-operator/8a49ffc9e4545c423b2de34d592116229b171f36/docs/kubectl.png -------------------------------------------------------------------------------- /docs/rerun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giantswarm/azure-operator/8a49ffc9e4545c423b2de34d592116229b171f36/docs/rerun.png -------------------------------------------------------------------------------- /docs/resourcegroupname.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giantswarm/azure-operator/8a49ffc9e4545c423b2de34d592116229b171f36/docs/resourcegroupname.png -------------------------------------------------------------------------------- /docs/ssh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giantswarm/azure-operator/8a49ffc9e4545c423b2de34d592116229b171f36/docs/ssh.png -------------------------------------------------------------------------------- /flag/flag.go: -------------------------------------------------------------------------------- 1 | package flag 2 | 3 | import ( 4 | "github.com/giantswarm/microkit/flag" 5 | 6 | "github.com/giantswarm/azure-operator/v8/flag/service" 7 | ) 8 | 9 | type Flag struct { 10 | Service service.Service 11 | } 12 | 13 | func New() *Flag { 14 | f := &Flag{} 15 | flag.Init(f) 16 | return f 17 | } 18 | -------------------------------------------------------------------------------- /flag/service/azure/azure.go: -------------------------------------------------------------------------------- 1 | package azure 2 | 3 | import ( 4 | "github.com/giantswarm/azure-operator/v8/flag/service/azure/hostcluster" 5 | "github.com/giantswarm/azure-operator/v8/flag/service/azure/msi" 6 | "github.com/giantswarm/azure-operator/v8/flag/service/azure/template" 7 | ) 8 | 9 | type Azure struct { 10 | ClientID string 11 | ClientSecret string 12 | EnvironmentName string 13 | HostCluster hostcluster.HostCluster 14 | MSI msi.MSI 15 | Location string 16 | PartnerID string 17 | SubscriptionID string 18 | TenantID string 19 | Template template.Template 20 | } 21 | -------------------------------------------------------------------------------- /flag/service/azure/hostcluster/host_cluster.go: -------------------------------------------------------------------------------- 1 | package hostcluster 2 | 3 | import ( 4 | "github.com/giantswarm/azure-operator/v8/flag/service/azure/hostcluster/tenant" 5 | ) 6 | 7 | type HostCluster struct { 8 | CIDR string 9 | ResourceGroup string 10 | Tenant tenant.Tenant 11 | VirtualNetwork string 12 | VirtualNetworkGateway string 13 | } 14 | -------------------------------------------------------------------------------- /flag/service/azure/hostcluster/tenant/tenant.go: -------------------------------------------------------------------------------- 1 | package tenant 2 | 3 | type Tenant struct { 4 | TenantID string 5 | SubscriptionID string 6 | PartnerID string 7 | } 8 | -------------------------------------------------------------------------------- /flag/service/azure/msi/msi.go: -------------------------------------------------------------------------------- 1 | package msi 2 | 3 | type MSI struct { 4 | Enabled string 5 | } 6 | -------------------------------------------------------------------------------- /flag/service/azure/template/template.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "github.com/giantswarm/azure-operator/v8/flag/service/azure/template/uri" 5 | ) 6 | 7 | type Template struct { 8 | URI uri.URI 9 | } 10 | -------------------------------------------------------------------------------- /flag/service/azure/template/uri/uri.go: -------------------------------------------------------------------------------- 1 | package uri 2 | 3 | type URI struct { 4 | Version string 5 | } 6 | -------------------------------------------------------------------------------- /flag/service/cluster/calico/calico.go: -------------------------------------------------------------------------------- 1 | package calico 2 | 3 | type Calico struct { 4 | CIDR string 5 | MTU string 6 | Subnet string 7 | } 8 | -------------------------------------------------------------------------------- /flag/service/cluster/cluster.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "github.com/giantswarm/azure-operator/v8/flag/service/cluster/calico" 5 | "github.com/giantswarm/azure-operator/v8/flag/service/cluster/docker" 6 | "github.com/giantswarm/azure-operator/v8/flag/service/cluster/etcd" 7 | "github.com/giantswarm/azure-operator/v8/flag/service/cluster/kubernetes" 8 | ) 9 | 10 | type Cluster struct { 11 | BaseDomain string 12 | Calico calico.Calico 13 | Docker docker.Docker 14 | Etcd etcd.Etcd 15 | Kubernetes kubernetes.Kubernetes 16 | } 17 | -------------------------------------------------------------------------------- /flag/service/cluster/docker/daemon/daemon.go: -------------------------------------------------------------------------------- 1 | package daemon 2 | 3 | type Daemon struct { 4 | CIDR string 5 | ExtraArgs string 6 | } 7 | -------------------------------------------------------------------------------- /flag/service/cluster/docker/docker.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "github.com/giantswarm/azure-operator/v8/flag/service/cluster/docker/daemon" 5 | ) 6 | 7 | type Docker struct { 8 | Daemon daemon.Daemon 9 | } 10 | -------------------------------------------------------------------------------- /flag/service/cluster/etcd/etcd.go: -------------------------------------------------------------------------------- 1 | package etcd 2 | 3 | type Etcd struct { 4 | AltNames string 5 | Port string 6 | Prefix string 7 | } 8 | -------------------------------------------------------------------------------- /flag/service/cluster/kubernetes/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | type API struct { 4 | AltNames string 5 | ClusterIPRange string 6 | Domain string 7 | SecurePort string 8 | } 9 | -------------------------------------------------------------------------------- /flag/service/cluster/kubernetes/ingress/ingress.go: -------------------------------------------------------------------------------- 1 | package ingress 2 | 3 | type IngressController struct { 4 | BaseDomain string 5 | InsecurePort string 6 | SecurePort string 7 | } 8 | -------------------------------------------------------------------------------- /flag/service/cluster/kubernetes/kubelet/kubelet.go: -------------------------------------------------------------------------------- 1 | package kubelet 2 | 3 | type Kubelet struct { 4 | AltNames string 5 | Port string 6 | } 7 | -------------------------------------------------------------------------------- /flag/service/cluster/kubernetes/kubernetes.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | import ( 4 | "github.com/giantswarm/azure-operator/v8/flag/service/cluster/kubernetes/api" 5 | "github.com/giantswarm/azure-operator/v8/flag/service/cluster/kubernetes/ingress" 6 | "github.com/giantswarm/azure-operator/v8/flag/service/cluster/kubernetes/kubelet" 7 | "github.com/giantswarm/azure-operator/v8/flag/service/cluster/kubernetes/ssh" 8 | ) 9 | 10 | type Kubernetes struct { 11 | API api.API 12 | Domain string 13 | IngressController ingress.IngressController 14 | Kubelet kubelet.Kubelet 15 | SSH ssh.SSH 16 | } 17 | -------------------------------------------------------------------------------- /flag/service/cluster/kubernetes/ssh/ssh.go: -------------------------------------------------------------------------------- 1 | package ssh 2 | 3 | type SSH struct { 4 | UserList string 5 | } 6 | -------------------------------------------------------------------------------- /flag/service/debug/debug.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | type Debug struct { 4 | InsecureStorageAccount string 5 | } 6 | -------------------------------------------------------------------------------- /flag/service/installation/guest/guest.go: -------------------------------------------------------------------------------- 1 | package guest 2 | 3 | import ( 4 | "github.com/giantswarm/azure-operator/v8/flag/service/installation/guest/ipam" 5 | ) 6 | 7 | type Guest struct { 8 | IPAM ipam.IPAM 9 | } 10 | -------------------------------------------------------------------------------- /flag/service/installation/guest/ipam/ipam.go: -------------------------------------------------------------------------------- 1 | package ipam 2 | 3 | import "github.com/giantswarm/azure-operator/v8/flag/service/installation/guest/ipam/network" 4 | 5 | type IPAM struct { 6 | Network network.Network 7 | } 8 | -------------------------------------------------------------------------------- /flag/service/installation/guest/ipam/network/network.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | type Network struct { 4 | // CIDR is network segment from which IPAM allocates subnets for guest 5 | // clusters. 6 | CIDR string 7 | 8 | // SubnetMaskBits is number of bits in guest cluster subnet mask. This 9 | // defines size of the guest cluster subnet that is allocated from CIDR. 10 | SubnetMaskBits string 11 | } 12 | -------------------------------------------------------------------------------- /flag/service/installation/installation.go: -------------------------------------------------------------------------------- 1 | package installation 2 | 3 | import ( 4 | "github.com/giantswarm/azure-operator/v8/flag/service/installation/guest" 5 | "github.com/giantswarm/azure-operator/v8/flag/service/installation/tenant" 6 | ) 7 | 8 | type Installation struct { 9 | Name string 10 | Guest guest.Guest 11 | Tenant tenant.Tenant 12 | } 13 | -------------------------------------------------------------------------------- /flag/service/installation/tenant/kubernetes/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/giantswarm/azure-operator/v8/flag/service/installation/tenant/kubernetes/api/auth" 5 | ) 6 | 7 | type API struct { 8 | Auth auth.Auth 9 | } 10 | -------------------------------------------------------------------------------- /flag/service/installation/tenant/kubernetes/api/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "github.com/giantswarm/azure-operator/v8/flag/service/installation/tenant/kubernetes/api/auth/provider" 5 | ) 6 | 7 | type Auth struct { 8 | Provider provider.Provider 9 | } 10 | -------------------------------------------------------------------------------- /flag/service/installation/tenant/kubernetes/api/auth/provider/oidc/oidc.go: -------------------------------------------------------------------------------- 1 | package oidc 2 | 3 | type OIDC struct { 4 | ClientID string 5 | IssuerURL string 6 | UsernameClaim string 7 | GroupsClaim string 8 | } 9 | -------------------------------------------------------------------------------- /flag/service/installation/tenant/kubernetes/api/auth/provider/provider.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "github.com/giantswarm/azure-operator/v8/flag/service/installation/tenant/kubernetes/api/auth/provider/oidc" 5 | ) 6 | 7 | type Provider struct { 8 | OIDC oidc.OIDC 9 | } 10 | -------------------------------------------------------------------------------- /flag/service/installation/tenant/kubernetes/kubernetes.go: -------------------------------------------------------------------------------- 1 | package kubernetes 2 | 3 | import ( 4 | "github.com/giantswarm/azure-operator/v8/flag/service/installation/tenant/kubernetes/api" 5 | ) 6 | 7 | type Kubernetes struct { 8 | API api.API 9 | } 10 | -------------------------------------------------------------------------------- /flag/service/installation/tenant/tenant.go: -------------------------------------------------------------------------------- 1 | package tenant 2 | 3 | import ( 4 | "github.com/giantswarm/azure-operator/v8/flag/service/installation/tenant/kubernetes" 5 | ) 6 | 7 | type Tenant struct { 8 | Kubernetes kubernetes.Kubernetes 9 | } 10 | -------------------------------------------------------------------------------- /flag/service/registry/registry.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | type Registry struct { 4 | Domain string 5 | Mirrors string 6 | 7 | // Registry tokens 8 | DockerhubToken string 9 | } 10 | -------------------------------------------------------------------------------- /flag/service/sentry/sentry.go: -------------------------------------------------------------------------------- 1 | package sentry 2 | 3 | type Sentry struct { 4 | DSN string 5 | } 6 | -------------------------------------------------------------------------------- /flag/service/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/giantswarm/operatorkit/v7/pkg/flag/service/kubernetes" 5 | 6 | "github.com/giantswarm/azure-operator/v8/flag/service/azure" 7 | "github.com/giantswarm/azure-operator/v8/flag/service/cluster" 8 | "github.com/giantswarm/azure-operator/v8/flag/service/debug" 9 | "github.com/giantswarm/azure-operator/v8/flag/service/installation" 10 | "github.com/giantswarm/azure-operator/v8/flag/service/registry" 11 | "github.com/giantswarm/azure-operator/v8/flag/service/sentry" 12 | "github.com/giantswarm/azure-operator/v8/flag/service/tenant" 13 | ) 14 | 15 | type Service struct { 16 | Azure azure.Azure 17 | Cluster cluster.Cluster 18 | Installation installation.Installation 19 | Kubernetes kubernetes.Kubernetes 20 | Registry registry.Registry 21 | Tenant tenant.Tenant 22 | Sentry sentry.Sentry 23 | Debug debug.Debug 24 | } 25 | -------------------------------------------------------------------------------- /flag/service/tenant/ignition/ignition.go: -------------------------------------------------------------------------------- 1 | package ignition 2 | 3 | type Ignition struct { 4 | Debug Debug 5 | Path string 6 | } 7 | 8 | type Debug struct { 9 | Enabled string 10 | LogsPrefix string 11 | LogsToken string 12 | } 13 | -------------------------------------------------------------------------------- /flag/service/tenant/ssh/ssh.go: -------------------------------------------------------------------------------- 1 | package ssh 2 | 3 | type SSH struct { 4 | SSOPublicKey string 5 | } 6 | -------------------------------------------------------------------------------- /flag/service/tenant/tenant.go: -------------------------------------------------------------------------------- 1 | package tenant 2 | 3 | import ( 4 | "github.com/giantswarm/azure-operator/v8/flag/service/tenant/ignition" 5 | "github.com/giantswarm/azure-operator/v8/flag/service/tenant/ssh" 6 | ) 7 | 8 | type Tenant struct { 9 | Ignition ignition.Ignition 10 | SSH ssh.SSH 11 | } 12 | -------------------------------------------------------------------------------- /helm/azure-operator/Chart.yaml: -------------------------------------------------------------------------------- 1 | name: azure-operator 2 | version: [[ .Version ]] 3 | description: "Azure operator to create k8s clusters on Azure" 4 | appVersion: "[[ .AppVersion ]]" 5 | home: "https://github.com/giantswarm/azure-operator" 6 | apiVersion: v1 7 | annotations: 8 | application.giantswarm.io/team: "phoenix" 9 | config.giantswarm.io/version: 1.x.x 10 | -------------------------------------------------------------------------------- /helm/azure-operator/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "name" -}} 6 | {{- .Chart.Name | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create chart name and version as used by the chart label. 11 | */}} 12 | {{- define "chart" -}} 13 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 14 | {{- end -}} 15 | 16 | {{/* 17 | Common labels 18 | */}} 19 | {{- define "labels.common" -}} 20 | app: {{ include "name" . | quote }} 21 | {{ include "labels.selector" . }} 22 | app.giantswarm.io/branch: {{ .Values.project.branch | quote }} 23 | app.giantswarm.io/commit: {{ .Values.project.commit | quote }} 24 | app.kubernetes.io/managed-by: {{ .Release.Service | quote }} 25 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 26 | helm.sh/chart: {{ include "chart" . | quote }} 27 | {{- end -}} 28 | 29 | {{/* 30 | Selector labels 31 | */}} 32 | {{- define "labels.selector" -}} 33 | app.kubernetes.io/name: {{ include "name" . | quote }} 34 | app.kubernetes.io/instance: {{ .Release.Name | quote }} 35 | {{- end -}} 36 | -------------------------------------------------------------------------------- /helm/azure-operator/templates/_resource.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Create a name stem for resource names 4 | 5 | When pods for deployments are created they have an additional 16 character 6 | suffix appended, e.g. "-957c9d6ff-pkzgw". Given that Kubernetes allows 63 7 | characters for resource names, the stem is truncated to 47 characters to leave 8 | room for such suffix. 9 | */}} 10 | {{- define "resource.default.name" -}} 11 | {{- .Release.Name | replace "." "-" | trunc 47 | trimSuffix "-" -}} 12 | {{- end -}} 13 | 14 | {{- define "resource.networkPolicy.name" -}} 15 | {{- include "resource.default.name" . -}}-network-policy 16 | {{- end -}} 17 | 18 | {{- define "resource.psp.name" -}} 19 | {{- include "resource.default.name" . -}}-psp 20 | {{- end -}} 21 | 22 | {{- define "resource.default.namespace" -}} 23 | giantswarm 24 | {{- end -}} 25 | -------------------------------------------------------------------------------- /helm/azure-operator/templates/network-policy.yaml: -------------------------------------------------------------------------------- 1 | kind: NetworkPolicy 2 | apiVersion: networking.k8s.io/v1 3 | metadata: 4 | name: {{ include "resource.networkPolicy.name" . }} 5 | namespace: {{ include "resource.default.namespace" . }} 6 | labels: 7 | {{- include "labels.common" . | nindent 4 }} 8 | spec: 9 | podSelector: 10 | matchLabels: 11 | {{- include "labels.selector" . | nindent 6 }} 12 | {{- if .Values.ports.ingress }} 13 | ingress: 14 | - ports: 15 | {{- range .Values.ports.ingress }} 16 | - port: {{ .port }} 17 | protocol: {{ .protocol }} 18 | {{- end }} 19 | {{- else }} 20 | ingress: [] 21 | {{- end }} 22 | egress: 23 | - {} 24 | policyTypes: 25 | - Egress 26 | - Ingress 27 | -------------------------------------------------------------------------------- /helm/azure-operator/templates/psp.yaml: -------------------------------------------------------------------------------- 1 | {{- if not .Values.global.podSecurityStandards.enforced }} 2 | apiVersion: policy/v1beta1 3 | kind: PodSecurityPolicy 4 | metadata: 5 | name: {{ include "resource.psp.name" . }} 6 | labels: 7 | {{- include "labels.common" . | nindent 4 }} 8 | annotations: 9 | seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'runtime/default' 10 | spec: 11 | privileged: false 12 | fsGroup: 13 | rule: MustRunAs 14 | ranges: 15 | - min: 1 16 | max: 65535 17 | runAsUser: 18 | rule: MustRunAsNonRoot 19 | runAsGroup: 20 | rule: MustRunAs 21 | ranges: 22 | - min: 1 23 | max: 65535 24 | seLinux: 25 | rule: RunAsAny 26 | supplementalGroups: 27 | rule: RunAsAny 28 | volumes: 29 | - 'secret' 30 | - 'configMap' 31 | - 'hostPath' 32 | allowPrivilegeEscalation: false 33 | hostNetwork: false 34 | hostIPC: false 35 | hostPID: false 36 | {{- end }} 37 | -------------------------------------------------------------------------------- /helm/azure-operator/templates/pss-exceptions.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: kyverno.io/v2alpha1 3 | kind: PolicyException 4 | metadata: 5 | name: {{ include "resource.default.name" . }}-exceptions 6 | namespace: {{ include "resource.default.namespace" . }} 7 | spec: 8 | exceptions: 9 | - policyName: disallow-host-path 10 | ruleNames: 11 | - host-path 12 | - autogen-host-path 13 | - policyName: restrict-volume-types 14 | ruleNames: 15 | - restricted-volumes 16 | - autogen-restricted-volumes 17 | match: 18 | any: 19 | - resources: 20 | kinds: 21 | - Deployment 22 | - ReplicaSet 23 | - Pod 24 | namespaces: 25 | - {{ include "resource.default.namespace" . }} 26 | names: 27 | - {{ .Chart.Name }}* 28 | -------------------------------------------------------------------------------- /helm/azure-operator/templates/secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | type: Opaque 4 | metadata: 5 | name: {{ include "resource.default.name" . }} 6 | namespace: {{ include "resource.default.namespace" . }} 7 | labels: 8 | {{- include "labels.common" . | nindent 4 }} 9 | stringData: 10 | azure-secret.yaml: | 11 | service: 12 | azure: 13 | clientid: {{ .Values.azureOperatorSecret.service.azure.clientid | quote }} 14 | clientsecret: {{ .Values.azureOperatorSecret.service.azure.clientsecret | quote }} 15 | sptenantid: {{ .Values.azureOperatorSecret.service.azure.sptenantid | quote }} 16 | subscriptionid: {{ .Values.azureOperatorSecret.service.azure.subscriptionid | quote }} 17 | tenantid: {{ .Values.azureOperatorSecret.service.azure.tenantid | quote }} 18 | dockerhub-secret.yaml: | 19 | service: 20 | registry: 21 | dockerhubToken: {{ .Values.registry.dockerhub.token | quote }} 22 | -------------------------------------------------------------------------------- /helm/azure-operator/templates/service-account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: {{ include "resource.default.name" . }} 5 | namespace: {{ include "resource.default.namespace" . }} 6 | labels: 7 | {{- include "labels.common" . | nindent 4 }} 8 | -------------------------------------------------------------------------------- /helm/azure-operator/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "resource.default.name" . }} 5 | namespace: {{ include "resource.default.namespace" . }} 6 | labels: 7 | {{- include "labels.common" . | nindent 4 }} 8 | giantswarm.io/monitoring: "true" 9 | annotations: 10 | prometheus.io/scrape: "true" 11 | spec: 12 | type: NodePort 13 | ports: 14 | - port: 8000 15 | selector: 16 | {{- include "labels.selector" . | nindent 4 }} 17 | -------------------------------------------------------------------------------- /helm/azure-operator/templates/vpa.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Capabilities.APIVersions.Has "autoscaling.k8s.io/v1" }} 2 | {{ if .Values.verticalPodAutoscaler.enabled }} 3 | apiVersion: autoscaling.k8s.io/v1 4 | kind: VerticalPodAutoscaler 5 | metadata: 6 | name: {{ include "resource.default.name" . }} 7 | namespace: {{ include "resource.default.namespace" . }} 8 | labels: 9 | {{- include "labels.common" . | nindent 4 }} 10 | spec: 11 | resourcePolicy: 12 | containerPolicies: 13 | - containerName: {{ .Chart.Name }} 14 | controlledValues: RequestsAndLimits 15 | mode: Auto 16 | targetRef: 17 | apiVersion: apps/v1 18 | kind: Deployment 19 | name: {{ include "resource.default.name" . }} 20 | updatePolicy: 21 | updateMode: Auto 22 | {{ end }} 23 | {{ end }} 24 | -------------------------------------------------------------------------------- /integration/test/crmapping/error.go: -------------------------------------------------------------------------------- 1 | package crmapping 2 | 3 | import "github.com/giantswarm/microerror" 4 | 5 | var unknownKindError = µerror.Error{ 6 | Kind: "unknownKindError", 7 | } 8 | 9 | // IsUnknownKindError asserts unknownKindError. 10 | func IsUnknownKindError(err error) bool { 11 | return microerror.Cause(err) == unknownKindError 12 | } 13 | -------------------------------------------------------------------------------- /integration/test/crmapping/testdata/case-0-create-AzureConfig_AzureCluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureCluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: "" 6 | creationTimestamp: null 7 | labels: 8 | azure-operator.giantswarm.io/version: 4.2.0 9 | cluster.x-k8s.io/cluster-name: c6fme 10 | giantswarm.io/cluster: c6fme 11 | giantswarm.io/organization: "" 12 | release.giantswarm.io/version: 12.0.0 13 | name: c6fme 14 | namespace: default 15 | resourceVersion: "2" 16 | spec: 17 | controlPlaneEndpoint: 18 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 19 | port: 443 20 | location: westeurope 21 | networkSpec: 22 | vnet: 23 | name: c6fme-VirtualNetwork 24 | resourceGroup: c6fme 25 | resourceGroup: c6fme 26 | status: 27 | ready: false 28 | -------------------------------------------------------------------------------- /integration/test/crmapping/testdata/case-0-create-AzureConfig_AzureMachine.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureMachine 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | azure-operator.giantswarm.io/version: 4.2.0 7 | cluster.x-k8s.io/cluster-name: c6fme 8 | cluster.x-k8s.io/control-plane: "true" 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: "" 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme-master-0 13 | namespace: default 14 | resourceVersion: "2" 15 | spec: 16 | failureDomain: "1" 17 | image: 18 | marketplace: 19 | offer: flatcar-container-linux-free 20 | publisher: kinvolk 21 | sku: stable 22 | version: 2345.3.1 23 | osDisk: 24 | diskSizeGB: 50 25 | managedDisk: 26 | storageAccountType: Premium_LRS 27 | osType: Linux 28 | sshPublicKey: ssh-rsa ASDFASADFASDFSDAFSADFSDF== john 29 | vmSize: Standard_D4s_v3 30 | status: 31 | ready: false 32 | -------------------------------------------------------------------------------- /integration/test/crmapping/testdata/case-0-create-AzureConfig_Cluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: cluster.x-k8s.io/v1beta1 2 | kind: Cluster 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | azure-operator.giantswarm.io/version: 4.2.0 7 | cluster-operator.giantswarm.io/version: "" 8 | cluster.x-k8s.io/cluster-name: c6fme 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: "" 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme 13 | namespace: default 14 | resourceVersion: "2" 15 | spec: 16 | clusterNetwork: 17 | apiServerPort: 443 18 | serviceDomain: c6fme.k8s.ghost.westeurope.azure.gigantic.io 19 | services: 20 | cidrBlocks: 21 | - 172.31.0.0/16 22 | controlPlaneEndpoint: 23 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 24 | port: 443 25 | infrastructureRef: 26 | kind: AzureCluster 27 | name: c6fme 28 | namespace: default 29 | status: 30 | infrastructureReady: false 31 | -------------------------------------------------------------------------------- /integration/test/crmapping/testdata/case-0-migrate-AzureConfig_AzureCluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureCluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: "" 6 | creationTimestamp: null 7 | labels: 8 | azure-operator.giantswarm.io/version: 4.2.0 9 | cluster.x-k8s.io/cluster-name: c6fme 10 | giantswarm.io/cluster: c6fme 11 | giantswarm.io/organization: giantswarm 12 | release.giantswarm.io/version: 12.0.0 13 | name: c6fme 14 | namespace: org-giantswarm 15 | resourceVersion: "1" 16 | spec: 17 | controlPlaneEndpoint: 18 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 19 | port: 443 20 | location: westeurope 21 | networkSpec: 22 | apiServerLB: {} 23 | vnet: 24 | cidrBlocks: 25 | - 10.10.0.0/16 26 | name: c6fme-VirtualNetwork 27 | resourceGroup: c6fme 28 | resourceGroup: c6fme 29 | status: 30 | ready: false 31 | -------------------------------------------------------------------------------- /integration/test/crmapping/testdata/case-0-migrate-AzureConfig_AzureMachine.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureMachine 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | azure-operator.giantswarm.io/version: 4.2.0 7 | cluster.x-k8s.io/cluster-name: c6fme 8 | cluster.x-k8s.io/control-plane: "true" 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: giantswarm 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme-master-0 13 | namespace: org-giantswarm 14 | resourceVersion: "1" 15 | spec: 16 | failureDomain: "1" 17 | image: 18 | marketplace: 19 | offer: flatcar-container-linux-free 20 | publisher: kinvolk 21 | sku: stable 22 | thirdPartyImage: false 23 | version: 2345.3.1 24 | osDisk: 25 | diskSizeGB: 50 26 | managedDisk: 27 | storageAccountType: Premium_LRS 28 | osType: Linux 29 | sshPublicKey: "" 30 | vmSize: Standard_D4s_v3 31 | status: 32 | ready: false 33 | -------------------------------------------------------------------------------- /integration/test/crmapping/testdata/case-0-migrate-AzureConfig_Cluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: cluster.x-k8s.io/v1beta1 2 | kind: Cluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: "" 6 | release.giantswarm.io/upgrading-to-node-pools: "True" 7 | creationTimestamp: null 8 | labels: 9 | azure-operator.giantswarm.io/version: 4.2.0 10 | cluster-operator.giantswarm.io/version: "" 11 | cluster.x-k8s.io/cluster-name: c6fme 12 | giantswarm.io/cluster: c6fme 13 | giantswarm.io/organization: giantswarm 14 | release.giantswarm.io/version: 12.0.0 15 | name: c6fme 16 | namespace: org-giantswarm 17 | resourceVersion: "1" 18 | spec: 19 | clusterNetwork: 20 | apiServerPort: 443 21 | serviceDomain: cluster.local 22 | services: 23 | cidrBlocks: 24 | - 172.31.0.0/16 25 | controlPlaneEndpoint: 26 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 27 | port: 443 28 | infrastructureRef: 29 | kind: AzureCluster 30 | name: c6fme 31 | namespace: org-giantswarm 32 | status: 33 | infrastructureReady: false 34 | -------------------------------------------------------------------------------- /integration/test/crmapping/testdata/namespace-cluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | app: master 6 | cluster: c6fme 7 | customer: giantswarm 8 | giantswarm.io/cluster: c6fme 9 | giantswarm.io/organization: giantswarm 10 | name: c6fme 11 | spec: 12 | finalizers: 13 | - kubernetes 14 | -------------------------------------------------------------------------------- /integration/test/crmapping/testdata/namespace-giantswarm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | customer: giantswarm 6 | giantswarm.io/organization: giantswarm 7 | name: giantswarm 8 | spec: 9 | finalizers: 10 | - kubernetes 11 | -------------------------------------------------------------------------------- /integration/test/crmapping/testdata/namespace-org.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | customer: giantswarm 6 | giantswarm.io/organization: giantswarm 7 | name: org-giantswarm 8 | spec: 9 | finalizers: 10 | - kubernetes 11 | -------------------------------------------------------------------------------- /integration/test/crmapping/testdata/simple_azurecluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureCluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: "" 6 | labels: 7 | azure-operator.giantswarm.io/version: 4.2.0 8 | cluster.x-k8s.io/cluster-name: c6fme 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: "giantswarm" 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme 13 | namespace: org-giantswarm 14 | spec: 15 | controlPlaneEndpoint: 16 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 17 | port: 443 18 | location: westeurope 19 | networkSpec: 20 | vnet: 21 | cidrBlocks: 22 | - 10.10.0.0/16 23 | name: c6fme-VirtualNetwork 24 | resourceGroup: c6fme 25 | resourceGroup: "" 26 | status: 27 | ready: false 28 | -------------------------------------------------------------------------------- /integration/test/crmapping/testdata/simple_azuremachine.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureMachine 3 | metadata: 4 | labels: 5 | azure-operator.giantswarm.io/version: 4.2.0 6 | cluster.x-k8s.io/cluster-name: c6fme 7 | cluster.x-k8s.io/control-plane: "true" 8 | giantswarm.io/cluster: c6fme 9 | giantswarm.io/organization: "giantswarm" 10 | release.giantswarm.io/version: 12.0.0 11 | name: c6fme-master-0 12 | namespace: org-giantswarm 13 | spec: 14 | failureDomain: "1" 15 | image: 16 | marketplace: 17 | offer: flatcar-container-linux-free 18 | publisher: kinvolk 19 | sku: stable 20 | version: 2345.3.1 21 | osDisk: 22 | diskSizeGB: 50 23 | managedDisk: 24 | storageAccountType: Premium_LRS 25 | osType: Linux 26 | sshPublicKey: ssh-rsa foobarbaz== foobar 27 | vmSize: Standard_D4s_v3 28 | status: 29 | ready: false 30 | -------------------------------------------------------------------------------- /integration/test/crmapping/testdata/simple_cluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cluster.x-k8s.io/v1beta1 2 | kind: Cluster 3 | metadata: 4 | labels: 5 | azure-operator.giantswarm.io/version: 4.2.0 6 | cluster-operator.giantswarm.io/version: "" 7 | cluster.x-k8s.io/cluster-name: c6fme 8 | giantswarm.io/cluster: c6fme 9 | giantswarm.io/organization: "giantswarm" 10 | release.giantswarm.io/version: 12.0.0 11 | name: c6fme 12 | namespace: org-giantswarm 13 | spec: 14 | clusterNetwork: 15 | apiServerPort: 443 16 | serviceDomain: c6fme.k8s.ghost.westeurope.azure.gigantic.io 17 | services: 18 | cidrBlocks: 19 | - 172.31.0.0/16 20 | controlPlaneEndpoint: 21 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 22 | port: 443 23 | infrastructureRef: 24 | kind: AzureCluster 25 | name: c6fme 26 | namespace: default 27 | status: 28 | infrastructureReady: false 29 | -------------------------------------------------------------------------------- /pkg/annotation/annotation.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | const ( 4 | StateMachineCurrentState = "azure-machine-pool.giantswarm.io/state-machine-current-state" 5 | 6 | // UpgradingToNodePools is set to True during the first cluster upgrade to node pools release. 7 | UpgradingToNodePools = "release.giantswarm.io/upgrading-to-node-pools" 8 | 9 | WorkersEgressExternalPublicIP = "giantswarm.io/workers-egress-external-public-ip" 10 | ) 11 | -------------------------------------------------------------------------------- /pkg/azureconditions/error.go: -------------------------------------------------------------------------------- 1 | package azureconditions 2 | 3 | import ( 4 | "github.com/Azure/go-autorest/autorest" 5 | "github.com/giantswarm/microerror" 6 | ) 7 | 8 | var invalidConfigError = µerror.Error{ 9 | Kind: "invalidConfigError", 10 | } 11 | 12 | // IsInvalidConfig asserts invalidConfigError. 13 | func IsInvalidConfig(err error) bool { 14 | return microerror.Cause(err) == invalidConfigError 15 | } 16 | 17 | var notFoundError = µerror.Error{ 18 | Kind: "notFoundError", 19 | } 20 | 21 | // IsNotFound asserts notFoundError. 22 | func IsNotFound(err error) bool { 23 | if err == nil { 24 | return false 25 | } 26 | 27 | c := microerror.Cause(err) 28 | 29 | if c == notFoundError { 30 | return true 31 | } 32 | 33 | { 34 | dErr, ok := c.(autorest.DetailedError) 35 | if ok { 36 | if dErr.StatusCode == 404 { 37 | return true 38 | } 39 | } 40 | } 41 | 42 | return false 43 | } 44 | -------------------------------------------------------------------------------- /pkg/backpressure/backpressure.go: -------------------------------------------------------------------------------- 1 | package backpressure 2 | 3 | import "time" 4 | 5 | type Backpressure struct { 6 | notBefore time.Time 7 | } 8 | 9 | func (g *Backpressure) NotBefore(t time.Time) { 10 | g.notBefore = t 11 | } 12 | 13 | func (g *Backpressure) CanProceed() bool { 14 | return time.Now().After(g.notBefore) 15 | } 16 | 17 | func (g *Backpressure) RetryAfter() time.Time { 18 | return g.notBefore 19 | } 20 | -------------------------------------------------------------------------------- /pkg/checksum/error.go: -------------------------------------------------------------------------------- 1 | package checksum 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var unableToGetTemplateError = µerror.Error{ 8 | Kind: "unableToGetTemplate", 9 | } 10 | 11 | func IsUnableToGetTemplateError(err error) bool { 12 | return microerror.Cause(err) == unableToGetTemplateError 13 | } 14 | 15 | var nilTemplateLinkError = µerror.Error{ 16 | Kind: "nilTemplateLink", 17 | } 18 | 19 | func IsNilTemplateLinkError(err error) bool { 20 | return microerror.Cause(err) == nilTemplateLinkError 21 | } 22 | -------------------------------------------------------------------------------- /pkg/credential/error.go: -------------------------------------------------------------------------------- 1 | package credential 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var missingValueError = µerror.Error{ 8 | Kind: "missingValueError", 9 | } 10 | 11 | var invalidConfigError = µerror.Error{ 12 | Kind: "invalidConfigError", 13 | } 14 | 15 | var oldStyleCredentialsError = µerror.Error{ 16 | Kind: "oldStyleCredentialsError", 17 | } 18 | -------------------------------------------------------------------------------- /pkg/credential/spec.go: -------------------------------------------------------------------------------- 1 | package credential 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/Azure/go-autorest/autorest/azure/auth" 7 | ) 8 | 9 | type Provider interface { 10 | GetOrganizationAzureCredentials(ctx context.Context, credentialNamespace, credentialName string) (auth.ClientCredentialsConfig, string, string, error) 11 | } 12 | type EmptyProvider struct { 13 | } 14 | 15 | func (p EmptyProvider) GetOrganizationAzureCredentials(ctx context.Context, credentialNamespace, credentialName string) (auth.ClientCredentialsConfig, string, string, error) { 16 | return auth.ClientCredentialsConfig{}, "", "", nil 17 | } 18 | -------------------------------------------------------------------------------- /pkg/drainer/error.go: -------------------------------------------------------------------------------- 1 | package drainer 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/giantswarm/microerror" 7 | ) 8 | 9 | var alreadyCordonedError = µerror.Error{ 10 | Kind: "alreadyCordonedError", 11 | } 12 | 13 | func IsAlreadyCordoned(err error) bool { 14 | return microerror.Cause(err) == alreadyCordonedError 15 | } 16 | 17 | var cannotEvictPodError = µerror.Error{ 18 | Kind: "cannotEvictPodError", 19 | } 20 | 21 | func IsCannotEvictPod(err error) bool { 22 | c := microerror.Cause(err) 23 | 24 | if err == nil { 25 | return false 26 | } 27 | 28 | if strings.Contains(c.Error(), "Cannot evict pod") { 29 | return true 30 | } 31 | 32 | return c == cannotEvictPodError 33 | } 34 | 35 | var evictionInProgressError = µerror.Error{ 36 | Kind: "evictionInProgressError", 37 | } 38 | 39 | func IsEvictionInProgress(err error) bool { 40 | return microerror.Cause(err) == evictionInProgressError 41 | } 42 | 43 | var drainTimeoutError = µerror.Error{ 44 | Kind: "drainTimeoutError", 45 | } 46 | 47 | // IsDrainTimeout asserts drainTimeoutError. 48 | func IsDrainTimeout(err error) bool { 49 | return microerror.Cause(err) == drainTimeoutError 50 | } 51 | 52 | var invalidConfigError = µerror.Error{ 53 | Kind: "invalidConfigError", 54 | } 55 | 56 | // IsInvalidConfig asserts invalidConfigError. 57 | func IsInvalidConfig(err error) bool { 58 | return microerror.Cause(err) == invalidConfigError 59 | } 60 | -------------------------------------------------------------------------------- /pkg/drainer/key.go: -------------------------------------------------------------------------------- 1 | package drainer 2 | 3 | import ( 4 | "strings" 5 | 6 | v1 "k8s.io/api/core/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | func isCriticalPod(podName string) bool { 11 | r := false 12 | // k8s-api-healthz is a service on master nodes that exposes 13 | // unauthenticated apiserver /healthz for load balancers. It is deployed as 14 | // manifest similar to api-server, controller-manager and scheduler and 15 | // therefore it always restarts after termination. 16 | r = r || strings.HasPrefix(podName, "k8s-api-healthz") 17 | r = r || strings.HasPrefix(podName, "k8s-api-server") 18 | r = r || strings.HasPrefix(podName, "k8s-controller-manager") 19 | r = r || strings.HasPrefix(podName, "k8s-scheduler") 20 | 21 | return r 22 | } 23 | 24 | func isDaemonSetPod(pod v1.Pod) bool { 25 | r := false 26 | ownerRefrence := metav1.GetControllerOf(&pod) 27 | 28 | if ownerRefrence != nil && ownerRefrence.Kind == "DaemonSet" { 29 | r = true 30 | } 31 | 32 | return r 33 | } 34 | 35 | func isEvictedPod(pod v1.Pod) bool { 36 | return pod.Status.Reason == "Evicted" 37 | } 38 | -------------------------------------------------------------------------------- /pkg/employees/employees.go: -------------------------------------------------------------------------------- 1 | package employees 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/giantswarm/microerror" 7 | ) 8 | 9 | type SSHUserList map[string][]string 10 | 11 | func FromDraughtsmanString(userList string) (SSHUserList, error) { 12 | sshUsers := SSHUserList{} 13 | 14 | for _, user := range strings.Split(userList, ",") { 15 | if user == "" { 16 | continue 17 | } 18 | 19 | trimmed := strings.TrimSpace(user) 20 | split := strings.Split(trimmed, ":") 21 | 22 | if len(split) != 2 { 23 | return nil, microerror.Maskf(parsingFailedError, "SSH user format must be :") 24 | } 25 | 26 | if split[0] == "" || len(split[1]) == 0 { 27 | continue 28 | } 29 | 30 | sshUsers[split[0]] = append(sshUsers[split[0]], split[1]) 31 | } 32 | 33 | return sshUsers, nil 34 | } 35 | -------------------------------------------------------------------------------- /pkg/employees/error.go: -------------------------------------------------------------------------------- 1 | package employees 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var parsingFailedError = µerror.Error{ 8 | Kind: "parsingFailedError", 9 | } 10 | 11 | // IsParsingFailedError asserts parsingFailedError. 12 | func IsParsingFailedError(err error) bool { 13 | return microerror.Cause(err) == parsingFailedError 14 | } 15 | -------------------------------------------------------------------------------- /pkg/handler/ipam/error.go: -------------------------------------------------------------------------------- 1 | package ipam 2 | 3 | import ( 4 | "github.com/Azure/go-autorest/autorest" 5 | "github.com/giantswarm/microerror" 6 | ) 7 | 8 | var parentNetworkRangeStillNotKnown = µerror.Error{ 9 | Kind: "parentNetworkRangeStillNotKnown", 10 | } 11 | 12 | // IsParentNetworkRangeStillNotKnown asserts parentNetworkRangeStillNotKnown. This can happen in 13 | // node pools IPAM reconciliation, during subnet allocation, when 14 | // AzureCluster.Spec.NetworkSpec.Vnet.CidrBlock is still not set, because VNet for the tenant 15 | // cluster is still not allocated (e.g. when cluster is still being created). 16 | func IsParentNetworkRangeStillNotKnown(err error) bool { 17 | return microerror.Cause(err) == parentNetworkRangeStillNotKnown 18 | } 19 | 20 | var invalidConfigError = µerror.Error{ 21 | Kind: "invalid config", 22 | } 23 | 24 | // IsInvalidConfig asserts invalidConfigError. 25 | func IsInvalidConfig(err error) bool { 26 | return microerror.Cause(err) == invalidConfigError 27 | } 28 | 29 | var invalidObjectError = µerror.Error{ 30 | Kind: "invalid object", 31 | } 32 | 33 | // IsInvalidObject asserts invalidObjectError. 34 | func IsInvalidObject(err error) bool { 35 | return microerror.Cause(err) == invalidObjectError 36 | } 37 | 38 | func IsNotFound(err error) bool { 39 | if err == nil { 40 | return false 41 | } 42 | 43 | { 44 | c := microerror.Cause(err) 45 | dErr, ok := c.(autorest.DetailedError) 46 | if ok { 47 | if dErr.StatusCode == 404 { 48 | return true 49 | } 50 | } 51 | } 52 | 53 | return false 54 | } 55 | -------------------------------------------------------------------------------- /pkg/handler/ipam/nop_releaser.go: -------------------------------------------------------------------------------- 1 | package ipam 2 | 3 | import ( 4 | "context" 5 | "net" 6 | ) 7 | 8 | type nopReleaser struct{} 9 | 10 | func NewNOPReleaser() Releaser { 11 | return &nopReleaser{} 12 | } 13 | 14 | func (r *nopReleaser) Release(ctx context.Context, subnet net.IPNet, namespace, name string) error { 15 | return nil 16 | } 17 | -------------------------------------------------------------------------------- /pkg/handler/ipam/test_checker.go: -------------------------------------------------------------------------------- 1 | package ipam 2 | 3 | import ( 4 | "context" 5 | "net" 6 | ) 7 | 8 | type TestChecker struct { 9 | subnet *net.IPNet 10 | } 11 | 12 | func NewTestChecker(subnet *net.IPNet) *TestChecker { 13 | a := &TestChecker{ 14 | subnet: subnet, 15 | } 16 | 17 | return a 18 | } 19 | 20 | func (c *TestChecker) Check(ctx context.Context, namespace string, name string) (*net.IPNet, error) { 21 | return c.subnet, nil 22 | } 23 | -------------------------------------------------------------------------------- /pkg/handler/ipam/test_collector.go: -------------------------------------------------------------------------------- 1 | package ipam 2 | 3 | import ( 4 | "context" 5 | "net" 6 | ) 7 | 8 | type TestCollector struct { 9 | subnets []net.IPNet 10 | } 11 | 12 | func NewTestCollector(subnets []net.IPNet) *TestCollector { 13 | c := &TestCollector{ 14 | subnets: subnets, 15 | } 16 | 17 | return c 18 | } 19 | 20 | func (c *TestCollector) Collect(ctx context.Context, obj interface{}) ([]net.IPNet, error) { 21 | return c.subnets, nil 22 | } 23 | -------------------------------------------------------------------------------- /pkg/handler/ipam/test_network_range_getter.go: -------------------------------------------------------------------------------- 1 | package ipam 2 | 3 | import ( 4 | "context" 5 | "net" 6 | ) 7 | 8 | type TestNetworkRangeGetter struct { 9 | parentNetworkRange net.IPNet 10 | requiredNetworkMask net.IPMask 11 | } 12 | 13 | func NewTestNetworkRangeGetter(parentNetworkRange net.IPNet, requiredNetworkMaskBits int) *TestNetworkRangeGetter { 14 | g := &TestNetworkRangeGetter{ 15 | parentNetworkRange: parentNetworkRange, 16 | requiredNetworkMask: net.CIDRMask(requiredNetworkMaskBits, 32), 17 | } 18 | 19 | return g 20 | } 21 | 22 | func (g *TestNetworkRangeGetter) GetParentNetworkRange(_ context.Context, _ interface{}) (net.IPNet, error) { 23 | return g.parentNetworkRange, nil 24 | } 25 | 26 | func (g *TestNetworkRangeGetter) GetRequiredIPMask() net.IPMask { 27 | return g.requiredNetworkMask 28 | } 29 | -------------------------------------------------------------------------------- /pkg/handler/ipam/test_persister.go: -------------------------------------------------------------------------------- 1 | package ipam 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "reflect" 7 | 8 | "github.com/giantswarm/microerror" 9 | ) 10 | 11 | type TestPersister struct { 12 | subnet net.IPNet 13 | } 14 | 15 | func NewTestPersister(subnet net.IPNet) *TestPersister { 16 | p := &TestPersister{ 17 | subnet: subnet, 18 | } 19 | 20 | return p 21 | } 22 | 23 | func (p *TestPersister) Persist(ctx context.Context, subnet net.IPNet, namespace string, name string) error { 24 | if !reflect.DeepEqual(subnet, p.subnet) { 25 | return microerror.Mask(invalidConfigError) 26 | } 27 | 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /pkg/handler/nodes/nodes.go: -------------------------------------------------------------------------------- 1 | package nodes 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/microerror" 7 | corev1 "k8s.io/api/core/v1" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | ) 10 | 11 | func GetTenantClusterNodes(ctx context.Context, tenantClusterK8sClient client.Client) ([]corev1.Node, error) { 12 | nodeList := &corev1.NodeList{} 13 | err := tenantClusterK8sClient.List(ctx, nodeList) 14 | if err != nil { 15 | return nil, microerror.Mask(err) 16 | } 17 | 18 | return nodeList.Items, nil 19 | } 20 | -------------------------------------------------------------------------------- /pkg/handler/nodes/scalestrategy/incremental.go: -------------------------------------------------------------------------------- 1 | package scalestrategy 2 | 3 | type Incremental struct { 4 | } 5 | 6 | func (i Incremental) GetNodeCount(currentCount int64, desiredCount int64) int64 { 7 | if currentCount < desiredCount { 8 | return currentCount + 1 9 | } 10 | 11 | if currentCount > desiredCount { 12 | return currentCount - 1 13 | } 14 | 15 | return currentCount 16 | } 17 | -------------------------------------------------------------------------------- /pkg/handler/nodes/scalestrategy/interface.go: -------------------------------------------------------------------------------- 1 | package scalestrategy 2 | 3 | type Interface interface { 4 | GetNodeCount(currentCount int64, desiredCount int64) int64 5 | } 6 | -------------------------------------------------------------------------------- /pkg/handler/nodes/scalestrategy/quick.go: -------------------------------------------------------------------------------- 1 | package scalestrategy 2 | 3 | type Quick struct { 4 | } 5 | 6 | func (i Quick) GetNodeCount(currentCount int64, desiredCount int64) int64 { 7 | return desiredCount 8 | } 9 | -------------------------------------------------------------------------------- /pkg/handler/nodes/scalestrategy/staircase.go: -------------------------------------------------------------------------------- 1 | package scalestrategy 2 | 3 | type Staircase struct { 4 | } 5 | 6 | const ( 7 | safeThreshold = 5 8 | ) 9 | 10 | func (i Staircase) GetNodeCount(currentCount int64, desiredCount int64) int64 { 11 | // Cluster size decreased or unchanged. 12 | if currentCount >= desiredCount { 13 | return desiredCount 14 | } 15 | 16 | if desiredCount-currentCount > safeThreshold { 17 | return currentCount + safeThreshold 18 | } 19 | 20 | return desiredCount 21 | } 22 | -------------------------------------------------------------------------------- /pkg/handler/nodes/scalestrategy/staircase_test.go: -------------------------------------------------------------------------------- 1 | package scalestrategy 2 | 3 | import "testing" 4 | 5 | func TestSafeQuick_GetNodeCount(t *testing.T) { 6 | tests := []struct { 7 | name string 8 | currentCount int64 9 | desiredCount int64 10 | want int64 11 | }{ 12 | { 13 | name: "Size increased by 2", 14 | currentCount: 2, 15 | desiredCount: 4, 16 | want: 4, 17 | }, 18 | { 19 | name: "Size increased by 20", 20 | currentCount: 10, 21 | desiredCount: 20, 22 | want: 15, 23 | }, 24 | { 25 | name: "Size increased by 20, second run", 26 | currentCount: 13, 27 | desiredCount: 20, 28 | want: 18, 29 | }, 30 | { 31 | name: "Size increased by 20, last run", 32 | currentCount: 18, 33 | desiredCount: 20, 34 | want: 20, 35 | }, 36 | { 37 | name: "Size decreased by 1", 38 | currentCount: 5, 39 | desiredCount: 4, 40 | want: 4, 41 | }, 42 | { 43 | name: "Size decreased by 10", 44 | currentCount: 20, 45 | desiredCount: 10, 46 | want: 10, 47 | }, 48 | } 49 | for _, tt := range tests { 50 | t.Run(tt.name, func(t *testing.T) { 51 | i := Staircase{} 52 | if got := i.GetNodeCount(tt.currentCount, tt.desiredCount); got != tt.want { 53 | t.Errorf("GetNodeCount() = %v, want %v", got, tt.want) 54 | } 55 | }) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /pkg/handler/nodes/state/error.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import "github.com/giantswarm/microerror" 4 | 5 | // executionFailedError is an error type for situations where Resource 6 | // execution cannot continue and must always fall back to operatorkit. 7 | // 8 | // This error should never be matched against and therefore there is no matcher 9 | // implement. For further information see: 10 | // 11 | // https://github.com/giantswarm/fmt/blob/master/go/errors.md#matching-errors 12 | var executionFailedError = µerror.Error{ 13 | Kind: "executionFailedError", 14 | } 15 | 16 | var unknownStateError = µerror.Error{ 17 | Kind: "unknownStateError", 18 | } 19 | 20 | func IsUnkownStateError(err error) bool { 21 | return microerror.Cause(err) == unknownStateError 22 | } 23 | -------------------------------------------------------------------------------- /pkg/handler/nodes/state/funcs.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/microerror" 7 | ) 8 | 9 | func (m Machine) Execute(ctx context.Context, obj interface{}, currentState State) (State, error) { 10 | transitionFunc, exists := m.Transitions[currentState] 11 | if !exists { 12 | return "", microerror.Maskf(unknownStateError, "State: %q is not configured in this state machine", currentState) 13 | } 14 | 15 | newState, err := transitionFunc(ctx, obj, currentState) 16 | if err != nil { 17 | return newState, microerror.Mask(err) 18 | } 19 | 20 | _, exists = m.Transitions[newState] 21 | if !exists { 22 | return newState, microerror.Maskf(executionFailedError, "State transition returned new unknown state: %q. Input state: %q", newState, currentState) 23 | } 24 | 25 | m.Logger.LogCtx(ctx, "resource", m.ResourceName, "message", "state changed", "oldState", currentState, "newState", newState) 26 | return newState, nil 27 | } 28 | -------------------------------------------------------------------------------- /pkg/handler/nodes/state/types.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/micrologger" 7 | ) 8 | 9 | // Machine is a simple type to hold state machine configuration. 10 | type Machine struct { 11 | Logger micrologger.Logger 12 | ResourceName string 13 | Transitions TransitionMap 14 | } 15 | 16 | type State string 17 | type TransitionMap map[State]TransitionFunc 18 | 19 | // TransitionFunc defines state transition function signature. 20 | type TransitionFunc func(ctx context.Context, obj interface{}, currentState State) (State, error) 21 | -------------------------------------------------------------------------------- /pkg/handler/release/delete.go: -------------------------------------------------------------------------------- 1 | package release 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { 8 | return nil 9 | } 10 | -------------------------------------------------------------------------------- /pkg/handler/release/error.go: -------------------------------------------------------------------------------- 1 | package release 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /pkg/handler/release/resource.go: -------------------------------------------------------------------------------- 1 | package release 2 | 3 | import ( 4 | "github.com/giantswarm/k8sclient/v7/pkg/k8sclient" 5 | "github.com/giantswarm/microerror" 6 | "github.com/giantswarm/micrologger" 7 | ) 8 | 9 | const ( 10 | Name = "release" 11 | ) 12 | 13 | type Config struct { 14 | K8sClient k8sclient.Interface 15 | Logger micrologger.Logger 16 | } 17 | 18 | type Resource struct { 19 | k8sClient k8sclient.Interface 20 | logger micrologger.Logger 21 | } 22 | 23 | func New(config Config) (*Resource, error) { 24 | if config.K8sClient == nil { 25 | return nil, microerror.Maskf(invalidConfigError, "%T.K8sClient must not be empty", config) 26 | } 27 | if config.Logger == nil { 28 | return nil, microerror.Maskf(invalidConfigError, "%T.Logger must not be empty", config) 29 | } 30 | 31 | newResource := &Resource{ 32 | k8sClient: config.K8sClient, 33 | logger: config.Logger, 34 | } 35 | 36 | return newResource, nil 37 | } 38 | 39 | func (r *Resource) Name() string { 40 | return Name 41 | } 42 | -------------------------------------------------------------------------------- /pkg/helpers/error.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidObjectError = µerror.Error{ 8 | Kind: "invalid object", 9 | } 10 | 11 | // IsInvalidObject asserts invalidObjectError. 12 | func IsInvalidObject(err error) bool { 13 | return microerror.Cause(err) == invalidObjectError 14 | } 15 | 16 | var tooManyCredentialsError = µerror.Error{ 17 | Kind: "tooManyCredentialsError", 18 | } 19 | 20 | // IsTooManyCredentialsError asserts tooManyCredentialsError. 21 | func IsTooManyCredentialsError(err error) bool { 22 | return microerror.Cause(err) == tooManyCredentialsError 23 | } 24 | 25 | var missingOrganizationLabel = µerror.Error{ 26 | Kind: "missingOrganizationLabel", 27 | } 28 | 29 | // IsMissingOrganizationLabel asserts missingOrganizationLabel. 30 | func IsMissingOrganizationLabel(err error) bool { 31 | return microerror.Cause(err) == missingOrganizationLabel 32 | } 33 | -------------------------------------------------------------------------------- /pkg/httputil/error.go: -------------------------------------------------------------------------------- 1 | package httputil 2 | 3 | import "github.com/giantswarm/microerror" 4 | 5 | var parseError = µerror.Error{ 6 | Kind: "parseError", 7 | } 8 | 9 | // IsParse asserts parseError. 10 | func IsParse(err error) bool { 11 | return microerror.Cause(err) == parseError 12 | } 13 | -------------------------------------------------------------------------------- /pkg/httputil/parser.go: -------------------------------------------------------------------------------- 1 | // Package httputil provides useful HTTP utilities such as header parsers etc. 2 | package httputil 3 | 4 | import ( 5 | "net/http" 6 | "strconv" 7 | "time" 8 | 9 | "github.com/giantswarm/microerror" 10 | ) 11 | 12 | // ParseRetryAfter tries to parse `Retry-After` value from given HTTP response. 13 | // In case there are multiple `Retry-After` header values present, first 14 | // parseable one is returned. In case of nil response, missing or unparseable 15 | // header value a `parseError` is returned. 16 | func ParseRetryAfter(r *http.Response) (time.Time, error) { 17 | if r == nil { 18 | return time.Time{}, microerror.Maskf(parseError, "nil response") 19 | } 20 | 21 | // Iterate over possible values for `Retry-After` header. First parseable 22 | // wins. 23 | for _, v := range r.Header.Values("Retry-After") { 24 | // `Retry-After` value can be either or . 25 | 26 | // Try to parse integer value of first. 27 | i64, err := strconv.ParseInt(v, 10, 32) 28 | if err == nil && i64 > 0 { 29 | return time.Now().UTC().Add(time.Duration(i64) * time.Second), nil 30 | } 31 | 32 | // Try instead. 33 | t, err := http.ParseTime(v) 34 | if err == nil { 35 | return t, nil 36 | } 37 | } 38 | 39 | return time.Time{}, microerror.Maskf(parseError, "parseable Retry-After missing") 40 | } 41 | -------------------------------------------------------------------------------- /pkg/label/label.go: -------------------------------------------------------------------------------- 1 | package label 2 | 3 | const ( 4 | App = "app" 5 | ) 6 | 7 | const ( 8 | Cluster = "giantswarm.io/cluster" 9 | Organization = "giantswarm.io/organization" 10 | Provider = "giantswarm.io/provider" 11 | VersionBundle = "giantswarm.io/version-bundle" 12 | ) 13 | 14 | const ( 15 | OperatorVersion = "azure-operator.giantswarm.io/version" 16 | ClusterOperatorVersion = "cluster-operator.giantswarm.io/version" 17 | CGroupVersion = "cgroups.giantswarm.io/version" 18 | ReleaseVersion = "release.giantswarm.io/version" 19 | SingleTenantSP = "giantswarm.io/single-tenant-service-principal" 20 | 21 | AzureOperatorVersionTag = "gs-azure-operator.giantswarm.io-version" 22 | ) 23 | -------------------------------------------------------------------------------- /pkg/locker/error.go: -------------------------------------------------------------------------------- 1 | package locker 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var alreadyExistsError = µerror.Error{ 8 | Kind: "alreadyExistsError", 9 | } 10 | 11 | // IsAlreadyExists asserts alreadyExistsError. 12 | func IsAlreadyExists(err error) bool { 13 | return microerror.Cause(err) == alreadyExistsError 14 | } 15 | 16 | var invalidConfigError = µerror.Error{ 17 | Kind: "invalidConfigError", 18 | } 19 | 20 | // IsInvalidConfig asserts invalidConfigError. 21 | func IsInvalidConfig(err error) bool { 22 | return microerror.Cause(err) == invalidConfigError 23 | } 24 | 25 | var notFoundError = µerror.Error{ 26 | Kind: "notFoundError", 27 | } 28 | 29 | // IsNotFound asserts notFoundError. 30 | func IsNotFound(err error) bool { 31 | return microerror.Cause(err) == notFoundError 32 | } 33 | -------------------------------------------------------------------------------- /pkg/locker/mutex_locker.go: -------------------------------------------------------------------------------- 1 | package locker 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | 7 | "github.com/giantswarm/microerror" 8 | "github.com/giantswarm/micrologger" 9 | ) 10 | 11 | type MutexLockerConfig struct { 12 | Logger micrologger.Logger 13 | } 14 | 15 | // MutexLocker implements Interface using sync.Mutex. For now we use a shared 16 | // instance of *MutexLocker for all IPAM related activity of network packages in 17 | // the legacy controllers and ipam resources in the clusterapi controllers. 18 | type MutexLocker struct { 19 | logger micrologger.Logger 20 | 21 | mutex sync.Mutex 22 | } 23 | 24 | func NewMutexLocker(config MutexLockerConfig) (*MutexLocker, error) { 25 | if config.Logger == nil { 26 | return nil, microerror.Maskf(invalidConfigError, "%T.Logger must not be empty", config) 27 | } 28 | 29 | l := &MutexLocker{ 30 | logger: config.Logger, 31 | 32 | mutex: sync.Mutex{}, 33 | } 34 | 35 | return l, nil 36 | } 37 | 38 | func (l *MutexLocker) Lock(ctx context.Context) error { 39 | l.mutex.Lock() 40 | return nil 41 | } 42 | 43 | func (l *MutexLocker) Unlock(ctx context.Context) error { 44 | l.mutex.Unlock() 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /pkg/locker/spec.go: -------------------------------------------------------------------------------- 1 | package locker 2 | 3 | import "context" 4 | 5 | // Interface is some form of lock implementation like achieved for in process 6 | // locking using sync.Mutex. 7 | type Interface interface { 8 | Lock(ctx context.Context) error 9 | Unlock(ctx context.Context) error 10 | } 11 | -------------------------------------------------------------------------------- /pkg/machinepoolmigration/error.go: -------------------------------------------------------------------------------- 1 | package machinepoolmigration 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /pkg/machinepoolmigration/references.go: -------------------------------------------------------------------------------- 1 | package machinepoolmigration 2 | 3 | import ( 4 | capz "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 5 | capzexp "sigs.k8s.io/cluster-api-provider-azure/exp/api/v1beta1" 6 | capi "sigs.k8s.io/cluster-api/api/v1beta1" 7 | capiexp "sigs.k8s.io/cluster-api/exp/api/v1beta1" 8 | ) 9 | 10 | var ( 11 | DesiredCAPIGroupVersion = capi.GroupVersion.String() 12 | DesiredCAPZGroupVersion = capz.GroupVersion.String() 13 | ) 14 | 15 | func AreMachinePoolReferencesUpdated(machinePool capiexp.MachinePool) bool { 16 | // check Cluster owner reference 17 | for _, ref := range machinePool.ObjectMeta.OwnerReferences { 18 | if ref.Kind == "Cluster" && ref.APIVersion != DesiredCAPIGroupVersion { 19 | return false 20 | } 21 | } 22 | 23 | // check InfrastructureRef (AzureMachinePool) API version 24 | if machinePool.Spec.Template.Spec.InfrastructureRef.Kind == "AzureMachinePool" && 25 | machinePool.Spec.Template.Spec.InfrastructureRef.APIVersion != DesiredCAPZGroupVersion { 26 | return false 27 | } 28 | 29 | return true 30 | } 31 | 32 | func AreAzureMachinePoolReferencesUpdated(azureMachinePool capzexp.AzureMachinePool) bool { 33 | // check MachinePool owner reference 34 | for _, ref := range azureMachinePool.ObjectMeta.OwnerReferences { 35 | if ref.Kind == "MachinePool" && ref.APIVersion != DesiredCAPIGroupVersion { 36 | return false 37 | } 38 | } 39 | 40 | return true 41 | } 42 | -------------------------------------------------------------------------------- /pkg/normalize/normalize.go: -------------------------------------------------------------------------------- 1 | package normalize 2 | 3 | import ( 4 | "strings" 5 | "unicode" 6 | ) 7 | 8 | const maxDNSLabelLength = 63 9 | 10 | // AsDNSLabelName normalizes input string to be valid DNS label name so that it 11 | // can be used as Kubernetes object identifier such as namespace name. 12 | // 13 | // NOTE: This function returns an empty string if input string consists of only 14 | // 15 | // non-allowed characters. 16 | // 17 | // https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names 18 | func AsDNSLabelName(v string) string { 19 | var xs []rune 20 | 21 | for _, x := range strings.ToLower(v) { 22 | 23 | // Is x in whitelisted characters? 24 | if x == '-' || unicode.IsDigit(x) || ('a' <= x && x <= 'z') { 25 | xs = append(xs, x) 26 | } else if len(xs) > 0 && xs[len(xs)-1] != '-' { 27 | // If not, append dash and coalesce consequtive ones. 28 | xs = append(xs, '-') 29 | } 30 | } 31 | 32 | // Ensure that the string doesn't start or end with dash. 33 | for len(xs) > 0 { 34 | if xs[0] == '-' { 35 | xs = xs[1:] 36 | continue 37 | } 38 | 39 | if xs[len(xs)-1] == '-' { 40 | xs = xs[:len(xs)-1] 41 | continue 42 | } 43 | 44 | break 45 | } 46 | 47 | if len(xs) > maxDNSLabelLength { 48 | xs = xs[:maxDNSLabelLength] 49 | } 50 | 51 | return string(xs) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/project/project.go: -------------------------------------------------------------------------------- 1 | package project 2 | 3 | var ( 4 | description string = "The azure-operator manages Kubernetes clusters on Azure." 5 | gitSHA = "n/a" 6 | name string = "azure-operator" 7 | source string = "https://github.com/giantswarm/azure-operator" 8 | version = "8.2.1-dev" 9 | ) 10 | 11 | func Description() string { 12 | return description 13 | } 14 | 15 | func GitSHA() string { 16 | return gitSHA 17 | } 18 | 19 | func Name() string { 20 | return name 21 | } 22 | 23 | func Source() string { 24 | return source 25 | } 26 | 27 | func Version() string { 28 | return version 29 | } 30 | -------------------------------------------------------------------------------- /pkg/project/version_bundle.go: -------------------------------------------------------------------------------- 1 | package project 2 | 3 | import ( 4 | "github.com/giantswarm/versionbundle" 5 | ) 6 | 7 | func NewVersionBundle() versionbundle.Bundle { 8 | return versionbundle.Bundle{ 9 | Name: Name(), 10 | Version: Version(), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /pkg/tenantcluster/error.go: -------------------------------------------------------------------------------- 1 | package tenantcluster 2 | 3 | import ( 4 | "github.com/giantswarm/errors/tenant" 5 | "github.com/giantswarm/microerror" 6 | ) 7 | 8 | var invalidConfigError = µerror.Error{ 9 | Kind: "invalidConfigError", 10 | } 11 | 12 | // IsInvalidConfig asserts invalidConfigError. 13 | func IsInvalidConfig(err error) bool { 14 | return microerror.Cause(err) == invalidConfigError 15 | } 16 | 17 | var apiNotAvailableError = µerror.Error{ 18 | Kind: "apiNotAvailableError", 19 | } 20 | 21 | // IsAPINotAvailableError asserts apiNotAvailableError. 22 | func IsAPINotAvailableError(err error) bool { 23 | if tenant.IsAPINotAvailable(err) { 24 | return true 25 | } 26 | 27 | return microerror.Cause(err) == apiNotAvailableError 28 | } 29 | -------------------------------------------------------------------------------- /pkg/tenantcluster/spec.go: -------------------------------------------------------------------------------- 1 | package tenantcluster 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/k8sclient/v7/pkg/k8sclient" 7 | 8 | capi "sigs.k8s.io/cluster-api/api/v1beta1" 9 | "sigs.k8s.io/controller-runtime/pkg/client" 10 | ) 11 | 12 | //go:generate mockgen -destination ../mock/mock_tenantcluster/factory.go -source spec.go Factory 13 | 14 | type Factory interface { 15 | GetAllClients(ctx context.Context, cr *capi.Cluster) (k8sclient.Interface, error) 16 | GetClient(ctx context.Context, cr *capi.Cluster) (client.Client, error) 17 | } 18 | -------------------------------------------------------------------------------- /renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | // Base config - https://github.com/giantswarm/renovate-presets/blob/main/default.json5 4 | "github>giantswarm/renovate-presets:default.json5", 5 | // Go specific config - https://github.com/giantswarm/renovate-presets/blob/main/lang-go.json5 6 | "github>giantswarm/renovate-presets:lang-go.json5", 7 | ], 8 | } 9 | -------------------------------------------------------------------------------- /scripts/flowchart.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | _FLOWCHART_DATA_ 9 |
10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /scripts/list-releases.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$1" = "" ]; then 4 | echo "Error: please provide provider parameter" 5 | echo "./list-releases {provider} {release_active_tag(optional)}" 6 | exit 1 7 | fi 8 | 9 | opsctl list installations > list-of-installations.txt 10 | gsctl list endpoints > list-of-endpoints.txt 11 | 12 | provider=$1 13 | active_release=$2 14 | for installation in $(opsctl list installations --provider=$provider --short); do 15 | gsctl select endpoint "${inallation_data[0]}" 16 | 17 | if [ "$active_release" = "" ] 18 | then 19 | gsctl list releases 20 | else 21 | gsctl list releases > list-of-releases.txt 22 | while read releases; do 23 | IFS=' ' read -ra release_data <<< "$releases" 24 | if [ "${release_data[0]}" == "$active_release" ] 25 | then 26 | echo "Release $active_release is ${release_data[1]}" 27 | fi 28 | done /dev/null 2>/dev/null 17 | API=`gsctl info -v | grep "API endpoint:" | awk '{print $3}'` 18 | TOKEN=`gsctl info -v | grep "Auth token:" | awk '{print $3}'` 19 | 20 | curl -s ${API}/v4/clusters/${CLUSTER}/ -H "Authorization: Bearer ${TOKEN}" -X PATCH -d "{ \"release_version\": \"${VERSION}\" }" 21 | -------------------------------------------------------------------------------- /server/endpoint/endpoint.go: -------------------------------------------------------------------------------- 1 | package endpoint 2 | 3 | import ( 4 | "github.com/giantswarm/microendpoint/endpoint/healthz" 5 | versionendpoint "github.com/giantswarm/microendpoint/endpoint/version" 6 | "github.com/giantswarm/microerror" 7 | "github.com/giantswarm/micrologger" 8 | 9 | "github.com/giantswarm/azure-operator/v8/service" 10 | ) 11 | 12 | type Config struct { 13 | Logger micrologger.Logger 14 | Service *service.Service 15 | } 16 | 17 | // Endpoint is the endpoint collection. 18 | type Endpoint struct { 19 | Healthz *healthz.Endpoint 20 | Version *versionendpoint.Endpoint 21 | } 22 | 23 | func New(config Config) (*Endpoint, error) { 24 | var err error 25 | 26 | var healthzEndpoint *healthz.Endpoint 27 | { 28 | c := healthz.Config{ 29 | Logger: config.Logger, 30 | } 31 | 32 | healthzEndpoint, err = healthz.New(c) 33 | if err != nil { 34 | return nil, microerror.Mask(err) 35 | } 36 | } 37 | 38 | var versionEndpoint *versionendpoint.Endpoint 39 | { 40 | c := versionendpoint.Config{ 41 | Logger: config.Logger, 42 | Service: config.Service.Version, 43 | } 44 | 45 | versionEndpoint, err = versionendpoint.New(c) 46 | if err != nil { 47 | return nil, microerror.Mask(err) 48 | } 49 | } 50 | 51 | newEndpoint := &Endpoint{ 52 | Healthz: healthzEndpoint, 53 | Version: versionEndpoint, 54 | } 55 | 56 | return newEndpoint, nil 57 | } 58 | -------------------------------------------------------------------------------- /server/endpoint/error.go: -------------------------------------------------------------------------------- 1 | package endpoint 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /server/error.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import "github.com/giantswarm/microerror" 4 | 5 | var invalidConfigError = µerror.Error{ 6 | Kind: "invalidConfigError", 7 | } 8 | 9 | // IsInvalidConfig asserts invalidConfigError. 10 | func IsInvalidConfig(err error) bool { 11 | return microerror.Cause(err) == invalidConfigError 12 | } 13 | -------------------------------------------------------------------------------- /service/collector/error.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | 16 | var wrongTypeError = µerror.Error{ 17 | Kind: "wrongTypeError", 18 | } 19 | 20 | // IsWrongTypeError asserts wrongTypeError. 21 | func IsWrongTypeError(err error) bool { 22 | return microerror.Cause(err) == wrongTypeError 23 | } 24 | -------------------------------------------------------------------------------- /service/collector/spec.go: -------------------------------------------------------------------------------- 1 | package collector 2 | 3 | import "github.com/prometheus/client_golang/prometheus" 4 | 5 | type AzureAPIMetrics interface { 6 | GetCounterVec(opts prometheus.Opts, labelNames []string) *prometheus.CounterVec 7 | GetHistogramVec(opts prometheus.Opts, labelNames []string) *prometheus.HistogramVec 8 | } 9 | -------------------------------------------------------------------------------- /service/controller/azurecluster/error.go: -------------------------------------------------------------------------------- 1 | package azurecluster 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/azurecluster/handler/azureclusterconditions/create.go: -------------------------------------------------------------------------------- 1 | package azureclusterconditions 2 | 3 | import ( 4 | "context" 5 | "reflect" 6 | 7 | "github.com/giantswarm/microerror" 8 | apierrors "k8s.io/apimachinery/pkg/api/errors" 9 | 10 | "github.com/giantswarm/azure-operator/v8/service/controller/key" 11 | ) 12 | 13 | func (r *Resource) EnsureCreated(ctx context.Context, cr interface{}) error { 14 | var err error 15 | azureCluster, err := key.ToAzureCluster(cr) 16 | if err != nil { 17 | return microerror.Mask(err) 18 | } 19 | 20 | oldStatus := azureCluster.Status 21 | 22 | // ensure Ready condition 23 | err = r.ensureReadyCondition(ctx, &azureCluster) 24 | if err != nil { 25 | return microerror.Mask(err) 26 | } 27 | 28 | if !reflect.DeepEqual(oldStatus, azureCluster.Status) { 29 | r.logger.Debugf(ctx, "status is changed, updating AzureCluster CR") 30 | err = r.ctrlClient.Status().Update(ctx, &azureCluster) 31 | if apierrors.IsConflict(err) { 32 | r.logger.Debugf(ctx, "conflict trying to save object in k8s API concurrently") 33 | r.logger.Debugf(ctx, "canceling resource") 34 | return nil 35 | } else if err != nil { 36 | return microerror.Mask(err) 37 | } 38 | } 39 | 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /service/controller/azurecluster/handler/azureclusterconditions/delete.go: -------------------------------------------------------------------------------- 1 | package azureclusterconditions 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { 8 | return nil 9 | } 10 | -------------------------------------------------------------------------------- /service/controller/azurecluster/handler/azureclusterconditions/error.go: -------------------------------------------------------------------------------- 1 | package azureclusterconditions 2 | 3 | import ( 4 | "github.com/Azure/go-autorest/autorest" 5 | "github.com/giantswarm/microerror" 6 | ) 7 | 8 | var invalidConfigError = µerror.Error{ 9 | Kind: "invalidConfigError", 10 | } 11 | 12 | // IsInvalidConfig asserts invalidConfigError. 13 | func IsInvalidConfig(err error) bool { 14 | return microerror.Cause(err) == invalidConfigError 15 | } 16 | 17 | var notFoundError = µerror.Error{ 18 | Kind: "notFoundError", 19 | } 20 | 21 | // IsNotFound asserts notFoundError. 22 | func IsNotFound(err error) bool { 23 | if err == nil { 24 | return false 25 | } 26 | 27 | c := microerror.Cause(err) 28 | 29 | if c == notFoundError { 30 | return true 31 | } 32 | 33 | { 34 | dErr, ok := c.(autorest.DetailedError) 35 | if ok { 36 | if dErr.StatusCode == 404 { 37 | return true 38 | } 39 | } 40 | } 41 | 42 | return false 43 | } 44 | -------------------------------------------------------------------------------- /service/controller/azurecluster/handler/azureclusterconfig/error.go: -------------------------------------------------------------------------------- 1 | package azureclusterconfig 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/azurecluster/handler/azureclusterconfig/resource.go: -------------------------------------------------------------------------------- 1 | package azureclusterconfig 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | "github.com/giantswarm/micrologger" 6 | "sigs.k8s.io/controller-runtime/pkg/client" 7 | ) 8 | 9 | const ( 10 | Name = "azureclusterconfig" 11 | ) 12 | 13 | type Config struct { 14 | CtrlClient client.Client 15 | Logger micrologger.Logger 16 | } 17 | 18 | type Resource struct { 19 | ctrlClient client.Client 20 | logger micrologger.Logger 21 | } 22 | 23 | func New(config Config) (*Resource, error) { 24 | if config.CtrlClient == nil { 25 | return nil, microerror.Maskf(invalidConfigError, "%T.CtrlClient must not be empty", config) 26 | } 27 | if config.Logger == nil { 28 | return nil, microerror.Maskf(invalidConfigError, "%T.Logger must not be empty", config) 29 | } 30 | 31 | newResource := &Resource{ 32 | ctrlClient: config.CtrlClient, 33 | logger: config.Logger, 34 | } 35 | 36 | return newResource, nil 37 | } 38 | 39 | func (r *Resource) Name() string { 40 | return Name 41 | } 42 | -------------------------------------------------------------------------------- /service/controller/azurecluster/handler/azureclusteridentity/delete.go: -------------------------------------------------------------------------------- 1 | package azureclusteridentity 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // EnsureDeleted is a no-op. 8 | func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { 9 | return nil 10 | } 11 | -------------------------------------------------------------------------------- /service/controller/azurecluster/handler/azureclusteridentity/error.go: -------------------------------------------------------------------------------- 1 | package azureclusteridentity 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfigError asserts invalidConfigError. 12 | func IsInvalidConfigError(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/azurecluster/handler/azureclusteridentity/naming.go: -------------------------------------------------------------------------------- 1 | package azureclusteridentity 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | corev1 "k8s.io/api/core/v1" 8 | ) 9 | 10 | const ( 11 | oldNamePrefix = "credential-" 12 | newNamePrefix = "org-credential-" 13 | ) 14 | 15 | func newSecretName(legacySecret corev1.Secret) string { 16 | name := strings.TrimPrefix(legacySecret.Name, oldNamePrefix) 17 | 18 | return fmt.Sprintf("%s%s", newNamePrefix, name) 19 | } 20 | -------------------------------------------------------------------------------- /service/controller/azurecluster/handler/azureclusteridentity/resource.go: -------------------------------------------------------------------------------- 1 | package azureclusteridentity 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | "github.com/giantswarm/micrologger" 6 | ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" 7 | 8 | "github.com/giantswarm/azure-operator/v8/client" 9 | ) 10 | 11 | const ( 12 | // Name is the identifier of the resource. 13 | Name = "azureclusteridentity" 14 | ) 15 | 16 | type Config struct { 17 | AzureClientsFactory client.OrganizationFactory 18 | CtrlClient ctrlclient.Client 19 | Logger micrologger.Logger 20 | } 21 | 22 | type Resource struct { 23 | azureClientsFactory client.OrganizationFactory 24 | ctrlClient ctrlclient.Client 25 | logger micrologger.Logger 26 | } 27 | 28 | func New(config Config) (*Resource, error) { 29 | if config.CtrlClient == nil { 30 | return nil, microerror.Maskf(invalidConfigError, "%T.CtrlClient must not be empty", config) 31 | } 32 | if config.Logger == nil { 33 | return nil, microerror.Maskf(invalidConfigError, "%T.Logger must not be empty", config) 34 | } 35 | 36 | r := &Resource{ 37 | azureClientsFactory: config.AzureClientsFactory, 38 | ctrlClient: config.CtrlClient, 39 | logger: config.Logger, 40 | } 41 | 42 | return r, nil 43 | } 44 | 45 | // Name returns the resource name. 46 | func (r *Resource) Name() string { 47 | return Name 48 | } 49 | -------------------------------------------------------------------------------- /service/controller/azurecluster/handler/azureclusterupgrade/error.go: -------------------------------------------------------------------------------- 1 | package azureclusterupgrade 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/azurecluster/handler/azureconfig/apiserver.go: -------------------------------------------------------------------------------- 1 | package azureconfig 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/giantswarm/certs/v4/pkg/certs" 7 | capz "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 8 | ) 9 | 10 | func newAPIServerDomain(cr capz.AzureCluster) (string, error) { 11 | splitted := strings.Split(cr.Spec.ControlPlaneEndpoint.Host, ".") 12 | splitted[0] = certs.APICert.String() 13 | apiServerDomain := strings.Join(splitted, ".") 14 | 15 | return apiServerDomain, nil 16 | } 17 | 18 | func newEtcdServerDomain(cr capz.AzureCluster) (string, error) { 19 | splitted := strings.Split(cr.Spec.ControlPlaneEndpoint.Host, ".") 20 | splitted[0] = certs.EtcdCert.String() 21 | etcdServerDomain := strings.Join(splitted, ".") 22 | 23 | return etcdServerDomain, nil 24 | } 25 | 26 | func newKubeletDomain(cr capz.AzureCluster) (string, error) { 27 | splitted := strings.Split(cr.Spec.ControlPlaneEndpoint.Host, ".") 28 | splitted[0] = certs.WorkerCert.String() 29 | kubeletDomain := strings.Join(splitted, ".") 30 | 31 | return kubeletDomain, nil 32 | } 33 | -------------------------------------------------------------------------------- /service/controller/azurecluster/handler/azureconfig/availability_zones.go: -------------------------------------------------------------------------------- 1 | package azureconfig 2 | 3 | import ( 4 | "sort" 5 | "strconv" 6 | 7 | "github.com/giantswarm/microerror" 8 | capz "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 9 | ) 10 | 11 | func getAvailabilityZones(masters, workers []capz.AzureMachine) ([]int, error) { 12 | azs := []int{} 13 | 14 | for _, m := range append(masters, workers...) { 15 | if m.Spec.FailureDomain == nil || *m.Spec.FailureDomain == "" { 16 | continue 17 | } 18 | 19 | n, err := strconv.ParseInt(*m.Spec.FailureDomain, 10, 64) 20 | if err != nil { 21 | return nil, microerror.Mask(err) 22 | } 23 | 24 | azs = append(azs, int(n)) 25 | } 26 | 27 | azs = sortAndUniq(azs) 28 | 29 | return azs, nil 30 | } 31 | 32 | func sortAndUniq(xs []int) []int { 33 | if xs == nil { 34 | return []int{} 35 | } 36 | 37 | sort.Ints(xs) 38 | 39 | for i := 0; i < len(xs)-1; i++ { 40 | if xs[i] == xs[i+1] { 41 | xs = append(xs[:i], xs[i+1:]...) 42 | i-- 43 | } 44 | } 45 | 46 | return xs 47 | } 48 | -------------------------------------------------------------------------------- /service/controller/azurecluster/handler/subnet/template/template.go: -------------------------------------------------------------------------------- 1 | package subnet 2 | 3 | import ( 4 | _ "embed" 5 | "encoding/json" 6 | "strings" 7 | 8 | "github.com/giantswarm/microerror" 9 | ) 10 | 11 | //go:embed main.json 12 | var template string 13 | 14 | // GetARMTemplate returns the ARM template reading a json file locally using go embed. 15 | func GetARMTemplate() (map[string]interface{}, error) { 16 | contents := make(map[string]interface{}) 17 | 18 | d := json.NewDecoder(strings.NewReader(template)) 19 | if err := d.Decode(&contents); err != nil { 20 | return contents, microerror.Mask(err) 21 | } 22 | return contents, nil 23 | } 24 | -------------------------------------------------------------------------------- /service/controller/azureconfig/error.go: -------------------------------------------------------------------------------- 1 | package azureconfig 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/azureconfigfinalizer/error.go: -------------------------------------------------------------------------------- 1 | package azureconfigfinalizer 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/blobobject/delete.go: -------------------------------------------------------------------------------- 1 | package blobobject 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/operatorkit/v7/pkg/resource/crud" 7 | ) 8 | 9 | // ApplyDeleteChange not in use as blobobject deleted 10 | // with container delete. 11 | func (r *Resource) ApplyDeleteChange(ctx context.Context, obj, change interface{}) error { 12 | return nil 13 | } 14 | 15 | // NewDeletePatch is not in use as blobobject deleted 16 | // with container delete. 17 | func (r *Resource) NewDeletePatch(ctx context.Context, obj, currentState, desiredState interface{}) (*crud.Patch, error) { 18 | return nil, nil 19 | } 20 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/blobobject/error.go: -------------------------------------------------------------------------------- 1 | package blobobject 2 | 3 | import ( 4 | "github.com/Azure/go-autorest/autorest" 5 | "github.com/giantswarm/microerror" 6 | ) 7 | 8 | var invalidConfigError = µerror.Error{ 9 | Kind: "invalidConfigError", 10 | } 11 | 12 | // IsInvalidConfig asserts invalidConfigError. 13 | func IsInvalidConfig(err error) bool { 14 | return microerror.Cause(err) == invalidConfigError 15 | } 16 | 17 | var notFoundError = µerror.Error{ 18 | Kind: "notFoundError", 19 | } 20 | 21 | // IsNotFound asserts notFoundError. 22 | func IsNotFound(err error) bool { 23 | if err == nil { 24 | return false 25 | } 26 | 27 | c := microerror.Cause(err) 28 | 29 | if c == notFoundError { 30 | return true 31 | } 32 | 33 | { 34 | dErr, ok := c.(autorest.DetailedError) 35 | if ok { 36 | if dErr.StatusCode == 404 { 37 | return true 38 | } 39 | } 40 | } 41 | 42 | return false 43 | } 44 | 45 | var timeoutError = µerror.Error{ 46 | Kind: "timeoutError", 47 | } 48 | 49 | // IsTimeout asserts timeoutError. 50 | func IsTimeout(err error) bool { 51 | return microerror.Cause(err) == timeoutError 52 | } 53 | 54 | var wrongTypeError = µerror.Error{ 55 | Kind: "wrongTypeError", 56 | } 57 | 58 | // IsWrongTypeError asserts wrongTypeError. 59 | func IsWrongTypeError(err error) bool { 60 | return microerror.Cause(err) == wrongTypeError 61 | } 62 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/blobobject/mock.go: -------------------------------------------------------------------------------- 1 | package blobobject 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/azure-operator/v8/service/controller/cloudconfig" 7 | "github.com/giantswarm/azure-operator/v8/service/controller/encrypter" 8 | ) 9 | 10 | type CloudConfigMock struct { 11 | template string 12 | } 13 | 14 | func (c *CloudConfigMock) NewMasterTemplate(ctx context.Context, data cloudconfig.IgnitionTemplateData, encrypter encrypter.Interface) (string, error) { 15 | return c.template, nil 16 | } 17 | 18 | func (c *CloudConfigMock) NewWorkerTemplate(ctx context.Context, data cloudconfig.IgnitionTemplateData, encrypter encrypter.Interface) (string, error) { 19 | return c.template, nil 20 | } 21 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/blobobject/types.go: -------------------------------------------------------------------------------- 1 | package blobobject 2 | 3 | const ( 4 | prefixMaster = "master" 5 | prefixWorker = "worker" 6 | ) 7 | 8 | type ContainerObjectState struct { 9 | ContainerName string 10 | Body string 11 | Key string 12 | StorageAccountName string 13 | } 14 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/delete.go: -------------------------------------------------------------------------------- 1 | package capzcrs 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { 8 | // Once cluster has been migrated to node pools, CAPI & CAPZ CRs are 9 | // deleted by api and AzureConfig is deleted by AzureCluster reconciliation 10 | // so nothing to do here. 11 | return nil 12 | } 13 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/error.go: -------------------------------------------------------------------------------- 1 | package capzcrs 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | 16 | var invalidDomainError = µerror.Error{ 17 | Kind: "invalidDomainError", 18 | } 19 | 20 | // IsInvalidDomain asserts invalidDomainError. 21 | func IsInvalidDomain(err error) bool { 22 | return microerror.Cause(err) == invalidDomainError 23 | } 24 | 25 | var notFoundError = µerror.Error{ 26 | Kind: "notFoundError", 27 | } 28 | 29 | // IsNotFound asserts notFoundError. 30 | func IsNotFound(err error) bool { 31 | if err == nil { 32 | return false 33 | } 34 | 35 | c := microerror.Cause(err) 36 | 37 | return c == notFoundError 38 | } 39 | 40 | var tooManyCredentialsError = µerror.Error{ 41 | Kind: "tooManyCredentialsError", 42 | } 43 | 44 | // IsTooManyCredentials asserts tooManyCredentialsError. 45 | func IsTooManyCredentials(err error) bool { 46 | return microerror.Cause(err) == tooManyCredentialsError 47 | } 48 | 49 | var unknownKindError = µerror.Error{ 50 | Kind: "unknownKindError", 51 | } 52 | 53 | // IsUnknownKindError asserts unknownKindError. 54 | func IsUnknownKindError(err error) bool { 55 | return microerror.Cause(err) == unknownKindError 56 | } 57 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/resource.go: -------------------------------------------------------------------------------- 1 | package capzcrs 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | "github.com/giantswarm/micrologger" 6 | "sigs.k8s.io/controller-runtime/pkg/client" 7 | ) 8 | 9 | const ( 10 | Name = "capzcrs" 11 | ) 12 | 13 | type Config struct { 14 | CtrlClient client.Client 15 | Logger micrologger.Logger 16 | 17 | Location string 18 | } 19 | 20 | type Resource struct { 21 | ctrlClient client.Client 22 | logger micrologger.Logger 23 | 24 | location string 25 | } 26 | 27 | func New(config Config) (*Resource, error) { 28 | if config.CtrlClient == nil { 29 | return nil, microerror.Maskf(invalidConfigError, "%T.CtrlClient must not be empty", config) 30 | } 31 | if config.Logger == nil { 32 | return nil, microerror.Maskf(invalidConfigError, "%T.Logger must not be empty", config) 33 | } 34 | if config.Location == "" { 35 | return nil, microerror.Maskf(invalidConfigError, "%T.Location must not be empty", config) 36 | } 37 | 38 | newResource := &Resource{ 39 | ctrlClient: config.CtrlClient, 40 | logger: config.Logger, 41 | 42 | location: config.Location, 43 | } 44 | 45 | return newResource, nil 46 | } 47 | 48 | func (r *Resource) Name() string { 49 | return Name 50 | } 51 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/azurecluster_with_partial_vnet.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureCluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: "" 6 | labels: 7 | azure-operator.giantswarm.io/version: 4.2.0 8 | cluster.x-k8s.io/cluster-name: c6fme 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: "giantswarm" 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme 13 | namespace: org-giantswarm 14 | spec: 15 | controlPlaneEndpoint: 16 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 17 | port: 443 18 | location: westeurope 19 | networkSpec: 20 | subnets: 21 | - role: node 22 | id: nodepool-np201 23 | name: nodepool-np201 24 | cidrBlocks: 25 | - 10.100.2.0/24 26 | vnet: 27 | name: c6fme-VirtualNetwork 28 | resourceGroup: c6fme 29 | resourceGroup: "" 30 | status: 31 | ready: false 32 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/azurecluster_with_subnets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureCluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: "" 6 | labels: 7 | azure-operator.giantswarm.io/version: 4.2.0 8 | cluster.x-k8s.io/cluster-name: c6fme 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: "giantswarm" 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme 13 | namespace: org-giantswarm 14 | spec: 15 | controlPlaneEndpoint: 16 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 17 | port: 443 18 | location: westeurope 19 | networkSpec: 20 | subnets: 21 | - role: node 22 | id: nodepool-np201 23 | name: nodepool-np201 24 | cidrBlocks: 25 | - 10.100.2.0/24 26 | vnet: 27 | cidrBlocks: 28 | - 10.10.0.0/16 29 | name: c6fme-VirtualNetwork 30 | resourceGroup: c6fme 31 | resourceGroup: "" 32 | status: 33 | ready: false 34 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/azurecluster_without_vnet.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureCluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: "" 6 | labels: 7 | azure-operator.giantswarm.io/version: 4.2.0 8 | cluster.x-k8s.io/cluster-name: c6fme 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: "giantswarm" 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme 13 | namespace: org-giantswarm 14 | spec: 15 | controlPlaneEndpoint: 16 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 17 | port: 443 18 | location: westeurope 19 | networkSpec: {} 20 | resourceGroup: "" 21 | status: 22 | ready: false 23 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-0-Simple-AzureConfig-migration-to-create-CAPI-CAPZ-CRs_AzureCluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureCluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: my-test-cluster 6 | creationTimestamp: null 7 | labels: 8 | azure-operator.giantswarm.io/version: 4.2.0 9 | cluster.x-k8s.io/cluster-name: c6fme 10 | giantswarm.io/cluster: c6fme 11 | giantswarm.io/organization: giantswarm 12 | release.giantswarm.io/version: 12.0.0 13 | name: c6fme 14 | namespace: org-giantswarm 15 | resourceVersion: "1" 16 | spec: 17 | bastionSpec: {} 18 | controlPlaneEndpoint: 19 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 20 | port: 443 21 | location: westeurope 22 | networkSpec: 23 | apiServerLB: {} 24 | vnet: 25 | cidrBlocks: 26 | - 10.10.0.0/16 27 | name: c6fme-VirtualNetwork 28 | resourceGroup: c6fme 29 | resourceGroup: c6fme 30 | status: 31 | ready: false 32 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-0-Simple-AzureConfig-migration-to-create-CAPI-CAPZ-CRs_AzureMachine.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureMachine 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | azure-operator.giantswarm.io/version: 4.2.0 7 | cluster.x-k8s.io/cluster-name: c6fme 8 | cluster.x-k8s.io/control-plane: "true" 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: giantswarm 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme-master-0 13 | namespace: org-giantswarm 14 | resourceVersion: "1" 15 | spec: 16 | failureDomain: "1" 17 | image: 18 | marketplace: 19 | offer: flatcar-container-linux-free 20 | publisher: kinvolk 21 | sku: stable 22 | thirdPartyImage: false 23 | version: 2345.3.1 24 | osDisk: 25 | diskSizeGB: 50 26 | managedDisk: 27 | storageAccountType: Premium_LRS 28 | osType: Linux 29 | sshPublicKey: "" 30 | vmSize: Standard_D4s_v3 31 | status: 32 | ready: false 33 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-0-Simple-AzureConfig-migration-to-create-CAPI-CAPZ-CRs_Cluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: cluster.x-k8s.io/v1beta1 2 | kind: Cluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: my-test-cluster 6 | release.giantswarm.io/upgrading-to-node-pools: "True" 7 | creationTimestamp: null 8 | labels: 9 | azure-operator.giantswarm.io/version: 4.2.0 10 | cluster-operator.giantswarm.io/version: "" 11 | cluster.x-k8s.io/cluster-name: c6fme 12 | giantswarm.io/cluster: c6fme 13 | giantswarm.io/organization: giantswarm 14 | release.giantswarm.io/version: 12.0.0 15 | name: c6fme 16 | namespace: org-giantswarm 17 | resourceVersion: "1" 18 | spec: 19 | clusterNetwork: 20 | apiServerPort: 443 21 | serviceDomain: cluster.local 22 | services: 23 | cidrBlocks: 24 | - 172.31.0.0/16 25 | controlPlaneEndpoint: 26 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 27 | port: 443 28 | infrastructureRef: 29 | kind: AzureCluster 30 | name: c6fme 31 | namespace: org-giantswarm 32 | status: 33 | controlPlaneReady: false 34 | infrastructureReady: false 35 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-1-Simple-AzureConfig-reconciliation-to-verify-existing-CAPI-CAPZ-CRs_AzureCluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureCluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: "" 6 | creationTimestamp: null 7 | labels: 8 | azure-operator.giantswarm.io/version: 4.2.0 9 | cluster.x-k8s.io/cluster-name: c6fme 10 | giantswarm.io/cluster: c6fme 11 | giantswarm.io/organization: giantswarm 12 | release.giantswarm.io/version: 12.0.0 13 | name: c6fme 14 | namespace: org-giantswarm 15 | resourceVersion: "2" 16 | spec: 17 | bastionSpec: {} 18 | controlPlaneEndpoint: 19 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 20 | port: 443 21 | location: westeurope 22 | networkSpec: 23 | apiServerLB: {} 24 | vnet: 25 | cidrBlocks: 26 | - 10.10.0.0/16 27 | name: c6fme-VirtualNetwork 28 | resourceGroup: c6fme 29 | resourceGroup: c6fme 30 | status: 31 | ready: false 32 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-1-Simple-AzureConfig-reconciliation-to-verify-existing-CAPI-CAPZ-CRs_AzureMachine.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureMachine 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | azure-operator.giantswarm.io/version: 4.2.0 7 | cluster.x-k8s.io/cluster-name: c6fme 8 | cluster.x-k8s.io/control-plane: "true" 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: giantswarm 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme-master-0 13 | namespace: org-giantswarm 14 | resourceVersion: "1" 15 | spec: 16 | failureDomain: "1" 17 | image: 18 | marketplace: 19 | offer: flatcar-container-linux-free 20 | publisher: kinvolk 21 | sku: stable 22 | thirdPartyImage: false 23 | version: 2345.3.1 24 | osDisk: 25 | diskSizeGB: 50 26 | managedDisk: 27 | storageAccountType: Premium_LRS 28 | osType: Linux 29 | sshPublicKey: ssh-rsa foobarbaz== foobar 30 | vmSize: Standard_D4s_v3 31 | status: 32 | ready: false 33 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-1-Simple-AzureConfig-reconciliation-to-verify-existing-CAPI-CAPZ-CRs_Cluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: cluster.x-k8s.io/v1beta1 2 | kind: Cluster 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | azure-operator.giantswarm.io/version: 4.2.0 7 | cluster-operator.giantswarm.io/version: "" 8 | cluster.x-k8s.io/cluster-name: c6fme 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: giantswarm 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme 13 | namespace: org-giantswarm 14 | resourceVersion: "1" 15 | spec: 16 | clusterNetwork: 17 | apiServerPort: 443 18 | serviceDomain: c6fme.k8s.ghost.westeurope.azure.gigantic.io 19 | services: 20 | cidrBlocks: 21 | - 172.31.0.0/16 22 | controlPlaneEndpoint: 23 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 24 | port: 443 25 | infrastructureRef: 26 | kind: AzureCluster 27 | name: c6fme 28 | namespace: org-giantswarm 29 | status: 30 | controlPlaneReady: false 31 | infrastructureReady: false 32 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-2-Modified-AzureConfig-reconciliation-to-update-existing-CAPI-CAPZ-CRs_AzureCluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureCluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: "" 6 | creationTimestamp: null 7 | labels: 8 | azure-operator.giantswarm.io/version: 4.2.0 9 | cluster.x-k8s.io/cluster-name: c6fme 10 | giantswarm.io/cluster: c6fme 11 | giantswarm.io/organization: giantswarm 12 | release.giantswarm.io/version: 12.0.0 13 | name: c6fme 14 | namespace: org-giantswarm 15 | resourceVersion: "2" 16 | spec: 17 | bastionSpec: {} 18 | controlPlaneEndpoint: 19 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 20 | port: 443 21 | location: westeurope 22 | networkSpec: 23 | apiServerLB: {} 24 | vnet: 25 | cidrBlocks: 26 | - 10.10.0.0/16 27 | name: c6fme-VirtualNetwork 28 | resourceGroup: c6fme 29 | resourceGroup: c6fme 30 | status: 31 | ready: false 32 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-2-Modified-AzureConfig-reconciliation-to-update-existing-CAPI-CAPZ-CRs_AzureMachine.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureMachine 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | azure-operator.giantswarm.io/version: 4.2.0 7 | cluster.x-k8s.io/cluster-name: c6fme 8 | cluster.x-k8s.io/control-plane: "true" 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: giantswarm 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme-master-0 13 | namespace: org-giantswarm 14 | resourceVersion: "1" 15 | spec: 16 | failureDomain: "1" 17 | image: 18 | marketplace: 19 | offer: flatcar-container-linux-free 20 | publisher: kinvolk 21 | sku: stable 22 | thirdPartyImage: false 23 | version: 2345.3.1 24 | osDisk: 25 | diskSizeGB: 50 26 | managedDisk: 27 | storageAccountType: Premium_LRS 28 | osType: Linux 29 | sshPublicKey: ssh-rsa foobarbaz== foobar 30 | vmSize: Standard_D4s_v3 31 | status: 32 | ready: false 33 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-2-Modified-AzureConfig-reconciliation-to-update-existing-CAPI-CAPZ-CRs_Cluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: cluster.x-k8s.io/v1beta1 2 | kind: Cluster 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | azure-operator.giantswarm.io/version: 4.2.0 7 | cluster-operator.giantswarm.io/version: "" 8 | cluster.x-k8s.io/cluster-name: c6fme 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: giantswarm 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme 13 | namespace: org-giantswarm 14 | resourceVersion: "1" 15 | spec: 16 | clusterNetwork: 17 | apiServerPort: 443 18 | serviceDomain: c6fme.k8s.ghost.westeurope.azure.gigantic.io 19 | services: 20 | cidrBlocks: 21 | - 172.31.0.0/16 22 | controlPlaneEndpoint: 23 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 24 | port: 443 25 | infrastructureRef: 26 | kind: AzureCluster 27 | name: c6fme 28 | namespace: org-giantswarm 29 | status: 30 | controlPlaneReady: false 31 | infrastructureReady: false 32 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-3-Ensure-that-AzureCluster-Subnets-are-not-wiped_AzureCluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureCluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: "" 6 | creationTimestamp: null 7 | labels: 8 | azure-operator.giantswarm.io/version: 4.2.0 9 | cluster.x-k8s.io/cluster-name: c6fme 10 | giantswarm.io/cluster: c6fme 11 | giantswarm.io/organization: giantswarm 12 | release.giantswarm.io/version: 12.0.0 13 | name: c6fme 14 | namespace: org-giantswarm 15 | resourceVersion: "2" 16 | spec: 17 | bastionSpec: {} 18 | controlPlaneEndpoint: 19 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 20 | port: 443 21 | location: westeurope 22 | networkSpec: 23 | apiServerLB: {} 24 | subnets: 25 | - cidrBlocks: 26 | - 10.100.2.0/24 27 | id: nodepool-np201 28 | name: nodepool-np201 29 | natGateway: 30 | ip: 31 | name: "" 32 | name: "" 33 | role: node 34 | routeTable: 35 | name: "" 36 | securityGroup: 37 | name: "" 38 | vnet: 39 | cidrBlocks: 40 | - 10.10.0.0/16 41 | name: c6fme-VirtualNetwork 42 | resourceGroup: c6fme 43 | resourceGroup: c6fme 44 | status: 45 | ready: false 46 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-3-Ensure-that-AzureCluster-Subnets-are-not-wiped_AzureMachine.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureMachine 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | azure-operator.giantswarm.io/version: 4.2.0 7 | cluster.x-k8s.io/cluster-name: c6fme 8 | cluster.x-k8s.io/control-plane: "true" 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: giantswarm 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme-master-0 13 | namespace: org-giantswarm 14 | resourceVersion: "1" 15 | spec: 16 | failureDomain: "1" 17 | image: 18 | marketplace: 19 | offer: flatcar-container-linux-free 20 | publisher: kinvolk 21 | sku: stable 22 | thirdPartyImage: false 23 | version: 2345.3.1 24 | osDisk: 25 | diskSizeGB: 50 26 | managedDisk: 27 | storageAccountType: Premium_LRS 28 | osType: Linux 29 | sshPublicKey: ssh-rsa foobarbaz== foobar 30 | vmSize: Standard_D4s_v3 31 | status: 32 | ready: false 33 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-3-Ensure-that-AzureCluster-Subnets-are-not-wiped_Cluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: cluster.x-k8s.io/v1beta1 2 | kind: Cluster 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | azure-operator.giantswarm.io/version: 4.2.0 7 | cluster-operator.giantswarm.io/version: "" 8 | cluster.x-k8s.io/cluster-name: c6fme 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: giantswarm 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme 13 | namespace: org-giantswarm 14 | resourceVersion: "1" 15 | spec: 16 | clusterNetwork: 17 | apiServerPort: 443 18 | serviceDomain: c6fme.k8s.ghost.westeurope.azure.gigantic.io 19 | services: 20 | cidrBlocks: 21 | - 172.31.0.0/16 22 | controlPlaneEndpoint: 23 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 24 | port: 443 25 | infrastructureRef: 26 | kind: AzureCluster 27 | name: c6fme 28 | namespace: org-giantswarm 29 | status: 30 | controlPlaneReady: false 31 | infrastructureReady: false 32 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-4-Ensure-that-AzureCluster-VNET-gets-updated_AzureCluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureCluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: "" 6 | creationTimestamp: null 7 | labels: 8 | azure-operator.giantswarm.io/version: 4.2.0 9 | cluster.x-k8s.io/cluster-name: c6fme 10 | giantswarm.io/cluster: c6fme 11 | giantswarm.io/organization: giantswarm 12 | release.giantswarm.io/version: 12.0.0 13 | name: c6fme 14 | namespace: org-giantswarm 15 | resourceVersion: "2" 16 | spec: 17 | bastionSpec: {} 18 | controlPlaneEndpoint: 19 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 20 | port: 443 21 | location: westeurope 22 | networkSpec: 23 | apiServerLB: {} 24 | vnet: 25 | cidrBlocks: 26 | - 10.10.0.0/16 27 | name: c6fme-VirtualNetwork 28 | resourceGroup: c6fme 29 | resourceGroup: c6fme 30 | status: 31 | ready: false 32 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-4-Ensure-that-AzureCluster-VNET-gets-updated_AzureMachine.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureMachine 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | azure-operator.giantswarm.io/version: 4.2.0 7 | cluster.x-k8s.io/cluster-name: c6fme 8 | cluster.x-k8s.io/control-plane: "true" 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: giantswarm 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme-master-0 13 | namespace: org-giantswarm 14 | resourceVersion: "1" 15 | spec: 16 | failureDomain: "1" 17 | image: 18 | marketplace: 19 | offer: flatcar-container-linux-free 20 | publisher: kinvolk 21 | sku: stable 22 | thirdPartyImage: false 23 | version: 2345.3.1 24 | osDisk: 25 | diskSizeGB: 50 26 | managedDisk: 27 | storageAccountType: Premium_LRS 28 | osType: Linux 29 | sshPublicKey: ssh-rsa foobarbaz== foobar 30 | vmSize: Standard_D4s_v3 31 | status: 32 | ready: false 33 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-4-Ensure-that-AzureCluster-VNET-gets-updated_Cluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: cluster.x-k8s.io/v1beta1 2 | kind: Cluster 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | azure-operator.giantswarm.io/version: 4.2.0 7 | cluster-operator.giantswarm.io/version: "" 8 | cluster.x-k8s.io/cluster-name: c6fme 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: giantswarm 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme 13 | namespace: org-giantswarm 14 | resourceVersion: "1" 15 | spec: 16 | clusterNetwork: 17 | apiServerPort: 443 18 | serviceDomain: c6fme.k8s.ghost.westeurope.azure.gigantic.io 19 | services: 20 | cidrBlocks: 21 | - 172.31.0.0/16 22 | controlPlaneEndpoint: 23 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 24 | port: 443 25 | infrastructureRef: 26 | kind: AzureCluster 27 | name: c6fme 28 | namespace: org-giantswarm 29 | status: 30 | controlPlaneReady: false 31 | infrastructureReady: false 32 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-5-Ensure-that-AzureCluster-partial-VNET-gets-updated_AzureCluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureCluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: "" 6 | creationTimestamp: null 7 | labels: 8 | azure-operator.giantswarm.io/version: 4.2.0 9 | cluster.x-k8s.io/cluster-name: c6fme 10 | giantswarm.io/cluster: c6fme 11 | giantswarm.io/organization: giantswarm 12 | release.giantswarm.io/version: 12.0.0 13 | name: c6fme 14 | namespace: org-giantswarm 15 | resourceVersion: "2" 16 | spec: 17 | bastionSpec: {} 18 | controlPlaneEndpoint: 19 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 20 | port: 443 21 | location: westeurope 22 | networkSpec: 23 | apiServerLB: {} 24 | subnets: 25 | - cidrBlocks: 26 | - 10.100.2.0/24 27 | id: nodepool-np201 28 | name: nodepool-np201 29 | natGateway: 30 | ip: 31 | name: "" 32 | name: "" 33 | role: node 34 | routeTable: 35 | name: "" 36 | securityGroup: 37 | name: "" 38 | vnet: 39 | cidrBlocks: 40 | - 10.10.0.0/16 41 | name: c6fme-VirtualNetwork 42 | resourceGroup: c6fme 43 | resourceGroup: c6fme 44 | status: 45 | ready: false 46 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-5-Ensure-that-AzureCluster-partial-VNET-gets-updated_AzureMachine.golden: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureMachine 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | azure-operator.giantswarm.io/version: 4.2.0 7 | cluster.x-k8s.io/cluster-name: c6fme 8 | cluster.x-k8s.io/control-plane: "true" 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: giantswarm 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme-master-0 13 | namespace: org-giantswarm 14 | resourceVersion: "1" 15 | spec: 16 | failureDomain: "1" 17 | image: 18 | marketplace: 19 | offer: flatcar-container-linux-free 20 | publisher: kinvolk 21 | sku: stable 22 | thirdPartyImage: false 23 | version: 2345.3.1 24 | osDisk: 25 | diskSizeGB: 50 26 | managedDisk: 27 | storageAccountType: Premium_LRS 28 | osType: Linux 29 | sshPublicKey: ssh-rsa foobarbaz== foobar 30 | vmSize: Standard_D4s_v3 31 | status: 32 | ready: false 33 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/case-5-Ensure-that-AzureCluster-partial-VNET-gets-updated_Cluster.golden: -------------------------------------------------------------------------------- 1 | apiVersion: cluster.x-k8s.io/v1beta1 2 | kind: Cluster 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | azure-operator.giantswarm.io/version: 4.2.0 7 | cluster-operator.giantswarm.io/version: "" 8 | cluster.x-k8s.io/cluster-name: c6fme 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: giantswarm 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme 13 | namespace: org-giantswarm 14 | resourceVersion: "1" 15 | spec: 16 | clusterNetwork: 17 | apiServerPort: 443 18 | serviceDomain: c6fme.k8s.ghost.westeurope.azure.gigantic.io 19 | services: 20 | cidrBlocks: 21 | - 172.31.0.0/16 22 | controlPlaneEndpoint: 23 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 24 | port: 443 25 | infrastructureRef: 26 | kind: AzureCluster 27 | name: c6fme 28 | namespace: org-giantswarm 29 | status: 30 | controlPlaneReady: false 31 | infrastructureReady: false 32 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/simple_azurecluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureCluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: "" 6 | labels: 7 | azure-operator.giantswarm.io/version: 4.2.0 8 | cluster.x-k8s.io/cluster-name: c6fme 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: "giantswarm" 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme 13 | namespace: org-giantswarm 14 | spec: 15 | controlPlaneEndpoint: 16 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 17 | port: 443 18 | location: westeurope 19 | networkSpec: 20 | vnet: 21 | cidrBlocks: 22 | - 10.10.0.0/16 23 | name: c6fme-VirtualNetwork 24 | resourceGroup: c6fme 25 | resourceGroup: "" 26 | status: 27 | ready: false 28 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/simple_azuremachine.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureMachine 3 | metadata: 4 | labels: 5 | azure-operator.giantswarm.io/version: 4.2.0 6 | cluster.x-k8s.io/cluster-name: c6fme 7 | cluster.x-k8s.io/control-plane: "true" 8 | giantswarm.io/cluster: c6fme 9 | giantswarm.io/organization: "giantswarm" 10 | release.giantswarm.io/version: 12.0.0 11 | name: c6fme-master-0 12 | namespace: org-giantswarm 13 | spec: 14 | failureDomain: "1" 15 | image: 16 | marketplace: 17 | offer: flatcar-container-linux-free 18 | publisher: kinvolk 19 | sku: stable 20 | thirdPartyImage: false 21 | version: 2345.3.1 22 | osDisk: 23 | diskSizeGB: 50 24 | managedDisk: 25 | storageAccountType: Premium_LRS 26 | osType: Linux 27 | sshPublicKey: ssh-rsa foobarbaz== foobar 28 | vmSize: Standard_D4s_v3 29 | status: 30 | ready: false 31 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/capzcrs/testdata/simple_cluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cluster.x-k8s.io/v1beta1 2 | kind: Cluster 3 | metadata: 4 | labels: 5 | azure-operator.giantswarm.io/version: 4.2.0 6 | cluster-operator.giantswarm.io/version: "" 7 | cluster.x-k8s.io/cluster-name: c6fme 8 | giantswarm.io/cluster: c6fme 9 | giantswarm.io/organization: "giantswarm" 10 | release.giantswarm.io/version: 12.0.0 11 | name: c6fme 12 | namespace: org-giantswarm 13 | spec: 14 | clusterNetwork: 15 | apiServerPort: 443 16 | serviceDomain: c6fme.k8s.ghost.westeurope.azure.gigantic.io 17 | services: 18 | cidrBlocks: 19 | - 172.31.0.0/16 20 | controlPlaneEndpoint: 21 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 22 | port: 443 23 | infrastructureRef: 24 | kind: AzureCluster 25 | name: c6fme 26 | namespace: org-giantswarm 27 | status: 28 | infrastructureReady: false 29 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/clusterid/error.go: -------------------------------------------------------------------------------- 1 | package clusterid 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/containerurl/delete.go: -------------------------------------------------------------------------------- 1 | package containerurl 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { 8 | return nil 9 | } 10 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/deployment/checksum.go: -------------------------------------------------------------------------------- 1 | package deployment 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/json" 6 | "fmt" 7 | 8 | "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2019-05-01/resources" 9 | "github.com/giantswarm/microerror" 10 | ) 11 | 12 | func getDeploymentTemplateChecksum(deployment resources.Deployment) (string, error) { 13 | template := deployment.Properties.Template.(map[string]interface{}) 14 | jsonStr, err := json.Marshal(template) 15 | if err != nil { 16 | return "", microerror.Mask(err) 17 | } 18 | 19 | // Calculate the sha256 hash of the JSON. 20 | hash := fmt.Sprintf("%x", sha256.Sum256(jsonStr)) 21 | 22 | return hash, nil 23 | } 24 | 25 | func getDeploymentParametersChecksum(deployment resources.Deployment) (string, error) { 26 | params := deployment.Properties.Parameters.(map[string]interface{}) 27 | 28 | // Create a JSON with the whole adjusted parameters. 29 | jsonStr, err := json.Marshal(params) 30 | if err != nil { 31 | return "", microerror.Mask(err) 32 | } 33 | 34 | // Calculate the sha256 hash of the JSON. 35 | hash := fmt.Sprintf("%x", sha256.Sum256(jsonStr)) 36 | 37 | return hash, nil 38 | } 39 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/deployment/template/template.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | _ "embed" 5 | "encoding/json" 6 | "strings" 7 | 8 | "github.com/giantswarm/microerror" 9 | ) 10 | 11 | //go:embed main.json 12 | var template string 13 | 14 | // GetARMTemplate returns the ARM template reading a json file locally using go embed. 15 | func GetARMTemplate() (map[string]interface{}, error) { 16 | contents := make(map[string]interface{}) 17 | 18 | d := json.NewDecoder(strings.NewReader(template)) 19 | if err := d.Decode(&contents); err != nil { 20 | return contents, microerror.Mask(err) 21 | } 22 | return contents, nil 23 | } 24 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/dnsrecord/conv.go: -------------------------------------------------------------------------------- 1 | package dnsrecord 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | func toDNSRecords(v interface{}) (dnsRecords, error) { 8 | if v == nil { 9 | return dnsRecords{}, nil 10 | } 11 | 12 | r, ok := v.(dnsRecords) 13 | if !ok { 14 | return dnsRecords{}, microerror.Maskf(wrongTypeError, "expected '%T', got '%T'", r, v) 15 | } 16 | 17 | return r, nil 18 | } 19 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/dnsrecord/create.go: -------------------------------------------------------------------------------- 1 | package dnsrecord 2 | 3 | import ( 4 | "context" 5 | 6 | providerv1alpha1 "github.com/giantswarm/apiextensions/v6/pkg/apis/provider/v1alpha1" 7 | "github.com/giantswarm/microerror" 8 | 9 | "github.com/giantswarm/azure-operator/v8/service/controller/key" 10 | ) 11 | 12 | // ApplyCreateChange is never called. We do not like it. It is not idempotent. 13 | func (r *Resource) ApplyCreateChange(ctx context.Context, obj, change interface{}) error { 14 | cr, err := key.ToCustomResource(obj) 15 | if err != nil { 16 | return microerror.Mask(err) 17 | } 18 | 19 | c, err := toDNSRecords(change) 20 | if err != nil { 21 | return microerror.Mask(err) 22 | } 23 | 24 | return r.applyCreateChange(ctx, cr, c) 25 | } 26 | 27 | func (r *Resource) applyCreateChange(ctx context.Context, obj providerv1alpha1.AzureConfig, change dnsRecords) error { 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/dnsrecord/current.go: -------------------------------------------------------------------------------- 1 | package dnsrecord 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2018-05-01/dns" 7 | providerv1alpha1 "github.com/giantswarm/apiextensions/v6/pkg/apis/provider/v1alpha1" 8 | "github.com/giantswarm/microerror" 9 | 10 | "github.com/giantswarm/azure-operator/v8/client" 11 | "github.com/giantswarm/azure-operator/v8/service/controller/key" 12 | ) 13 | 14 | func (r *Resource) GetCurrentState(ctx context.Context, obj interface{}) (interface{}, error) { 15 | cr, err := key.ToCustomResource(obj) 16 | if err != nil { 17 | return nil, microerror.Mask(err) 18 | } 19 | 20 | return r.getCurrentState(ctx, cr) 21 | } 22 | 23 | func (r *Resource) getCurrentState(ctx context.Context, obj providerv1alpha1.AzureConfig) (dnsRecords, error) { 24 | recordSetsClient, err := r.getDNSRecordSetsGuestClient(ctx) 25 | if err != nil { 26 | return nil, microerror.Mask(err) 27 | } 28 | 29 | current := newPartialDNSRecords(obj) 30 | 31 | for i, record := range current { 32 | resp, err := recordSetsClient.Get(ctx, record.ZoneRG, record.Zone, record.RelativeName, dns.NS) 33 | if client.ResponseWasNotFound(resp.Response) { 34 | continue 35 | } else if err != nil { 36 | return nil, microerror.Mask(err) 37 | } 38 | 39 | var nameServers []string 40 | for _, ns := range *resp.NsRecords { 41 | nameServers = append(nameServers, *ns.Nsdname) 42 | } 43 | 44 | current[i].NameServers = nameServers 45 | } 46 | 47 | return current, nil 48 | } 49 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/dnsrecord/desired.go: -------------------------------------------------------------------------------- 1 | package dnsrecord 2 | 3 | import ( 4 | "context" 5 | 6 | providerv1alpha1 "github.com/giantswarm/apiextensions/v6/pkg/apis/provider/v1alpha1" 7 | "github.com/giantswarm/microerror" 8 | 9 | "github.com/giantswarm/azure-operator/v8/service/controller/key" 10 | 11 | "github.com/giantswarm/azure-operator/v8/client" 12 | ) 13 | 14 | // GetDesiredState returns the desired resource group for this cluster. 15 | func (r *Resource) GetDesiredState(ctx context.Context, obj interface{}) (interface{}, error) { 16 | cr, err := key.ToCustomResource(obj) 17 | if err != nil { 18 | return nil, microerror.Mask(err) 19 | } 20 | 21 | return r.getDesiredState(ctx, cr) 22 | } 23 | 24 | func (r *Resource) getDesiredState(ctx context.Context, obj providerv1alpha1.AzureConfig) (dnsRecords, error) { 25 | zonesClient, err := r.getDNSZonesGuestClient(ctx) 26 | if err != nil { 27 | return nil, microerror.Mask(err) 28 | } 29 | 30 | desired := newPartialDNSRecords(obj) 31 | 32 | for i, record := range desired { 33 | zone := record.RelativeName + "." + record.Zone 34 | resp, err := zonesClient.Get(ctx, key.ResourceGroupName(obj), zone) 35 | if client.ResponseWasNotFound(resp.Response) { 36 | return dnsRecords{}, nil 37 | } else if err != nil { 38 | return nil, microerror.Mask(err) 39 | } 40 | 41 | var nameServers []string 42 | nameServers = append(nameServers, *resp.NameServers...) 43 | 44 | desired[i].NameServers = nameServers 45 | } 46 | 47 | return desired, nil 48 | } 49 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/dnsrecord/error.go: -------------------------------------------------------------------------------- 1 | package dnsrecord 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | 16 | var wrongTypeError = µerror.Error{ 17 | Kind: "wrongTypeError", 18 | } 19 | 20 | // IsWrongTypeError asserts wrongTypeError. 21 | func IsWrongTypeError(err error) bool { 22 | return microerror.Cause(err) == wrongTypeError 23 | } 24 | 25 | var timeoutError = µerror.Error{ 26 | Kind: "timeoutError", 27 | } 28 | 29 | // IsTimeoutError asserts deleteTimeoutError. 30 | func IsTimeoutError(err error) bool { 31 | return microerror.Cause(err) == timeoutError 32 | } 33 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/encryptionkey/delete.go: -------------------------------------------------------------------------------- 1 | package encryptionkey 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/microerror" 7 | "k8s.io/apimachinery/pkg/api/errors" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | 10 | "github.com/giantswarm/azure-operator/v8/service/controller/key" 11 | ) 12 | 13 | func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { 14 | cr, err := key.ToCustomResource(obj) 15 | if err != nil { 16 | return microerror.Mask(err) 17 | } 18 | 19 | { 20 | r.logger.Debugf(ctx, "deleting encryptionkey secret upon delete event") 21 | 22 | err = r.k8sClient.CoreV1().Secrets(key.CertificateEncryptionNamespace).Delete(ctx, key.CertificateEncryptionSecretName(&cr), metav1.DeleteOptions{}) 23 | if errors.IsNotFound(err) { 24 | r.logger.Debugf(ctx, "encryptionkey secret already deleted") 25 | } else if err != nil { 26 | return microerror.Mask(err) 27 | } else { 28 | r.logger.Debugf(ctx, "deleted encryptionkey secret upon delete event") 29 | } 30 | } 31 | 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/encryptionkey/error.go: -------------------------------------------------------------------------------- 1 | package encryptionkey 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/encryptionkey/resource.go: -------------------------------------------------------------------------------- 1 | package encryptionkey 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | "github.com/giantswarm/micrologger" 6 | "k8s.io/client-go/kubernetes" 7 | ) 8 | 9 | const ( 10 | keySize = 32 11 | Name = "encryptionkey" 12 | ) 13 | 14 | type Config struct { 15 | K8sClient kubernetes.Interface 16 | Logger micrologger.Logger 17 | ProjectName string 18 | } 19 | 20 | type Resource struct { 21 | k8sClient kubernetes.Interface 22 | logger micrologger.Logger 23 | projectName string 24 | } 25 | 26 | func New(config Config) (*Resource, error) { 27 | if config.K8sClient == nil { 28 | return nil, microerror.Maskf(invalidConfigError, "%T.K8sClient must not be empty", config) 29 | } 30 | if config.Logger == nil { 31 | return nil, microerror.Maskf(invalidConfigError, "%T.Logger must not be empty", config) 32 | } 33 | if config.ProjectName == "" { 34 | return nil, microerror.Maskf(invalidConfigError, "%T.ProjectName must not be empty", config) 35 | } 36 | 37 | newResource := &Resource{ 38 | k8sClient: config.K8sClient, 39 | logger: config.Logger, 40 | projectName: config.ProjectName, 41 | } 42 | 43 | return newResource, nil 44 | } 45 | 46 | func (r *Resource) Name() string { 47 | return Name 48 | } 49 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/endpoints/resource.go: -------------------------------------------------------------------------------- 1 | package endpoints 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | "github.com/giantswarm/micrologger" 6 | corev1 "k8s.io/api/core/v1" 7 | "k8s.io/client-go/kubernetes" 8 | ) 9 | 10 | const ( 11 | Name = "endpoints" 12 | 13 | httpsPort = 443 14 | ) 15 | 16 | type Config struct { 17 | K8sClient kubernetes.Interface 18 | Logger micrologger.Logger 19 | } 20 | 21 | type Resource struct { 22 | k8sClient kubernetes.Interface 23 | logger micrologger.Logger 24 | } 25 | 26 | func New(config Config) (*Resource, error) { 27 | if config.K8sClient == nil { 28 | return nil, microerror.Maskf(invalidConfigError, "%T.K8sClient must not be empty", config) 29 | } 30 | if config.Logger == nil { 31 | return nil, microerror.Maskf(invalidConfigError, "%t.Logger must not be empty", config) 32 | } 33 | 34 | r := &Resource{ 35 | k8sClient: config.K8sClient, 36 | logger: config.Logger, 37 | } 38 | 39 | return r, nil 40 | } 41 | 42 | func (r *Resource) Name() string { 43 | return Name 44 | } 45 | 46 | func toEndpoints(v interface{}) (*corev1.Endpoints, error) { 47 | if v == nil { 48 | return nil, nil 49 | } 50 | 51 | endpoints, ok := v.(*corev1.Endpoints) 52 | if !ok { 53 | return nil, microerror.Maskf(wrongTypeError, "expected '%T', got '%T'", endpoints, v) 54 | } 55 | 56 | return endpoints, nil 57 | } 58 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/masters/create_empty.go: -------------------------------------------------------------------------------- 1 | package masters 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/azure-operator/v8/pkg/handler/nodes/state" 7 | ) 8 | 9 | func (r *Resource) emptyStateTransition(ctx context.Context, obj interface{}, currentState state.State) (state.State, error) { 10 | return DeploymentUninitialized, nil 11 | } 12 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/masters/create_provisioning_successful.go: -------------------------------------------------------------------------------- 1 | package masters 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/azure-operator/v8/pkg/handler/nodes/state" 7 | ) 8 | 9 | func (r *Resource) provisioningSuccessfulTransition(ctx context.Context, obj interface{}, currentState state.State) (state.State, error) { 10 | r.Logger.Debugf(ctx, "Master VMSS deployment successfully provisioned") 11 | return ClusterUpgradeRequirementCheck, nil 12 | } 13 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/masters/delete.go: -------------------------------------------------------------------------------- 1 | package masters 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/microerror" 7 | "k8s.io/apimachinery/pkg/api/errors" 8 | "k8s.io/apimachinery/pkg/api/meta" 9 | capz "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 10 | capi "sigs.k8s.io/cluster-api/api/v1beta1" 11 | "sigs.k8s.io/controller-runtime/pkg/client" 12 | 13 | "github.com/giantswarm/azure-operator/v8/service/controller/key" 14 | ) 15 | 16 | func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { 17 | m, err := meta.Accessor(obj) 18 | if err != nil { 19 | return microerror.Mask(err) 20 | } 21 | 22 | // Ensure that AzureMachines for the cluster are deleted. 23 | { 24 | o := client.MatchingLabels{ 25 | capi.ClusterLabelName: key.ClusterID(m), 26 | } 27 | mList := new(capz.AzureMachineList) 28 | err = r.ctrlClient.List(ctx, mList, o) 29 | if err != nil { 30 | return microerror.Mask(err) 31 | } 32 | 33 | for i := range mList.Items { 34 | err = r.ctrlClient.Delete(ctx, &mList.Items[i]) 35 | if errors.IsNotFound(err) { 36 | continue 37 | } else if err != nil { 38 | return microerror.Mask(err) 39 | } 40 | } 41 | } 42 | 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/masters/status.go: -------------------------------------------------------------------------------- 1 | package masters 2 | 3 | const ( 4 | // Types 5 | Stage = "Stage" 6 | DeploymentTemplateChecksum = "TemplateChecksum" 7 | DeploymentParametersChecksum = "ParametersChecksum" 8 | 9 | // States 10 | ClusterUpgradeRequirementCheck = "ClusterUpgradeRequirementCheck" 11 | DeploymentUninitialized = "DeploymentUninitialized" 12 | DeploymentInitialized = "DeploymentInitialized" 13 | DeploymentCompleted = "DeploymentCompleted" 14 | Empty = "" 15 | MasterInstancesUpgrading = "MasterInstancesUpgrading" 16 | ProvisioningSuccessful = "ProvisioningSuccessful" 17 | ) 18 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/masters/template/template.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | _ "embed" 5 | "encoding/json" 6 | "strings" 7 | 8 | "github.com/giantswarm/microerror" 9 | ) 10 | 11 | //go:embed main.json 12 | var template string 13 | 14 | // GetARMTemplate returns the ARM template reading a json file locally using go embed. 15 | func GetARMTemplate() (map[string]interface{}, error) { 16 | contents := make(map[string]interface{}) 17 | 18 | d := json.NewDecoder(strings.NewReader(template)) 19 | if err := d.Decode(&contents); err != nil { 20 | return contents, microerror.Mask(err) 21 | } 22 | return contents, nil 23 | } 24 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/namespace/current_test.go: -------------------------------------------------------------------------------- 1 | package namespace 2 | 3 | import ( 4 | "context" 5 | "reflect" 6 | "testing" 7 | 8 | corev1 "k8s.io/api/core/v1" 9 | "k8s.io/client-go/kubernetes/fake" 10 | 11 | "github.com/giantswarm/apiextensions/v6/pkg/apis/provider/v1alpha1" 12 | "github.com/giantswarm/micrologger/microloggertest" 13 | ) 14 | 15 | func Test_Resource_Namespace_GetCurrentState(t *testing.T) { 16 | t.Parallel() 17 | testCases := []struct { 18 | Obj interface{} 19 | ExpectedNamespace *corev1.Namespace 20 | }{ 21 | { 22 | Obj: &v1alpha1.AzureConfig{ 23 | Spec: v1alpha1.AzureConfigSpec{ 24 | Cluster: v1alpha1.Cluster{ 25 | ID: "al9qy", 26 | }, 27 | }, 28 | }, 29 | ExpectedNamespace: nil, 30 | }, 31 | } 32 | 33 | var err error 34 | var newResource *Resource 35 | { 36 | c := Config{ 37 | K8sClient: fake.NewSimpleClientset(), 38 | Logger: microloggertest.New(), 39 | } 40 | newResource, err = New(c) 41 | if err != nil { 42 | t.Fatal("expected", nil, "got", err) 43 | } 44 | } 45 | 46 | for i, tc := range testCases { 47 | result, err := newResource.GetCurrentState(context.TODO(), tc.Obj) 48 | if err != nil { 49 | t.Fatal("case", i+1, "expected", nil, "got", err) 50 | } 51 | if !reflect.DeepEqual(tc.ExpectedNamespace, result) { 52 | t.Fatalf("case %d expected %#v got %#v", i+1, tc.ExpectedNamespace, result) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/namespace/desired.go: -------------------------------------------------------------------------------- 1 | package namespace 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/microerror" 7 | corev1 "k8s.io/api/core/v1" 8 | apismetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | 10 | "github.com/giantswarm/azure-operator/v8/service/controller/key" 11 | ) 12 | 13 | func (r *Resource) GetDesiredState(ctx context.Context, obj interface{}) (interface{}, error) { 14 | cr, err := key.ToCustomResource(obj) 15 | if err != nil { 16 | return nil, microerror.Mask(err) 17 | } 18 | 19 | namespace := &corev1.Namespace{ 20 | TypeMeta: apismetav1.TypeMeta{ 21 | Kind: "Namespace", 22 | APIVersion: "v1", 23 | }, 24 | ObjectMeta: apismetav1.ObjectMeta{ 25 | Name: key.ClusterNamespace(cr), 26 | Labels: map[string]string{ 27 | key.LabelApp: "master", 28 | key.LegacyLabelCluster: key.ClusterID(&cr), 29 | key.LabelCustomer: key.ClusterCustomer(cr), 30 | key.LabelCluster: key.ClusterID(&cr), 31 | key.LabelOrganization: key.ClusterCustomer(cr), 32 | }, 33 | }, 34 | } 35 | 36 | return namespace, nil 37 | } 38 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/namespace/error.go: -------------------------------------------------------------------------------- 1 | package namespace 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | 16 | var notFoundError = µerror.Error{ 17 | Kind: "notFoundError", 18 | } 19 | 20 | // IsNotFound asserts notFoundError. 21 | func IsNotFound(err error) bool { 22 | return microerror.Cause(err) == notFoundError 23 | } 24 | 25 | var wrongTypeError = µerror.Error{ 26 | Kind: "wrongTypeError", 27 | } 28 | 29 | // IsWrongTypeError asserts wrongTypeError. 30 | func IsWrongTypeError(err error) bool { 31 | return microerror.Cause(err) == wrongTypeError 32 | } 33 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/namespace/resource.go: -------------------------------------------------------------------------------- 1 | package namespace 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | "github.com/giantswarm/micrologger" 6 | corev1 "k8s.io/api/core/v1" 7 | "k8s.io/client-go/kubernetes" 8 | ) 9 | 10 | const ( 11 | Name = "namespace" 12 | ) 13 | 14 | type Config struct { 15 | K8sClient kubernetes.Interface 16 | Logger micrologger.Logger 17 | } 18 | 19 | type Resource struct { 20 | k8sClient kubernetes.Interface 21 | logger micrologger.Logger 22 | } 23 | 24 | func New(config Config) (*Resource, error) { 25 | if config.K8sClient == nil { 26 | return nil, microerror.Maskf(invalidConfigError, "%T.K8sClient must not be empty", config) 27 | } 28 | if config.Logger == nil { 29 | return nil, microerror.Maskf(invalidConfigError, "%T.Logger must not be empty", config) 30 | } 31 | 32 | r := &Resource{ 33 | k8sClient: config.K8sClient, 34 | logger: config.Logger, 35 | } 36 | 37 | return r, nil 38 | } 39 | 40 | func (r *Resource) Name() string { 41 | return Name 42 | } 43 | 44 | func toNamespace(v interface{}) (*corev1.Namespace, error) { 45 | if v == nil { 46 | return nil, nil 47 | } 48 | 49 | namespace, ok := v.(*corev1.Namespace) 50 | if !ok { 51 | return nil, microerror.Maskf(wrongTypeError, "expected '%T', got '%T'", &corev1.Namespace{}, v) 52 | } 53 | 54 | return namespace, nil 55 | } 56 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/namespace/update.go: -------------------------------------------------------------------------------- 1 | package namespace 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/microerror" 7 | "github.com/giantswarm/operatorkit/v7/pkg/resource/crud" 8 | ) 9 | 10 | func (r *Resource) ApplyUpdateChange(ctx context.Context, obj, updateChange interface{}) error { 11 | return nil 12 | } 13 | 14 | func (r *Resource) NewUpdatePatch(ctx context.Context, obj, currentState, desiredState interface{}) (*crud.Patch, error) { 15 | create, err := r.newCreateChange(currentState, desiredState) 16 | if err != nil { 17 | return nil, microerror.Mask(err) 18 | } 19 | 20 | update, err := r.newUpdateChange(ctx, obj, currentState, desiredState) 21 | if err != nil { 22 | return nil, microerror.Mask(err) 23 | } 24 | 25 | patch := crud.NewPatch() 26 | patch.SetCreateChange(create) 27 | patch.SetUpdateChange(update) 28 | 29 | return patch, nil 30 | } 31 | 32 | // newUpdateChange is a no-op because Kubernetes namespaces are not updated. 33 | func (r *Resource) newUpdateChange(ctx context.Context, obj, currentState, desiredState interface{}) (interface{}, error) { 34 | return nil, nil 35 | } 36 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/resourcegroup/error.go: -------------------------------------------------------------------------------- 1 | package resourcegroup 2 | 3 | import ( 4 | "github.com/Azure/go-autorest/autorest" 5 | "github.com/giantswarm/microerror" 6 | ) 7 | 8 | var invalidConfigError = µerror.Error{ 9 | Kind: "invalidConfigError", 10 | } 11 | 12 | // IsInvalidConfig asserts invalidConfigError. 13 | func IsInvalidConfig(err error) bool { 14 | return microerror.Cause(err) == invalidConfigError 15 | } 16 | 17 | var notFoundError = µerror.Error{ 18 | Kind: "notFoundError", 19 | } 20 | 21 | // IsNotFound asserts notFoundError. 22 | func IsNotFound(err error) bool { 23 | if err == nil { 24 | return false 25 | } 26 | 27 | c := microerror.Cause(err) 28 | 29 | if c == notFoundError { 30 | return true 31 | } 32 | 33 | { 34 | dErr, ok := c.(autorest.DetailedError) 35 | if ok { 36 | if dErr.StatusCode == 404 { 37 | return true 38 | } 39 | } 40 | } 41 | 42 | return false 43 | } 44 | 45 | var wrongTypeError = µerror.Error{ 46 | Kind: "wrongTypeError", 47 | } 48 | 49 | // IsWrongTypeError asserts wrongTypeError. 50 | func IsWrongTypeError(err error) bool { 51 | return microerror.Cause(err) == wrongTypeError 52 | } 53 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/resourcegroup/spec.go: -------------------------------------------------------------------------------- 1 | package resourcegroup 2 | 3 | // Group defines an Azure Resource Group. 4 | type Group struct { 5 | Name string 6 | Location string 7 | Tags map[string]string 8 | } 9 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/service/current.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/microerror" 7 | corev1 "k8s.io/api/core/v1" 8 | apierrors "k8s.io/apimachinery/pkg/api/errors" 9 | apismetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | 11 | "github.com/giantswarm/azure-operator/v8/service/controller/key" 12 | ) 13 | 14 | func (r *Resource) GetCurrentState(ctx context.Context, obj interface{}) (interface{}, error) { 15 | cr, err := key.ToCustomResource(obj) 16 | if err != nil { 17 | return nil, microerror.Mask(err) 18 | } 19 | 20 | r.logger.Debugf(ctx, "looking for the master service in the Kubernetes API") 21 | 22 | namespace := key.ClusterNamespace(cr) 23 | 24 | // Lookup the current state of the service. 25 | var service *corev1.Service 26 | { 27 | manifest, err := r.k8sClient.CoreV1().Services(namespace).Get(ctx, masterServiceName, apismetav1.GetOptions{}) 28 | if apierrors.IsNotFound(err) { 29 | r.logger.Debugf(ctx, "did not find the master service in the Kubernetes API") 30 | // fall through 31 | } else if err != nil { 32 | return nil, microerror.Mask(err) 33 | } else { 34 | r.logger.Debugf(ctx, "found the master service in the Kubernetes API") 35 | service = manifest 36 | } 37 | } 38 | 39 | return service, nil 40 | } 41 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/service/desired.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/microerror" 7 | v1 "k8s.io/api/core/v1" 8 | apismetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | "k8s.io/apimachinery/pkg/util/intstr" 10 | 11 | "github.com/giantswarm/azure-operator/v8/service/controller/key" 12 | ) 13 | 14 | func (r *Resource) GetDesiredState(ctx context.Context, obj interface{}) (interface{}, error) { 15 | cr, err := key.ToCustomResource(obj) 16 | if err != nil { 17 | return nil, microerror.Mask(err) 18 | } 19 | 20 | service := &v1.Service{ 21 | ObjectMeta: apismetav1.ObjectMeta{ 22 | Name: "master", 23 | Namespace: key.ClusterID(&cr), 24 | Labels: map[string]string{ 25 | key.LabelApp: "master", 26 | key.LegacyLabelCluster: key.ClusterID(&cr), 27 | key.LabelCustomer: key.ClusterCustomer(cr), 28 | key.LabelCluster: key.ClusterID(&cr), 29 | key.LabelOrganization: key.ClusterCustomer(cr), 30 | }, 31 | Annotations: map[string]string{ 32 | key.AnnotationPrometheusCluster: key.ClusterID(&cr), 33 | key.AnnotationEtcdDomain: key.ClusterEtcdDomain(cr), 34 | }, 35 | }, 36 | Spec: v1.ServiceSpec{ 37 | Ports: []v1.ServicePort{ 38 | { 39 | Protocol: v1.ProtocolTCP, 40 | Port: httpsPort, 41 | TargetPort: intstr.FromInt(httpsPort), 42 | }, 43 | }, 44 | }, 45 | } 46 | 47 | return service, nil 48 | } 49 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/service/error.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import "github.com/giantswarm/microerror" 4 | 5 | var invalidConfigError = µerror.Error{ 6 | Kind: "invalidConfigError", 7 | } 8 | 9 | // IsInvalidConfig asserts invalidConfigError. 10 | func IsInvalidConfig(err error) bool { 11 | return microerror.Cause(err) == invalidConfigError 12 | } 13 | 14 | var notFoundError = µerror.Error{ 15 | Kind: "notFoundError", 16 | } 17 | 18 | // IsNotFound asserts notFoundError. 19 | func IsNotFound(err error) bool { 20 | return microerror.Cause(err) == notFoundError 21 | } 22 | 23 | var wrongTypeError = µerror.Error{ 24 | Kind: "wrongTypeError", 25 | } 26 | 27 | // IsWrongTypeError asserts wrongTypeError. 28 | func IsWrongTypeError(err error) bool { 29 | return microerror.Cause(err) == wrongTypeError 30 | } 31 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/storageclassmigrator/error.go: -------------------------------------------------------------------------------- 1 | package storageclassmigrator 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/vnetpeering/error.go: -------------------------------------------------------------------------------- 1 | package vnetpeering 2 | 3 | import ( 4 | "github.com/Azure/go-autorest/autorest" 5 | "github.com/giantswarm/microerror" 6 | ) 7 | 8 | var invalidConfigError = µerror.Error{ 9 | Kind: "invalidConfigError", 10 | } 11 | 12 | // IsInvalidConfig asserts invalidConfigError. 13 | func IsInvalidConfig(err error) bool { 14 | return microerror.Cause(err) == invalidConfigError 15 | } 16 | 17 | var notFoundError = µerror.Error{ 18 | Kind: "notFoundError", 19 | } 20 | 21 | // IsNotFound asserts notFoundError. 22 | func IsNotFound(err error) bool { 23 | if err == nil { 24 | return false 25 | } 26 | 27 | c := microerror.Cause(err) 28 | 29 | if c == notFoundError { 30 | return true 31 | } 32 | 33 | { 34 | dErr, ok := c.(autorest.DetailedError) 35 | if ok { 36 | if dErr.StatusCode == 404 { 37 | return true 38 | } 39 | } 40 | } 41 | 42 | return false 43 | } 44 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/workermigration/delete.go: -------------------------------------------------------------------------------- 1 | package workermigration 2 | 3 | import "context" 4 | 5 | // EnsureDeleted is no-op. 6 | func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { 7 | return nil 8 | } 9 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/workermigration/internal/azure/error.go: -------------------------------------------------------------------------------- 1 | package azure 2 | 3 | import ( 4 | "github.com/Azure/go-autorest/autorest" 5 | "github.com/giantswarm/microerror" 6 | ) 7 | 8 | // IsNotFound asserts generic Azure API not found error. 9 | func IsNotFound(err error) bool { 10 | if err == nil { 11 | return false 12 | } 13 | 14 | c := microerror.Cause(err) 15 | 16 | { 17 | dErr, ok := c.(autorest.DetailedError) 18 | if ok { 19 | if dErr.StatusCode == 404 { 20 | return true 21 | } 22 | } 23 | } 24 | 25 | return false 26 | } 27 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/workermigration/internal/azure/spec.go: -------------------------------------------------------------------------------- 1 | package azure 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" 7 | "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-11-01/network" 8 | ) 9 | 10 | type VMSS *compute.VirtualMachineScaleSet 11 | type VMSSNodes []compute.VirtualMachineScaleSetVM 12 | type SecurityGroups []network.SecurityGroup 13 | 14 | type API interface { 15 | // GetVMSS gets VMSS metadata from Azure API. 16 | GetVMSS(ctx context.Context, resourceGroupName, vmssName string) (VMSS, error) 17 | 18 | // DeleteDeployment deletes the corresponding deployment via Azure API. 19 | DeleteDeployment(ctx context.Context, resourceGroupName, deploymentName string) error 20 | 21 | // DeleteVMSS deletes the corresponding VMSS via Azure API. 22 | DeleteVMSS(ctx context.Context, resourceGroupName, vmssName string) error 23 | 24 | // ListVMSSNodes lists VMs in given VMSS via Azure API. 25 | ListVMSSNodes(ctx context.Context, resourceGroupName, vmssName string) (VMSSNodes, error) 26 | 27 | // ListNetworkSecurityGroups lists all network security groups in given resource group via Azure API. 28 | ListNetworkSecurityGroups(ctx context.Context, resourceGroupName string) (SecurityGroups, error) 29 | 30 | // CreateOrUpdateNetworkSecurityGroup creates or updates existing network security group via Azure API. 31 | CreateOrUpdateNetworkSecurityGroup(ctx context.Context, resourceGroupName, networkSecurityGroupName string, securityGroup network.SecurityGroup) error 32 | } 33 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/workermigration/testdata/azurecluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureCluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: "dummy cluster" 6 | labels: 7 | azure-operator.giantswarm.io/version: 4.2.0 8 | cluster.x-k8s.io/cluster-name: c6fme 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: giantswarm 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme 13 | namespace: org-giantswarm 14 | spec: 15 | controlPlaneEndpoint: 16 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 17 | port: 443 18 | location: westeurope 19 | networkSpec: 20 | vnet: 21 | cidrBlocks: 22 | - 10.10.0.0/16 23 | name: c6fme-VirtualNetwork 24 | resourceGroup: c6fme 25 | resourceGroup: "" 26 | status: 27 | ready: false 28 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/workermigration/testdata/azuremachinepool.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureMachinePool 3 | metadata: 4 | labels: 5 | azure-operator.giantswarm.io/version: 4.2.0 6 | cluster.x-k8s.io/cluster-name: c6fme 7 | giantswarm.io/cluster: c6fme 8 | giantswarm.io/machine-pool: c6fme 9 | giantswarm.io/organization: giantswarm 10 | release.giantswarm.io/version: 12.0.0 11 | name: c6fme 12 | namespace: org-giantswarm 13 | spec: 14 | location: westeurope 15 | template: 16 | osDisk: 17 | diskSizeGB: 0 18 | managedDisk: 19 | storageAccountType: "" 20 | osType: "" 21 | sshPublicKey: abcdefg 22 | vmSize: Standard_D4s_v3 23 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/workermigration/testdata/cluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cluster.x-k8s.io/v1beta1 2 | kind: Cluster 3 | metadata: 4 | labels: 5 | azure-operator.giantswarm.io/version: 4.2.0 6 | cluster-operator.giantswarm.io/version: "" 7 | cluster.x-k8s.io/cluster-name: c6fme 8 | giantswarm.io/cluster: c6fme 9 | giantswarm.io/organization: giantswarm 10 | release.giantswarm.io/version: 12.0.0 11 | name: c6fme 12 | namespace: org-giantswarm 13 | spec: 14 | clusterNetwork: 15 | apiServerPort: 443 16 | serviceDomain: c6fme.k8s.ghost.westeurope.azure.gigantic.io 17 | services: 18 | cidrBlocks: 19 | - 172.31.0.0/16 20 | controlPlaneEndpoint: 21 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 22 | port: 443 23 | infrastructureRef: 24 | kind: AzureCluster 25 | name: c6fme 26 | namespace: default 27 | status: 28 | infrastructureReady: false 29 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/workermigration/testdata/machinepool.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cluster.x-k8s.io/v1beta1 2 | kind: MachinePool 3 | metadata: 4 | annotations: 5 | machine-pool.giantswarm.io/name: migrated built-in workers 6 | labels: 7 | azure-operator.giantswarm.io/version: 4.2.0 8 | cluster.x-k8s.io/cluster-name: c6fme 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/machine-pool: c6fme 11 | giantswarm.io/organization: giantswarm 12 | release.giantswarm.io/version: 12.0.0 13 | name: c6fme 14 | namespace: org-giantswarm 15 | spec: 16 | clusterName: c6fme 17 | failureDomains: 18 | - "1" 19 | replicas: 3 20 | template: 21 | metadata: {} 22 | spec: 23 | bootstrap: 24 | configRef: 25 | apiVersion: core.giantswarm.io/v1alpha1 26 | kind: Spark 27 | name: c6fme 28 | namespace: default 29 | clusterName: c6fme 30 | infrastructureRef: 31 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 32 | kind: AzureMachinePool 33 | name: c6fme 34 | namespace: default 35 | status: {} 36 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/workermigration/testdata/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | items: 3 | - apiVersion: v1 4 | kind: Namespace 5 | metadata: 6 | labels: 7 | app: master 8 | cluster: c6fme 9 | customer: giantswarm 10 | giantswarm.io/cluster: c6fme 11 | giantswarm.io/organization: giantswarm 12 | name: c6fme 13 | spec: 14 | finalizers: 15 | - kubernetes 16 | - apiVersion: v1 17 | kind: Namespace 18 | metadata: 19 | labels: 20 | customer: giantswarm 21 | giantswarm.io/organization: giantswarm 22 | name: giantswarm 23 | spec: 24 | finalizers: 25 | - kubernetes 26 | - apiVersion: v1 27 | kind: Namespace 28 | metadata: 29 | labels: 30 | customer: giantswarm 31 | giantswarm.io/organization: giantswarm 32 | name: org-giantswarm 33 | spec: 34 | finalizers: 35 | - kubernetes 36 | kind: NamespaceList 37 | -------------------------------------------------------------------------------- /service/controller/azureconfig/handler/workermigration/testdata/spark.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.giantswarm.io/v1alpha1 2 | kind: Spark 3 | metadata: 4 | labels: 5 | cluster.x-k8s.io/cluster-name: c6fme 6 | giantswarm.io/cluster: c6fme 7 | release.giantswarm.io/version: 12.0.0 8 | name: c6fme 9 | namespace: org-giantswarm 10 | spec: {} 11 | -------------------------------------------------------------------------------- /service/controller/azuremachine/error.go: -------------------------------------------------------------------------------- 1 | package azuremachine 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/azuremachine/handler/azuremachineconditions/create.go: -------------------------------------------------------------------------------- 1 | package azuremachineconditions 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/microerror" 7 | apierrors "k8s.io/apimachinery/pkg/api/errors" 8 | 9 | "github.com/giantswarm/azure-operator/v8/service/controller/key" 10 | ) 11 | 12 | func (r *Resource) EnsureCreated(ctx context.Context, cr interface{}) error { 13 | var err error 14 | azureMachine, err := key.ToAzureMachine(cr) 15 | if err != nil { 16 | return microerror.Mask(err) 17 | } 18 | 19 | // ensure Ready condition 20 | err = r.ensureReadyCondition(ctx, &azureMachine) 21 | if err != nil { 22 | return microerror.Mask(err) 23 | } 24 | 25 | // ensure Creating condition 26 | err = r.creatingConditionHandler.EnsureCreated(ctx, &azureMachine) 27 | if err != nil { 28 | return microerror.Mask(err) 29 | } 30 | 31 | // ensure Upgrading condition 32 | err = r.upgradingConditionHandler.EnsureCreated(ctx, &azureMachine) 33 | if err != nil { 34 | return microerror.Mask(err) 35 | } 36 | 37 | err = r.ctrlClient.Status().Update(ctx, &azureMachine) 38 | if apierrors.IsConflict(err) { 39 | r.logger.Debugf(ctx, "conflict trying to save object in k8s API concurrently") 40 | r.logger.Debugf(ctx, "cancelling resource") 41 | return nil 42 | } else if err != nil { 43 | return microerror.Mask(err) 44 | } 45 | 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /service/controller/azuremachine/handler/azuremachineconditions/delete.go: -------------------------------------------------------------------------------- 1 | package azuremachineconditions 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { 8 | return nil 9 | } 10 | -------------------------------------------------------------------------------- /service/controller/azuremachine/handler/azuremachineconditions/error.go: -------------------------------------------------------------------------------- 1 | package azuremachineconditions 2 | 3 | import ( 4 | "github.com/Azure/go-autorest/autorest" 5 | "github.com/giantswarm/microerror" 6 | ) 7 | 8 | var invalidConfigError = µerror.Error{ 9 | Kind: "invalidConfigError", 10 | } 11 | 12 | // IsInvalidConfig asserts invalidConfigError. 13 | func IsInvalidConfig(err error) bool { 14 | return microerror.Cause(err) == invalidConfigError 15 | } 16 | 17 | var notFoundError = µerror.Error{ 18 | Kind: "notFoundError", 19 | } 20 | 21 | // IsNotFound asserts notFoundError. 22 | func IsNotFound(err error) bool { 23 | if err == nil { 24 | return false 25 | } 26 | 27 | c := microerror.Cause(err) 28 | 29 | if c == notFoundError { 30 | return true 31 | } 32 | 33 | { 34 | dErr, ok := c.(autorest.DetailedError) 35 | if ok { 36 | if dErr.StatusCode == 404 { 37 | return true 38 | } 39 | } 40 | } 41 | 42 | return false 43 | } 44 | -------------------------------------------------------------------------------- /service/controller/azuremachine/handler/azuremachinemetadata/delete.go: -------------------------------------------------------------------------------- 1 | package azuremachinemetadata 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { 8 | return nil 9 | } 10 | -------------------------------------------------------------------------------- /service/controller/azuremachine/handler/azuremachinemetadata/error.go: -------------------------------------------------------------------------------- 1 | package azuremachinemetadata 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/azuremachine/handler/azuremachinemetadata/resource.go: -------------------------------------------------------------------------------- 1 | package azuremachinemetadata 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | "github.com/giantswarm/micrologger" 6 | "sigs.k8s.io/controller-runtime/pkg/client" 7 | ) 8 | 9 | const ( 10 | // Name is the identifier of the resource. 11 | Name = "azuremachinemetadata" 12 | ) 13 | 14 | type Config struct { 15 | CtrlClient client.Client 16 | Logger micrologger.Logger 17 | } 18 | 19 | // Resource ensures that AzureMachinePool Status Conditions are set. 20 | type Resource struct { 21 | ctrlClient client.Client 22 | logger micrologger.Logger 23 | } 24 | 25 | func New(config Config) (*Resource, error) { 26 | if config.CtrlClient == nil { 27 | return nil, microerror.Maskf(invalidConfigError, "%T.CtrlClient must not be empty", config) 28 | } 29 | if config.Logger == nil { 30 | return nil, microerror.Maskf(invalidConfigError, "%T.Logger must not be empty", config) 31 | } 32 | 33 | r := &Resource{ 34 | ctrlClient: config.CtrlClient, 35 | logger: config.Logger, 36 | } 37 | 38 | return r, nil 39 | } 40 | 41 | // Name returns the resource name. 42 | func (r *Resource) Name() string { 43 | return Name 44 | } 45 | -------------------------------------------------------------------------------- /service/controller/azuremachinepool/error.go: -------------------------------------------------------------------------------- 1 | package azuremachinepool 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/azuremachinepool/handler/azuremachinepoolconditions/create.go: -------------------------------------------------------------------------------- 1 | package azuremachinepoolconditions 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/microerror" 7 | apierrors "k8s.io/apimachinery/pkg/api/errors" 8 | 9 | "github.com/giantswarm/azure-operator/v8/service/controller/key" 10 | ) 11 | 12 | func (r *Resource) EnsureCreated(ctx context.Context, cr interface{}) error { 13 | var err error 14 | azureMachinePool, err := key.ToAzureMachinePool(cr) 15 | if err != nil { 16 | return microerror.Mask(err) 17 | } 18 | 19 | // ensure Ready condition 20 | err = r.ensureReadyCondition(ctx, &azureMachinePool) 21 | if err != nil { 22 | return microerror.Mask(err) 23 | } 24 | 25 | err = r.ctrlClient.Status().Update(ctx, &azureMachinePool) 26 | if apierrors.IsConflict(err) { 27 | r.logger.Debugf(ctx, "conflict trying to save object in k8s API concurrently") 28 | r.logger.Debugf(ctx, "canceling resource") 29 | return nil 30 | } else if err != nil { 31 | return microerror.Mask(err) 32 | } 33 | 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /service/controller/azuremachinepool/handler/azuremachinepoolconditions/delete.go: -------------------------------------------------------------------------------- 1 | package azuremachinepoolconditions 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { 8 | return nil 9 | } 10 | -------------------------------------------------------------------------------- /service/controller/azuremachinepool/handler/azuremachinepoolconditions/error.go: -------------------------------------------------------------------------------- 1 | package azuremachinepoolconditions 2 | 3 | import ( 4 | "github.com/Azure/go-autorest/autorest" 5 | "github.com/giantswarm/microerror" 6 | ) 7 | 8 | var invalidConfigError = µerror.Error{ 9 | Kind: "invalidConfigError", 10 | } 11 | 12 | // IsInvalidConfig asserts invalidConfigError. 13 | func IsInvalidConfig(err error) bool { 14 | return microerror.Cause(err) == invalidConfigError 15 | } 16 | 17 | var notFoundError = µerror.Error{ 18 | Kind: "notFoundError", 19 | } 20 | 21 | // IsNotFound asserts notFoundError. 22 | func IsNotFound(err error) bool { 23 | if err == nil { 24 | return false 25 | } 26 | 27 | c := microerror.Cause(err) 28 | 29 | if c == notFoundError { 30 | return true 31 | } 32 | 33 | { 34 | dErr, ok := c.(autorest.DetailedError) 35 | if ok { 36 | if dErr.StatusCode == 404 { 37 | return true 38 | } 39 | } 40 | } 41 | 42 | return false 43 | } 44 | -------------------------------------------------------------------------------- /service/controller/azuremachinepool/handler/nodepool/template/errors.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var wrongTypeError = µerror.Error{ 8 | Kind: "wrongTypeError", 9 | } 10 | 11 | // IsWrongTypeError asserts wrongTypeError. 12 | func IsWrongTypeError(err error) bool { 13 | return microerror.Cause(err) == wrongTypeError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/azuremachinepool/handler/spark/testdata/encryption_key_secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | encryptioniv: JvF1/QAWdq8tqL8Z6IjvxQ== 4 | encryptionkey: jO0AD5mL9hF96+HNYukiwfw7x1Naj0BVV2QLvaub1NY= 5 | kind: Secret 6 | metadata: 7 | labels: 8 | giantswarm.io/cluster: c6fme 9 | giantswarm.io/managed-by: azure-operator 10 | giantswarm.io/organization: giantswarm 11 | name: c6fme-certificate-encryption 12 | namespace: default 13 | type: Opaque 14 | -------------------------------------------------------------------------------- /service/controller/azuremachinepool/handler/spark/testdata/simple_azurecluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 2 | kind: AzureCluster 3 | metadata: 4 | annotations: 5 | cluster.giantswarm.io/description: "" 6 | labels: 7 | azure-operator.giantswarm.io/version: 4.2.0 8 | cluster.x-k8s.io/cluster-name: c6fme 9 | giantswarm.io/cluster: c6fme 10 | giantswarm.io/organization: "" 11 | release.giantswarm.io/version: 12.0.0 12 | name: c6fme 13 | namespace: default 14 | spec: 15 | controlPlaneEndpoint: 16 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 17 | port: 443 18 | location: westeurope 19 | networkSpec: 20 | vnet: 21 | cidrBlocks: 22 | - 10.10.0.0/16 23 | name: c6fme-VirtualNetwork 24 | resourceGroup: c6fme 25 | resourceGroup: "" 26 | status: 27 | ready: false 28 | -------------------------------------------------------------------------------- /service/controller/azuremachinepool/handler/spark/testdata/simple_cluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cluster.x-k8s.io/v1beta1 2 | kind: Cluster 3 | metadata: 4 | labels: 5 | azure-operator.giantswarm.io/version: 4.2.0 6 | cluster-operator.giantswarm.io/version: "" 7 | cluster.x-k8s.io/cluster-name: c6fme 8 | giantswarm.io/cluster: c6fme 9 | giantswarm.io/organization: "" 10 | release.giantswarm.io/version: 12.0.0 11 | name: c6fme 12 | namespace: default 13 | spec: 14 | clusterNetwork: 15 | apiServerPort: 443 16 | serviceDomain: c6fme.k8s.ghost.westeurope.azure.gigantic.io 17 | services: 18 | cidrBlocks: 19 | - 172.31.0.0/16 20 | controlPlaneEndpoint: 21 | host: api.c6fme.k8s.ghost.westeurope.azure.gigantic.io 22 | port: 443 23 | infrastructureRef: 24 | kind: AzureCluster 25 | name: c6fme 26 | namespace: default 27 | status: 28 | infrastructureReady: false 29 | -------------------------------------------------------------------------------- /service/controller/azuremachinepool/handler/spark/testdata/simple_machinepool.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cluster.x-k8s.io/v1beta1 2 | kind: MachinePool 3 | metadata: 4 | labels: 5 | azure-operator.giantswarm.io/version: 4.2.0 6 | cluster.x-k8s.io/cluster-name: c6fme 7 | giantswarm.io/machine-pool: nopo1 8 | giantswarm.io/cluster: c6fme 9 | giantswarm.io/organization: giantswarm 10 | release.giantswarm.io/version: 12.0.0 11 | name: nopo1 12 | namespace: default 13 | spec: 14 | clusterName: c6fme 15 | failureDomains: 16 | - "1" 17 | - "3" 18 | replicas: 2 19 | template: 20 | metadata: { } 21 | spec: 22 | bootstrap: {} 23 | clusterName: c6fme 24 | infrastructureRef: 25 | apiVersion: infrastructure.cluster.x-k8s.io/v1beta1 26 | kind: AzureMachinePool 27 | name: nopo1 28 | version: v1.17.4 29 | -------------------------------------------------------------------------------- /service/controller/azuremachinepool/handler/spark/testdata/simple_spark.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: core.giantswarm.io/v1alpha1 2 | kind: Spark 3 | metadata: 4 | labels: 5 | azure-operator.giantswarm.io/version: 4.2.0 6 | cluster.x-k8s.io/cluster-name: c6fme 7 | giantswarm.io/cluster: c6fme 8 | giantswarm.io/organization: giantswarm 9 | release.giantswarm.io/version: 12.0.0 10 | name: nopo1 11 | namespace: default 12 | spec: 13 | values: 14 | dummy: test 15 | -------------------------------------------------------------------------------- /service/controller/cloudconfig/error.go: -------------------------------------------------------------------------------- 1 | package cloudconfig 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | 16 | var invalidSecretError = µerror.Error{ 17 | Kind: "invalidSecretError", 18 | } 19 | 20 | func IsInvalidSecret(err error) bool { 21 | return microerror.Cause(err) == invalidSecretError 22 | } 23 | 24 | var secretNotFoundError = µerror.Error{ 25 | Kind: "secretNotFoundError", 26 | } 27 | 28 | func IsSecretNotFoundError(err error) bool { 29 | return microerror.Cause(err) == secretNotFoundError 30 | } 31 | -------------------------------------------------------------------------------- /service/controller/cloudconfig/interface.go: -------------------------------------------------------------------------------- 1 | package cloudconfig 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/giantswarm/azure-operator/v8/service/controller/encrypter" 7 | ) 8 | 9 | type Interface interface { 10 | NewMasterTemplate(ctx context.Context, data IgnitionTemplateData, encrypter encrypter.Interface) (string, error) 11 | NewWorkerTemplate(ctx context.Context, data IgnitionTemplateData, encrypter encrypter.Interface) (string, error) 12 | } 13 | -------------------------------------------------------------------------------- /service/controller/cluster/error.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/cluster/handler/clusterdependents/error.go: -------------------------------------------------------------------------------- 1 | package clusterdependents 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/cluster/handler/clusterownerreference/error.go: -------------------------------------------------------------------------------- 1 | package clusterownerreference 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | 16 | var crBeingDeletedError = µerror.Error{ 17 | Kind: "crBeingDeletedError", 18 | } 19 | 20 | // IsCRBeingDeletedError asserts crBeingDeletedError. 21 | func IsCRBeingDeletedError(err error) bool { 22 | return microerror.Cause(err) == crBeingDeletedError 23 | } 24 | -------------------------------------------------------------------------------- /service/controller/cluster/handler/clusterreleaseversion/delete.go: -------------------------------------------------------------------------------- 1 | package clusterreleaseversion 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { 8 | return nil 9 | } 10 | -------------------------------------------------------------------------------- /service/controller/cluster/handler/clusterreleaseversion/error.go: -------------------------------------------------------------------------------- 1 | package clusterreleaseversion 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/cluster/handler/clusterreleaseversion/resource.go: -------------------------------------------------------------------------------- 1 | package clusterreleaseversion 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | "github.com/giantswarm/micrologger" 6 | 7 | "sigs.k8s.io/controller-runtime/pkg/client" 8 | ) 9 | 10 | const ( 11 | // Name is the identifier of the resource. 12 | Name = "clusterreleaseversion" 13 | ) 14 | 15 | type Config struct { 16 | CtrlClient client.Client 17 | Logger micrologger.Logger 18 | } 19 | 20 | // Resource ensures that Cluster release version annotation is set to the 21 | // release version with which the cluster was created or to which it was 22 | // upgraded. 23 | type Resource struct { 24 | ctrlClient client.Client 25 | logger micrologger.Logger 26 | } 27 | 28 | func New(config Config) (*Resource, error) { 29 | if config.CtrlClient == nil { 30 | return nil, microerror.Maskf(invalidConfigError, "%T.CtrlClient must not be empty", config) 31 | } 32 | if config.Logger == nil { 33 | return nil, microerror.Maskf(invalidConfigError, "%T.Logger must not be empty", config) 34 | } 35 | 36 | r := &Resource{ 37 | ctrlClient: config.CtrlClient, 38 | logger: config.Logger, 39 | } 40 | 41 | return r, nil 42 | } 43 | 44 | // Name returns the resource name. 45 | func (r *Resource) Name() string { 46 | return Name 47 | } 48 | -------------------------------------------------------------------------------- /service/controller/cluster/handler/clusterupgrade/delete.go: -------------------------------------------------------------------------------- 1 | package clusterupgrade 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { 8 | return nil 9 | } 10 | -------------------------------------------------------------------------------- /service/controller/cluster/handler/clusterupgrade/error.go: -------------------------------------------------------------------------------- 1 | package clusterupgrade 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | 16 | var notFoundError = µerror.Error{ 17 | Kind: "notFoundError", 18 | } 19 | 20 | // IsNotFound asserts notFoundError. 21 | func IsNotFound(err error) bool { 22 | if err == nil { 23 | return false 24 | } 25 | 26 | c := microerror.Cause(err) 27 | 28 | return c == notFoundError 29 | } 30 | -------------------------------------------------------------------------------- /service/controller/controllercontext/error.go: -------------------------------------------------------------------------------- 1 | package controllercontext 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidContextError = µerror.Error{ 8 | Kind: "invalidContextError", 9 | } 10 | 11 | // IsInvalidContext asserts invalidContextError. 12 | func IsInvalidContext(err error) bool { 13 | return microerror.Cause(err) == invalidContextError 14 | } 15 | 16 | var notFoundError = µerror.Error{ 17 | Kind: "notFoundError", 18 | } 19 | 20 | // IsNotFound asserts notFoundError. 21 | func IsNotFound(err error) bool { 22 | return microerror.Cause(err) == notFoundError 23 | } 24 | -------------------------------------------------------------------------------- /service/controller/debugger/debugger.go: -------------------------------------------------------------------------------- 1 | package debugger 2 | 3 | import ( 4 | "context" 5 | "io/ioutil" // nolint:staticcheck 6 | 7 | "github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2019-05-01/resources" 8 | "github.com/giantswarm/microerror" 9 | "github.com/giantswarm/micrologger" 10 | 11 | "github.com/giantswarm/azure-operator/v8/service/controller/key" 12 | ) 13 | 14 | type Config struct { 15 | Logger micrologger.Logger 16 | } 17 | 18 | type Debugger struct { 19 | logger micrologger.Logger 20 | } 21 | 22 | func New(config Config) (*Debugger, error) { 23 | if config.Logger == nil { 24 | return nil, microerror.Maskf(invalidConfigError, "%T.Logger must not be empty", config) 25 | } 26 | 27 | d := &Debugger{ 28 | logger: config.Logger, 29 | } 30 | 31 | return d, nil 32 | } 33 | 34 | func (d *Debugger) LogFailedDeployment(ctx context.Context, deployment resources.DeploymentExtended, err error) { 35 | if !key.IsFailedProvisioningState(*deployment.Properties.ProvisioningState) { 36 | return 37 | } 38 | 39 | body, _ := ioutil.ReadAll(deployment.Body) 40 | 41 | d.logger.LogCtx(ctx, 42 | "correlationID", *deployment.Properties.CorrelationID, 43 | "id", *deployment.ID, 44 | "level", "error", 45 | "message", "deployment failed", 46 | "status", deployment.Status, 47 | "body", string(body), 48 | "name", *deployment.Name, 49 | "stack", microerror.JSON(microerror.Mask(err)), 50 | ) 51 | } 52 | -------------------------------------------------------------------------------- /service/controller/debugger/error.go: -------------------------------------------------------------------------------- 1 | package debugger 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/encrypter/encrypter_test.go: -------------------------------------------------------------------------------- 1 | package encrypter 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_EncryptCFB(t *testing.T) { 8 | testCases := []struct { 9 | Name string 10 | Input []byte 11 | }{ 12 | { 13 | Name: "plain text encryption", 14 | Input: []byte("testtext"), 15 | }, 16 | } 17 | 18 | testKey := []byte("12345678901234567890123456789012") 19 | testIV := []byte("1234567891234567") 20 | c := Config{ 21 | Key: testKey, 22 | IV: testIV, 23 | } 24 | 25 | encrypter, err := New(c) 26 | if err != nil { 27 | t.Errorf("failed to create encrypter, %v", err) 28 | } 29 | 30 | for i, tc := range testCases { 31 | encrypted, err := encrypter.Encrypt(tc.Input) 32 | if err != nil { 33 | t.Errorf("case %d: %s: expected err = nil, got %v", i, tc.Name, err) 34 | } 35 | 36 | decrypted, err := encrypter.Decrypt(encrypted) 37 | if err != nil { 38 | t.Errorf("case %d: %s: expected err = nil, got %v", i, tc.Name, err) 39 | } 40 | 41 | if string(tc.Input) != string(decrypted) { 42 | t.Errorf("case %d: %s: expected %s, got %s", i, tc.Name, tc.Input, decrypted) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /service/controller/encrypter/error.go: -------------------------------------------------------------------------------- 1 | package encrypter 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/encrypter/interface.go: -------------------------------------------------------------------------------- 1 | package encrypter 2 | 3 | type Interface interface { 4 | Encrypt([]byte) ([]byte, error) 5 | Decrypt([]byte) ([]byte, error) 6 | GetEncryptionKey() string 7 | GetInitialVector() string 8 | } 9 | -------------------------------------------------------------------------------- /service/controller/internal/vmsku/error.go: -------------------------------------------------------------------------------- 1 | package vmsku 2 | 3 | import "github.com/giantswarm/microerror" 4 | 5 | var invalidConfigError = µerror.Error{ 6 | Kind: "invalidConfigError", 7 | } 8 | 9 | // IsInvalidConfig asserts invalidConfigError. 10 | func IsInvalidConfig(err error) bool { 11 | return microerror.Cause(err) == invalidConfigError 12 | } 13 | 14 | var skuNotFoundError = µerror.Error{ 15 | Kind: "skuNotFoundError", 16 | } 17 | 18 | // IsSkuNotFoundError asserts skuNotFoundError. 19 | func IsSkuNotFoundError(err error) bool { 20 | return microerror.Cause(err) == skuNotFoundError 21 | } 22 | -------------------------------------------------------------------------------- /service/controller/internal/vmsscheck/error.go: -------------------------------------------------------------------------------- 1 | package vmsscheck 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/giantswarm/microerror" 7 | ) 8 | 9 | var invalidConfigError = µerror.Error{ 10 | Kind: "invalidConfigError", 11 | } 12 | 13 | // IsInvalidConfig asserts invalidConfigError. 14 | func IsInvalidConfig(err error) bool { 15 | return microerror.Cause(err) == invalidConfigError 16 | } 17 | 18 | // IsNotFound asserts resource not found error messages from upstream API's response. 19 | func IsNotFound(err error) bool { 20 | if err == nil { 21 | return false 22 | } 23 | return strings.Contains(microerror.Cause(err).Error(), "ResourceNotFound") || 24 | strings.Contains(microerror.Cause(err).Error(), "ResourceGroupNotFound") 25 | } 26 | -------------------------------------------------------------------------------- /service/controller/internal/vmsscheck/vmss.go: -------------------------------------------------------------------------------- 1 | package vmsscheck 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute" 7 | "github.com/giantswarm/microerror" 8 | "github.com/giantswarm/micrologger" 9 | ) 10 | 11 | const ( 12 | // Provisioning States. 13 | provisioningStateFailed = "Failed" 14 | provisioningStateSucceeded = "Succeeded" 15 | ) 16 | 17 | // Find out provisioning state of all VMSS instances and return true if all are 18 | // Succeeded. 19 | func InstancesAreRunning(ctx context.Context, logger micrologger.Logger, virtualMachineScaleSetVMsClient *compute.VirtualMachineScaleSetVMsClient, resourceGroup string, vmssName string) (bool, error) { 20 | // Get a list of instances in the VMSS. 21 | iterator, err := virtualMachineScaleSetVMsClient.ListComplete(ctx, resourceGroup, vmssName, "", "", "") 22 | if err != nil { 23 | return false, microerror.Mask(err) 24 | } 25 | 26 | allSucceeded := true 27 | 28 | for iterator.NotDone() { 29 | instance := iterator.Value() 30 | logger.Debugf(ctx, "Instance %s has state %s", *instance.Name, *instance.ProvisioningState) 31 | 32 | switch *instance.ProvisioningState { 33 | case provisioningStateFailed: 34 | allSucceeded = false 35 | case provisioningStateSucceeded: 36 | // OK to continue. 37 | default: 38 | allSucceeded = false 39 | } 40 | 41 | if err := iterator.NextWithContext(ctx); err != nil { 42 | return false, microerror.Mask(err) 43 | } 44 | } 45 | 46 | return allSucceeded, nil 47 | } 48 | -------------------------------------------------------------------------------- /service/controller/internal/workerpool/pool.go: -------------------------------------------------------------------------------- 1 | package workerpool 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | 7 | "github.com/giantswarm/microerror" 8 | "github.com/giantswarm/micrologger" 9 | ) 10 | 11 | type Pool struct { 12 | jobQueue chan Job 13 | logger micrologger.Logger 14 | } 15 | 16 | func New(size int, logger micrologger.Logger) *Pool { 17 | p := &Pool{ 18 | jobQueue: make(chan Job), 19 | logger: logger, 20 | } 21 | 22 | for i := 0; i < size; i++ { 23 | p.startWorker() 24 | } 25 | 26 | return p 27 | } 28 | 29 | func (p *Pool) EnqueueJob(job Job) { 30 | go func() { 31 | p.jobQueue <- job 32 | }() 33 | } 34 | 35 | func (p *Pool) Stop() { 36 | close(p.jobQueue) 37 | } 38 | 39 | func (p *Pool) startWorker() { 40 | go func() { 41 | for { 42 | j, open := <-p.jobQueue 43 | if !open { 44 | break 45 | } 46 | 47 | if j != nil { 48 | err := j.Run() 49 | if err != nil { 50 | p.logger.Log("level", "debug", "message", "job execution failed", "job_id", j.ID(), "stack", microerror.JSON(err)) // nolint: errcheck 51 | } else { 52 | if !j.Finished() { 53 | p.EnqueueJob(j) 54 | } else { 55 | p.logger.Log("level", "debug", "message", "job finished", "job_id", j.ID()) // nolint: errcheck 56 | } 57 | } 58 | } 59 | 60 | // Random wait time between 10 and 100 milliseconds, so we avoid 61 | // infinite loop with idling jobs. 62 | waitTime := time.Duration((rand.Intn(10) + 1) * 10) // nolint:gosec 63 | time.Sleep(waitTime * time.Millisecond) 64 | } 65 | }() 66 | } 67 | -------------------------------------------------------------------------------- /service/controller/internal/workerpool/spec.go: -------------------------------------------------------------------------------- 1 | package workerpool 2 | 3 | type Job interface { 4 | ID() string 5 | Finished() bool 6 | Run() error 7 | } 8 | -------------------------------------------------------------------------------- /service/controller/key/spec.go: -------------------------------------------------------------------------------- 1 | package key 2 | 3 | import ( 4 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | ) 6 | 7 | type DeletionTimestampGetter interface { 8 | GetDeletionTimestamp() *metav1.Time 9 | } 10 | 11 | type LabelsGetter interface { 12 | GetLabels() map[string]string 13 | } 14 | 15 | type AnnotationsGetter interface { 16 | GetAnnotations() map[string]string 17 | } 18 | -------------------------------------------------------------------------------- /service/controller/machinepool/error.go: -------------------------------------------------------------------------------- 1 | package machinepool 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/machinepool/handler/machinepooldependents/error.go: -------------------------------------------------------------------------------- 1 | package machinepooldependents 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/machinepool/handler/machinepoolownerreference/error.go: -------------------------------------------------------------------------------- 1 | package machinepoolownerreference 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/machinepool/handler/machinepoolupgrade/error.go: -------------------------------------------------------------------------------- 1 | package machinepoolupgrade 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/machinepool/handler/nodestatus/delete.go: -------------------------------------------------------------------------------- 1 | package nodestatus 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { 8 | return nil 9 | } 10 | -------------------------------------------------------------------------------- /service/controller/machinepool/handler/nodestatus/error.go: -------------------------------------------------------------------------------- 1 | package nodestatus 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfigError asserts invalidConfigError. 12 | func IsInvalidConfigError(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | 16 | var errNoAvailableNodes = µerror.Error{ 17 | Kind: "errNoAvailableNodes", 18 | } 19 | 20 | // IsErrNoAvailableNodes asserts errNoAvailableNodes. 21 | func IsErrNoAvailableNodes(err error) bool { 22 | return microerror.Cause(err) == errNoAvailableNodes 23 | } 24 | -------------------------------------------------------------------------------- /service/controller/templates/ignition/azure_cni_nat_rules.go: -------------------------------------------------------------------------------- 1 | package ignition 2 | 3 | const AzureCNINatRules = `[Unit] 4 | Description=Setup iptables Nat rules for Azure CNI 5 | Wants=systemd-networkd.service 6 | After=systemd-networkd.service 7 | Before=docker.service 8 | [Service] 9 | Type=oneshot 10 | ExecStartPre=/bin/sh -c "iptables -I INPUT 1 -m udp -p udp --source-port 80 -j DROP" 11 | ExecStartPre=/bin/sh -c "iptables -I INPUT 1 -m udp -p udp --source-port 443 -j DROP" 12 | ExecStart=/bin/sh -c "iptables -t nat -A POSTROUTING -m addrtype ! --dst-type local ! -d {{.VnetCIDR}} -j MASQUERADE" 13 | [Install] 14 | WantedBy=multi-user.target 15 | ` 16 | -------------------------------------------------------------------------------- /service/controller/templates/ignition/certificate_decrypter_unit.go: -------------------------------------------------------------------------------- 1 | package ignition 2 | 3 | const CertificateDecrypterUnit = `[Unit] 4 | Description=Certificate Decrypter 5 | Wants=k8s-setup-network-env.service 6 | After=k8s-setup-network-env.service 7 | Before=k8s-kubelet.service etcd3.service 8 | [Service] 9 | Type=oneshot 10 | EnvironmentFile=/etc/.enc/key 11 | EnvironmentFile=/etc/.enc/iv 12 | ExecStart=/bin/sh -c "\ 13 | {{ range $index, $file := .CertsPaths -}} 14 | openssl enc -aes-256-cfb -d -K ${ENCRYPTION_KEY} -iv ${INITIAL_VECTOR} -in {{ $file }}.enc -out {{ $file }} ; \ 15 | {{ end -}} 16 | " 17 | [Install] 18 | WantedBy=multi-user.target 19 | ` 20 | -------------------------------------------------------------------------------- /service/controller/templates/ignition/cloud_provider_conf.go: -------------------------------------------------------------------------------- 1 | package ignition 2 | 3 | const CloudProviderConf = `cloud: {{ .EnvironmentName }} 4 | tenantId: {{ .TenantID }} 5 | subscriptionId: {{ .SubscriptionID }} 6 | resourceGroup: {{ .ResourceGroup }} 7 | location: {{ .Location }} 8 | {{- if not .UseManagedIdentityExtension }} 9 | aadClientId: {{ .AADClientID }} 10 | aadClientSecret: {{ .AADClientSecret }} 11 | {{- end}} 12 | cloudProviderBackoff: true 13 | cloudProviderBackoffRetries: 6 14 | cloudProviderBackoffJitter: 1 15 | cloudProviderBackoffDuration: 6 16 | cloudProviderBackoffExponent: 1.5 17 | cloudProviderRateLimit: true 18 | cloudProviderRateLimitQPS: 3 19 | cloudProviderRateLimitBucket: 10 20 | cloudProviderRateLimitQPSWrite: 3 21 | cloudProviderRateLimitBucketWrite: 10 22 | primaryScaleSetName: {{ .PrimaryScaleSetName }} 23 | subnetName: {{ .SubnetName }} 24 | securityGroupName: {{ .SecurityGroupName }} 25 | vnetName: {{ .VnetName }} 26 | vmType: vmss 27 | routeTableName: {{ .RouteTableName }} 28 | useManagedIdentityExtension: {{ .UseManagedIdentityExtension }} 29 | useInstanceMetadata: true 30 | loadBalancerSku: standard 31 | ` 32 | -------------------------------------------------------------------------------- /service/controller/templates/ignition/docker_mount_unit.go: -------------------------------------------------------------------------------- 1 | package ignition 2 | 3 | const DockerMountUnit = `[Unit] 4 | Description=Mounts disk to /var/lib/docker 5 | Before=docker.service 6 | 7 | [Mount] 8 | What=/dev/disk/by-label/docker 9 | Where=/var/lib/docker 10 | Type=xfs 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | ` 15 | -------------------------------------------------------------------------------- /service/controller/templates/ignition/etcd_mount_unit.go: -------------------------------------------------------------------------------- 1 | package ignition 2 | 3 | const EtcdMountUnit = `[Unit] 4 | Description=Mounts disk to /var/lib/etcd 5 | Before=etcd3.service 6 | 7 | [Mount] 8 | What=/dev/disk/by-label/etcd 9 | Where=/var/lib/etcd 10 | Type=ext4 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | ` 15 | -------------------------------------------------------------------------------- /service/controller/templates/ignition/kubelet_mount_unit.go: -------------------------------------------------------------------------------- 1 | package ignition 2 | 3 | const KubeletMountUnit = `[Unit] 4 | Description=Mounts disk to /var/lib/kubelet 5 | Before=k8s-kubelet.service 6 | 7 | [Mount] 8 | What=/dev/disk/by-label/kubelet 9 | Where=/var/lib/kubelet 10 | Type=xfs 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | ` 15 | -------------------------------------------------------------------------------- /service/controller/templates/ignition/vnic_configuration_unit.go: -------------------------------------------------------------------------------- 1 | package ignition 2 | 3 | const VNICConfigurationUnit = `[Unit] 4 | Description=VNIC configuration 5 | Wants=systemd-networkd.service 6 | After=systemd-networkd.service 7 | Before=docker.service 8 | [Service] 9 | Type=oneshot 10 | ExecStart=/bin/sh -c "ethtool -G eth0 tx 1024" 11 | [Install] 12 | WantedBy=multi-user.target 13 | ` 14 | -------------------------------------------------------------------------------- /service/controller/templates/templates.go: -------------------------------------------------------------------------------- 1 | package templates 2 | 3 | import ( 4 | "bytes" 5 | "text/template" 6 | 7 | "github.com/giantswarm/microerror" 8 | ) 9 | 10 | func Render(templates []string, data interface{}) (string, error) { 11 | var err error 12 | 13 | main := template.New("main") 14 | for _, t := range templates { 15 | main, err = main.Parse(t) 16 | if err != nil { 17 | return "", microerror.Mask(err) 18 | } 19 | } 20 | 21 | var b bytes.Buffer 22 | err = main.ExecuteTemplate(&b, "main", data) 23 | if err != nil { 24 | return "", microerror.Mask(err) 25 | } 26 | 27 | return b.String(), nil 28 | } 29 | -------------------------------------------------------------------------------- /service/controller/templates/templates_test.go: -------------------------------------------------------------------------------- 1 | package templates_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/giantswarm/azure-operator/v8/service/controller/templates" 7 | ) 8 | 9 | func TestRender(t *testing.T) { 10 | t.Parallel() 11 | tpl := "some string <{{.Value}}> another string" 12 | d := struct { 13 | Value string 14 | }{"myvalue"} 15 | expected := "some string another string" 16 | 17 | actual, err := templates.Render([]string{tpl}, d) 18 | if err != nil { 19 | t.Errorf("unexpected error %v", err) 20 | } 21 | 22 | if actual != expected { 23 | t.Errorf("unexpected output, want %q, got %q", expected, actual) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /service/controller/unhealthynode/error.go: -------------------------------------------------------------------------------- 1 | package unhealthynode 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/controller/unhealthynode/handler/terminateunhealthynode/delete.go: -------------------------------------------------------------------------------- 1 | package terminateunhealthynode 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | func (r *Resource) EnsureDeleted(ctx context.Context, obj interface{}) error { 8 | return nil 9 | } 10 | -------------------------------------------------------------------------------- /service/controller/unhealthynode/handler/terminateunhealthynode/error.go: -------------------------------------------------------------------------------- 1 | package terminateunhealthynode 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | 16 | var invalidProviderIDError = µerror.Error{ 17 | Kind: "invalidProviderID", 18 | } 19 | 20 | // IsInvalidProviderID asserts invalidConfigError. 21 | func IsInvalidProviderID(err error) bool { 22 | return microerror.Cause(err) == invalidProviderIDError 23 | } 24 | 25 | var unsupportedOperationError = µerror.Error{ 26 | Kind: "unsupportedOperationError", 27 | } 28 | 29 | // IsUnsupportedOperation asserts unsupportedOperationError. 30 | func IsUnsupportedOperation(err error) bool { 31 | return microerror.Cause(err) == unsupportedOperationError 32 | } 33 | -------------------------------------------------------------------------------- /service/controller/unhealthynode/handler/terminateunhealthynode/metrics.go: -------------------------------------------------------------------------------- 1 | package terminateunhealthynode 2 | 3 | import ( 4 | "github.com/prometheus/client_golang/prometheus" 5 | ) 6 | 7 | const ( 8 | gaugeValue float64 = 1 9 | ) 10 | 11 | var ( 12 | nodeAutoRepairTermination = prometheus.NewGaugeVec( 13 | prometheus.GaugeOpts{ 14 | Name: "azure_operator_unhealthy_node_termination", 15 | Help: "Gauge representing node termination due to node auto repair feature.", 16 | }, 17 | []string{"cluster_id", "terminated_node", "terminated_instance_id"}, 18 | ) 19 | ) 20 | 21 | func init() { 22 | prometheus.MustRegister(nodeAutoRepairTermination) 23 | 24 | } 25 | 26 | // reportNodeTermination is a utility function for updating metrics related to 27 | // node auto repair node termination. 28 | func reportNodeTermination(clusterID string, nodeName string, instanceID string) { 29 | nodeAutoRepairTermination.WithLabelValues( 30 | clusterID, nodeName, instanceID, 31 | ).Set(gaugeValue) 32 | } 33 | -------------------------------------------------------------------------------- /service/error.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/giantswarm/microerror" 5 | ) 6 | 7 | var invalidConfigError = µerror.Error{ 8 | Kind: "invalidConfigError", 9 | } 10 | 11 | // IsInvalidConfig asserts invalidConfigError. 12 | func IsInvalidConfig(err error) bool { 13 | return microerror.Cause(err) == invalidConfigError 14 | } 15 | -------------------------------------------------------------------------------- /service/network/network.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/giantswarm/ipam" 7 | "github.com/giantswarm/microerror" 8 | ) 9 | 10 | const ( 11 | masterSubnetMask = 24 12 | workerSubnetMask = 24 13 | vpnSubnetMask = 24 14 | 15 | ipv4MaskSize = 32 16 | ) 17 | 18 | // Compute computes subnets within network. 19 | // 20 | // subnets computation rely on ipam.Free and use ipv4MaskSize as IPMask length. 21 | func Compute(network net.IPNet) (subnets *Subnets, err error) { 22 | subnets = new(Subnets) 23 | 24 | subnets.Parent = network 25 | 26 | _, subnets.Calico, err = ipam.Half(network) 27 | if err != nil { 28 | return nil, microerror.Mask(err) 29 | } 30 | 31 | masterCIDRMask := net.CIDRMask(masterSubnetMask, ipv4MaskSize) 32 | subnets.Master, err = ipam.Free(network, masterCIDRMask, []net.IPNet{subnets.Calico}) 33 | if err != nil { 34 | return nil, microerror.Mask(err) 35 | } 36 | 37 | workerCIDRMask := net.CIDRMask(workerSubnetMask, ipv4MaskSize) 38 | subnets.Worker, err = ipam.Free(network, workerCIDRMask, []net.IPNet{subnets.Calico, subnets.Master}) 39 | if err != nil { 40 | return nil, microerror.Mask(err) 41 | } 42 | 43 | vpnCIDRMask := net.CIDRMask(vpnSubnetMask, ipv4MaskSize) 44 | subnets.VPN, err = ipam.Free(network, vpnCIDRMask, []net.IPNet{subnets.Calico, subnets.Master, subnets.Worker}) 45 | if err != nil { 46 | return nil, microerror.Mask(err) 47 | } 48 | 49 | return subnets, nil 50 | } 51 | -------------------------------------------------------------------------------- /service/network/subnets.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | import ( 4 | "bytes" 5 | "net" 6 | ) 7 | 8 | // Subnets holds network subnets used by guest clusters. 9 | type Subnets struct { 10 | Parent net.IPNet 11 | Calico net.IPNet 12 | Master net.IPNet 13 | VPN net.IPNet 14 | Worker net.IPNet 15 | } 16 | 17 | // Equal return true when every network IP and Mask in a and b are equal. 18 | func (a Subnets) Equal(b Subnets) bool { 19 | // Condition (for humans): 20 | // a.Calico.IP == b.Calico.IP && 21 | // a.Master.IP == b.Master.IP && 22 | // a.Parent.IP == b.Parent.IP && 23 | // a.VPN.IP == b.VPN.IP && 24 | // a.Worker.IP == b.Worker.IP && 25 | // a.Calico.Mask == b.Calico.Mask && 26 | // a.Master.Mask == b.Master.Mask && 27 | // a.Parent.Mask == b.Parent.Mask && 28 | // a.VPN.Mask == b.VPN.Mask && 29 | // a.Worker.Mask == b.Worker.Mask 30 | return a.Calico.IP.Equal(b.Calico.IP) && 31 | bytes.Equal(a.Calico.Mask, b.Calico.Mask) && 32 | a.Master.IP.Equal(b.Master.IP) && 33 | bytes.Equal(a.Master.Mask, b.Master.Mask) && 34 | a.Parent.IP.Equal(b.Parent.IP) && 35 | bytes.Equal(a.Parent.Mask, b.Parent.Mask) && 36 | a.VPN.IP.Equal(b.VPN.IP) && 37 | bytes.Equal(a.VPN.Mask, b.VPN.Mask) && 38 | a.Worker.IP.Equal(b.Worker.IP) && 39 | bytes.Equal(a.Worker.Mask, b.Worker.Mask) 40 | } 41 | --------------------------------------------------------------------------------