From 4d7f8c433b2a5378562acffd1bcd639a82d89225 Mon Sep 17 00:00:00 2001 From: Ciprian Hacman Date: Wed, 15 Apr 2026 03:47:31 +0300 Subject: [PATCH] azure: Add Workload Identity support Signed-off-by: Ciprian Hacman --- cmd/kops/integration_test.go | 11 + go.mod | 1 + go.sum | 2 + pkg/apis/kops/cluster.go | 23 +- pkg/apis/kops/componentconfig.go | 4 + pkg/apis/kops/v1alpha2/componentconfig.go | 4 + .../kops/v1alpha2/zz_generated.conversion.go | 2 + pkg/apis/kops/v1alpha3/componentconfig.go | 4 + .../kops/v1alpha3/zz_generated.conversion.go | 2 + pkg/apis/kops/validation/legacy.go | 4 +- pkg/model/azuremodel/workloadidentity.go | 98 ++ pkg/model/components/discovery.go | 5 + pkg/model/issuerdiscovery.go | 14 + ...asters.minimal-azure.example.com_user_data | 134 +++ ..._nodes.minimal-azure.example.com_user_data | 157 +++ ...storage_blob_cluster-completed.spec_source | 200 ++++ ...azurerm_storage_blob_discovery.json_source | 18 + ...orage_blob_etcd-cluster-spec-events_source | 4 + ...storage_blob_etcd-cluster-spec-main_source | 4 + .../azurerm_storage_blob_keys.json_source | 20 + ...urerm_storage_blob_kops-version.txt_source | 1 + ...nager-events-control-plane-eastus-1_source | 115 ++ ...manager-main-control-plane-eastus-1_source | 115 ++ ...s-static-kube-apiserver-healthcheck_source | 32 + ...d-controller.addons.k8s.io-k8s-1.31_source | 426 +++++++ ...k-csi-driver.addons.k8s.io-k8s-1.31_source | 1044 +++++++++++++++++ ...-azure.example.com-addons-bootstrap_source | 136 +++ ...dons-coredns.addons.k8s.io-k8s-1.12_source | 384 ++++++ ...s-controller.addons.k8s.io-k8s-1.16_source | 221 ++++ ...elet-api.rbac.addons.k8s.io-k8s-1.9_source | 16 + ...om-addons-limit-range.addons.k8s.io_source | 14 + ...torage-azure.addons.k8s.io-k8s-1.31_source | 16 + ...nodeupconfig-control-plane-eastus-1_source | 328 ++++++ ...erm_storage_blob_nodeupconfig-nodes_source | 64 + .../minimal_azure_wi/id_rsa.pub | 1 + .../minimal_azure_wi/in-v1alpha3.yaml | 104 ++ .../minimal_azure_wi/kubernetes.tf | 738 ++++++++++++ .../k8s-1.31.yaml.template | 44 + .../k8s-1.31.yaml.template | 15 + upup/pkg/fi/cloudup/apply_cluster.go | 68 +- upup/pkg/fi/cloudup/azure/azure_cloud.go | 19 + upup/pkg/fi/cloudup/azure/managedidentity.go | 129 ++ .../azuretasks/federatedidentitycredential.go | 130 ++ .../federatedidentitycredential_fitask.go | 52 + .../federatedidentitycredential_terraform.go | 48 + .../fi/cloudup/azuretasks/managedidentity.go | 135 +++ .../azuretasks/managedidentity_fitask.go | 52 + .../azuretasks/managedidentity_terraform.go | 52 + .../fi/cloudup/azuretasks/roleassignment.go | 88 +- .../azuretasks/roleassignment_terraform.go | 23 +- upup/pkg/fi/cloudup/azuretasks/testing.go | 88 ++ upup/pkg/fi/cloudup/new_cluster.go | 3 + upup/pkg/fi/cloudup/template_functions.go | 39 + util/pkg/vfs/azureblob.go | 29 + .../resourcemanager/msi/armmsi/CHANGELOG.md | 68 ++ .../resourcemanager/msi/armmsi/LICENSE.txt | 21 + .../sdk/resourcemanager/msi/armmsi/README.md | 90 ++ .../resourcemanager/msi/armmsi/assets.json | 6 + .../resourcemanager/msi/armmsi/autorest.md | 13 + .../sdk/resourcemanager/msi/armmsi/build.go | 7 + .../sdk/resourcemanager/msi/armmsi/ci.yml | 28 + .../msi/armmsi/client_factory.go | 64 + .../resourcemanager/msi/armmsi/constants.go | 47 + .../federatedidentitycredentials_client.go | 317 +++++ .../sdk/resourcemanager/msi/armmsi/models.go | 212 ++++ .../msi/armmsi/models_serde.go | 552 +++++++++ .../msi/armmsi/operations_client.go | 85 ++ .../sdk/resourcemanager/msi/armmsi/options.go | 82 ++ .../resourcemanager/msi/armmsi/responses.go | 76 ++ .../armmsi/systemassignedidentities_client.go | 89 ++ .../msi/armmsi/time_rfc3339.go | 110 ++ .../armmsi/userassignedidentities_client.go | 414 +++++++ vendor/modules.txt | 3 + 73 files changed, 7724 insertions(+), 40 deletions(-) create mode 100644 pkg/model/azuremodel/workloadidentity.go create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_linux_virtual_machine_scale_set_control-plane-eastus-1.masters.minimal-azure.example.com_user_data create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_linux_virtual_machine_scale_set_nodes.minimal-azure.example.com_user_data create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_cluster-completed.spec_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_discovery.json_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_etcd-cluster-spec-events_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_etcd-cluster-spec-main_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_keys.json_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_kops-version.txt_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_manifests-etcdmanager-events-control-plane-eastus-1_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_manifests-etcdmanager-main-control-plane-eastus-1_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_manifests-static-kube-apiserver-healthcheck_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-azure-cloud-controller.addons.k8s.io-k8s-1.31_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-azuredisk-csi-driver.addons.k8s.io-k8s-1.31_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-bootstrap_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-coredns.addons.k8s.io-k8s-1.12_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-kops-controller.addons.k8s.io-k8s-1.16_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-kubelet-api.rbac.addons.k8s.io-k8s-1.9_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-limit-range.addons.k8s.io_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-storage-azure.addons.k8s.io-k8s-1.31_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_nodeupconfig-control-plane-eastus-1_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_nodeupconfig-nodes_source create mode 100644 tests/integration/update_cluster/minimal_azure_wi/id_rsa.pub create mode 100644 tests/integration/update_cluster/minimal_azure_wi/in-v1alpha3.yaml create mode 100644 tests/integration/update_cluster/minimal_azure_wi/kubernetes.tf create mode 100644 upup/pkg/fi/cloudup/azure/managedidentity.go create mode 100644 upup/pkg/fi/cloudup/azuretasks/federatedidentitycredential.go create mode 100644 upup/pkg/fi/cloudup/azuretasks/federatedidentitycredential_fitask.go create mode 100644 upup/pkg/fi/cloudup/azuretasks/federatedidentitycredential_terraform.go create mode 100644 upup/pkg/fi/cloudup/azuretasks/managedidentity.go create mode 100644 upup/pkg/fi/cloudup/azuretasks/managedidentity_fitask.go create mode 100644 upup/pkg/fi/cloudup/azuretasks/managedidentity_terraform.go create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/CHANGELOG.md create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/LICENSE.txt create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/README.md create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/assets.json create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/autorest.md create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/build.go create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/ci.yml create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/client_factory.go create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/constants.go create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/federatedidentitycredentials_client.go create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/models.go create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/models_serde.go create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/operations_client.go create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/options.go create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/responses.go create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/systemassignedidentities_client.go create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/time_rfc3339.go create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/userassignedidentities_client.go diff --git a/cmd/kops/integration_test.go b/cmd/kops/integration_test.go index 5e5b1e52b6abf..4e7330ca20ef7 100644 --- a/cmd/kops/integration_test.go +++ b/cmd/kops/integration_test.go @@ -330,6 +330,17 @@ func TestMinimalAzure(t *testing.T) { runTestTerraformAzure(t) } +// TestMinimalAzureWi runs the test on a minimum Azure configuration with +// Workload Identity (useServiceAccountExternalPermissions) enabled. This pins +// the rendered CCM and azuredisk-csi addon manifests on the workload-identity +// branch so future refactors can't silently break the aadClientID secret or +// the projected ServiceAccountToken volume. +func TestMinimalAzureWi(t *testing.T) { + newIntegrationTest("minimal-azure.example.com", "minimal_azure_wi"). + withVersion("v1alpha3"). + runTestTerraformAzure(t) +} + // TestMinimal_NoneDNS runs the test on a minimum configuration with --dns=none func TestMinimal_NoneDNS(t *testing.T) { newIntegrationTest("minimal.example.com", "minimal-dns-none"). diff --git a/go.mod b/go.mod index 0d77773ec1dc4..02521918a76b2 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3 v3.0.0-beta.2 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.3.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 diff --git a/go.sum b/go.sum index b296b4d2b1edb..36bebe9bf5014 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsI github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0/go.mod h1:AW8VEadnhw9xox+VaVd9sP7NjzOAnaZBLRH6Tq3cJ38= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0 h1:pPvTJ1dY0sA35JOeFq6TsY2xj6Z85Yo23Pj4wCCvu4o= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/managementgroups/armmanagementgroups v1.0.0/go.mod h1:mLfWfj8v3jfWKsL9G4eoBoXVcsqcIUTapmdKy7uGOp0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.3.0 h1:L7G3dExHBgUxsO3qpTGhk/P2dgnYyW48yn7AO33Tbek= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.3.0/go.mod h1:Ms6gYEy0+A2knfKrwdatsggTXYA2+ICKug8w7STorFw= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0/go.mod h1:243D9iHbcQXoFUtgHJwL7gl2zx1aDuDMjvBZVGr2uW0= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= diff --git a/pkg/apis/kops/cluster.go b/pkg/apis/kops/cluster.go index 122773073bc8b..399e462a813f9 100644 --- a/pkg/apis/kops/cluster.go +++ b/pkg/apis/kops/cluster.go @@ -18,6 +18,7 @@ package kops import ( "fmt" + "strings" "github.com/blang/semver/v4" corev1 "k8s.io/api/core/v1" @@ -922,15 +923,31 @@ func (c *Cluster) AzureRouteTableName() string { return c.Name } -// AzureNetworkSecurityGroupName returns the name of the network security group for the cluster. -// The NSG shares its name with the virtual network. -func (c *Cluster) AzureNetworkSecurityGroupName() string { +// AzureNetworkName returns the name of the Azure Virtual Network for the cluster. +// If Networking.NetworkID is set (shared network), that is used; otherwise the +// cluster name is used. +func (c *Cluster) AzureNetworkName() string { if c.Spec.Networking.NetworkID != "" { return c.Spec.Networking.NetworkID } return c.Name } +// AzureNetworkSecurityGroupName returns the name of the network security group for the cluster. +// The NSG shares its name with the virtual network; callers relying on this invariant +// should prefer this helper over re-deriving the name inline. +func (c *Cluster) AzureNetworkSecurityGroupName() string { + return c.AzureNetworkName() +} + +// AzureWorkloadIdentityName returns the name of the User-Assigned Managed Identity +// used for Azure Workload Identity. Both the pre-flight in apply_cluster.go and the +// task-graph entry in the azuremodel must compute the identical string — callers +// should delegate here rather than re-deriving it inline. +func (c *Cluster) AzureWorkloadIdentityName() string { + return "wi-" + strings.ReplaceAll(c.Name, ".", "-") +} + func (c *Cluster) PublishesDNSRecords() bool { if c.UsesNoneDNS() || dns.IsGossipClusterName(c.Name) { return false diff --git a/pkg/apis/kops/componentconfig.go b/pkg/apis/kops/componentconfig.go index cf3064a4a26b4..940e1ec539fd9 100644 --- a/pkg/apis/kops/componentconfig.go +++ b/pkg/apis/kops/componentconfig.go @@ -958,6 +958,10 @@ type AzureSpec struct { RouteTableName string `json:"routeTableName,omitempty"` // AdminUser specifies the admin user of VMs. AdminUser string `json:"adminUser,omitempty"` + // WorkloadIdentityClientID is the client ID of the User-Assigned Managed Identity + // used for Azure Workload Identity. This is populated automatically by kops + // when UseServiceAccountExternalPermissions is enabled. + WorkloadIdentityClientID string `json:"workloadIdentityClientID,omitempty"` } // CloudConfiguration defines the cloud provider configuration diff --git a/pkg/apis/kops/v1alpha2/componentconfig.go b/pkg/apis/kops/v1alpha2/componentconfig.go index 9f4df2698aaed..25f69703afb6e 100644 --- a/pkg/apis/kops/v1alpha2/componentconfig.go +++ b/pkg/apis/kops/v1alpha2/componentconfig.go @@ -964,6 +964,10 @@ type AzureSpec struct { RouteTableName string `json:"routeTableName,omitempty"` // AdminUser specifies the admin user of VMs. AdminUser string `json:"adminUser,omitempty"` + // WorkloadIdentityClientID is the client ID of the User-Assigned Managed Identity + // used for Azure Workload Identity. This is populated automatically by kops + // when UseServiceAccountExternalPermissions is enabled. + WorkloadIdentityClientID string `json:"workloadIdentityClientID,omitempty"` } // CloudConfiguration defines the cloud provider configuration diff --git a/pkg/apis/kops/v1alpha2/zz_generated.conversion.go b/pkg/apis/kops/v1alpha2/zz_generated.conversion.go index 73776045ead59..1e2301cef1cdf 100644 --- a/pkg/apis/kops/v1alpha2/zz_generated.conversion.go +++ b/pkg/apis/kops/v1alpha2/zz_generated.conversion.go @@ -1758,6 +1758,7 @@ func autoConvert_v1alpha2_AzureSpec_To_kops_AzureSpec(in *AzureSpec, out *kops.A out.ResourceGroupName = in.ResourceGroupName out.RouteTableName = in.RouteTableName out.AdminUser = in.AdminUser + out.WorkloadIdentityClientID = in.WorkloadIdentityClientID return nil } @@ -1773,6 +1774,7 @@ func autoConvert_kops_AzureSpec_To_v1alpha2_AzureSpec(in *kops.AzureSpec, out *A out.ResourceGroupName = in.ResourceGroupName out.RouteTableName = in.RouteTableName out.AdminUser = in.AdminUser + out.WorkloadIdentityClientID = in.WorkloadIdentityClientID return nil } diff --git a/pkg/apis/kops/v1alpha3/componentconfig.go b/pkg/apis/kops/v1alpha3/componentconfig.go index 671ab5dcb4b46..0377ca7998861 100644 --- a/pkg/apis/kops/v1alpha3/componentconfig.go +++ b/pkg/apis/kops/v1alpha3/componentconfig.go @@ -955,6 +955,10 @@ type AzureSpec struct { RouteTableName string `json:"routeTableName,omitempty"` // AdminUser specifies the admin user of VMs. AdminUser string `json:"adminUser,omitempty"` + // WorkloadIdentityClientID is the client ID of the User-Assigned Managed Identity + // used for Azure Workload Identity. This is populated automatically by kops + // when UseServiceAccountExternalPermissions is enabled. + WorkloadIdentityClientID string `json:"workloadIdentityClientID,omitempty"` } // CloudConfiguration defines the cloud provider configuration diff --git a/pkg/apis/kops/v1alpha3/zz_generated.conversion.go b/pkg/apis/kops/v1alpha3/zz_generated.conversion.go index 4de2fc8d16339..712e426aefda9 100644 --- a/pkg/apis/kops/v1alpha3/zz_generated.conversion.go +++ b/pkg/apis/kops/v1alpha3/zz_generated.conversion.go @@ -1944,6 +1944,7 @@ func autoConvert_v1alpha3_AzureSpec_To_kops_AzureSpec(in *AzureSpec, out *kops.A out.ResourceGroupName = in.ResourceGroupName out.RouteTableName = in.RouteTableName out.AdminUser = in.AdminUser + out.WorkloadIdentityClientID = in.WorkloadIdentityClientID return nil } @@ -1959,6 +1960,7 @@ func autoConvert_kops_AzureSpec_To_v1alpha3_AzureSpec(in *kops.AzureSpec, out *A out.ResourceGroupName = in.ResourceGroupName out.RouteTableName = in.RouteTableName out.AdminUser = in.AdminUser + out.WorkloadIdentityClientID = in.WorkloadIdentityClientID return nil } diff --git a/pkg/apis/kops/validation/legacy.go b/pkg/apis/kops/validation/legacy.go index dfd27726b66df..cd5b9240e5f16 100644 --- a/pkg/apis/kops/validation/legacy.go +++ b/pkg/apis/kops/validation/legacy.go @@ -238,6 +238,8 @@ func validateServiceAccountIssuerDiscovery(c *kops.Cluster, said *kops.ServiceAc } case *vfs.GSPath: // No known restrictions currently. Added here to avoid falling into the default catch all below. + case *vfs.AzureBlobPath: + // Azure Blob Storage is supported as a discovery store. case *vfs.MemFSPath: // memfs is ok for tests; not OK otherwise if !base.IsClusterReadable() { @@ -245,7 +247,7 @@ func validateServiceAccountIssuerDiscovery(c *kops.Cluster, said *kops.ServiceAc allErrs = append(allErrs, field.Invalid(saidStoreField, discoveryStore, "S3 is the only supported VFS for discoveryStore")) } default: - allErrs = append(allErrs, field.Invalid(saidStoreField, discoveryStore, "S3 is the only supported VFS for discoveryStore")) + allErrs = append(allErrs, field.Invalid(saidStoreField, discoveryStore, "S3, GCS, and Azure Blob are the only supported VFS types for discoveryStore")) } } } diff --git a/pkg/model/azuremodel/workloadidentity.go b/pkg/model/azuremodel/workloadidentity.go new file mode 100644 index 0000000000000..0d5c0b1626b4e --- /dev/null +++ b/pkg/model/azuremodel/workloadidentity.go @@ -0,0 +1,98 @@ +/* +Copyright 2026 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package azuremodel + +import ( + "fmt" + + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/upup/pkg/fi/cloudup/azuretasks" +) + +// Azure built-in role definition IDs. +// See: https://learn.microsoft.com/azure/role-based-access-control/built-in-roles +const ( + // azureContributorRoleDefID is the ID of the built-in "Contributor" role. + azureContributorRoleDefID = "b24988ac-6180-42a0-ab88-20f7382dd24c" +) + +// WorkloadIdentityModelBuilder configures Azure Workload Identity resources +// (UAMI, federated identity credentials, and role assignments). +type WorkloadIdentityModelBuilder struct { + *AzureModelContext + Lifecycle fi.Lifecycle +} + +var _ fi.CloudupModelBuilder = &WorkloadIdentityModelBuilder{} + +func (b *WorkloadIdentityModelBuilder) Build(c *fi.CloudupModelBuilderContext) error { + if !b.UseServiceAccountExternalPermissions() { + return nil + } + + issuerURL := fi.ValueOf(b.Cluster.Spec.KubeAPIServer.ServiceAccountIssuer) + if issuerURL == "" { + return fmt.Errorf("serviceAccountIssuer must be set for Azure Workload Identity") + } + + rgName := b.Cluster.AzureResourceGroupName() + subscriptionID := b.Cluster.Spec.CloudProvider.Azure.SubscriptionID + identityName := b.Cluster.AzureWorkloadIdentityName() + + // UAMI task — idempotent, may already exist from pre-flight in apply_cluster.go. + uami := &azuretasks.ManagedIdentity{ + Name: fi.PtrTo(identityName), + Lifecycle: b.Lifecycle, + ResourceGroup: b.LinkToResourceGroup(), + Tags: map[string]*string{}, + } + c.AddTask(uami) + + // Role assignment: Contributor on the resource group. + resourceGroupScope := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s", subscriptionID, rgName) + c.AddTask(&azuretasks.RoleAssignment{ + Name: fi.PtrTo("wi-uami-contributor"), + Lifecycle: b.Lifecycle, + Scope: fi.PtrTo(resourceGroupScope), + ManagedIdentity: uami, + RoleDefID: fi.PtrTo(azureContributorRoleDefID), + }) + + // Federated Identity Credentials — one per service account. + saBindings := []struct { + name string + namespace string + sa string + }{ + {name: "fic-ccm", namespace: "kube-system", sa: "cloud-controller-manager"}, + {name: "fic-csi-azuredisk", namespace: "kube-system", sa: "csi-azuredisk-controller-sa"}, + } + + for _, binding := range saBindings { + c.AddTask(&azuretasks.FederatedIdentityCredential{ + Name: fi.PtrTo(binding.name), + Lifecycle: b.Lifecycle, + ManagedIdentity: uami, + ResourceGroup: b.LinkToResourceGroup(), + Issuer: fi.PtrTo(issuerURL), + Subject: fi.PtrTo(fmt.Sprintf("system:serviceaccount:%s:%s", binding.namespace, binding.sa)), + Audiences: []*string{fi.PtrTo("api://AzureADTokenExchange")}, + }) + } + + return nil +} diff --git a/pkg/model/components/discovery.go b/pkg/model/components/discovery.go index 55d3c517423ae..1bf5be4dbca2d 100644 --- a/pkg/model/components/discovery.go +++ b/pkg/model/components/discovery.go @@ -66,6 +66,11 @@ func (b *DiscoveryOptionsBuilder) BuildOptions(o *kops.Cluster) error { if err != nil { return err } + case *vfs.AzureBlobPath: + serviceAccountIssuer, err = base.GetHTTPsUrl() + if err != nil { + return err + } case *vfs.MemFSPath: if !base.IsClusterReadable() { // If this _is_ a test, we should call MarkClusterReadable diff --git a/pkg/model/issuerdiscovery.go b/pkg/model/issuerdiscovery.go index a7c71deaeb597..45fb3ea936439 100644 --- a/pkg/model/issuerdiscovery.go +++ b/pkg/model/issuerdiscovery.go @@ -123,6 +123,20 @@ func (b *IssuerDiscoveryModelBuilder) Build(c *fi.CloudupModelBuilderContext) er klog.Infof("using user managed serviceAccountIssuers") } + case *vfs.AzureBlobPath: + // Azure Blob Storage uses container-level public access (Private / Blob / + // Container), not per-object ACLs like S3 or GCS, so kops cannot flip a + // PublicACL on the individual discovery files. The user must pre-configure + // the container with anonymous "Blob" access before running kops; otherwise + // the OIDC discovery endpoint is unreachable and AAD token exchange fails. + isPublic, err := discoveryStore.IsBucketPublic(ctx) + if err != nil { + return fmt.Errorf("checking if Azure Blob container was public: %w", err) + } + if !isPublic { + return fmt.Errorf("serviceAccountIssuers Azure Blob storage %q is not public", discoveryStore.Path()) + } + case *vfs.MemFSPath: // ok diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_linux_virtual_machine_scale_set_control-plane-eastus-1.masters.minimal-azure.example.com_user_data b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_linux_virtual_machine_scale_set_control-plane-eastus-1.masters.minimal-azure.example.com_user_data new file mode 100644 index 0000000000000..8946abf065166 --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_linux_virtual_machine_scale_set_control-plane-eastus-1.masters.minimal-azure.example.com_user_data @@ -0,0 +1,134 @@ +#!/bin/bash +set -o errexit +set -o nounset +set -o pipefail + +NODEUP_URL_AMD64=https://artifacts.k8s.io/binaries/kops/1.34.0-beta.1/linux/amd64/nodeup,https://github.com/kubernetes/kops/releases/download/v1.34.0-beta.1/nodeup-linux-amd64 +NODEUP_HASH_AMD64=c86e072f622b91546b7b3f3cb1a0f8a131e48b966ad018a0ac1520ceedf37725 +NODEUP_URL_ARM64=https://artifacts.k8s.io/binaries/kops/1.34.0-beta.1/linux/arm64/nodeup,https://github.com/kubernetes/kops/releases/download/v1.34.0-beta.1/nodeup-linux-arm64 +NODEUP_HASH_ARM64=64a9a9510538a449e85d05e13e3cd98b80377d68a673447c26821d40f00f0075 + +export AZURE_STORAGE_ACCOUNT=teststorage + + + + +sysctl -w net.core.rmem_max=16777216 || true +sysctl -w net.core.wmem_max=16777216 || true +sysctl -w net.ipv4.tcp_rmem='4096 87380 16777216' || true +sysctl -w net.ipv4.tcp_wmem='4096 87380 16777216' || true + + +function ensure-install-dir() { + INSTALL_DIR="/opt/kops" + # On ContainerOS, we install under /var/lib/toolbox; /opt is ro and noexec + if [[ -d /var/lib/toolbox ]]; then + INSTALL_DIR="/var/lib/toolbox/kops" + fi + mkdir -p ${INSTALL_DIR}/bin + mkdir -p ${INSTALL_DIR}/conf + cd ${INSTALL_DIR} +} + +# Retry a download until we get it. args: name, sha, urls +download-or-bust() { + echo "== Downloading $1 with hash $2 from $3 ==" + local -r file="$1" + local -r hash="$2" + local -a urls + IFS=, read -r -a urls <<< "$3" + + if [[ -f "${file}" ]]; then + if ! validate-hash "${file}" "${hash}"; then + rm -f "${file}" + else + return 0 + fi + fi + + while true; do + for url in "${urls[@]}"; do + commands=( + "curl -f --compressed -Lo ${file} --connect-timeout 20 --retry 6 --retry-delay 10" + "wget --compression=auto -O ${file} --connect-timeout=20 --tries=6 --wait=10" + "curl -f -Lo ${file} --connect-timeout 20 --retry 6 --retry-delay 10" + "wget -O ${file} --connect-timeout=20 --tries=6 --wait=10" + ) + for cmd in "${commands[@]}"; do + echo "== Downloading ${url} using ${cmd} ==" + if ! (${cmd} "${url}"); then + echo "== Failed to download ${url} using ${cmd} ==" + continue + fi + if ! validate-hash "${file}" "${hash}"; then + echo "== Failed to validate hash for ${url} ==" + rm -f "${file}" + else + echo "== Downloaded ${url} with hash ${hash} ==" + return 0 + fi + done + done + + echo "== All downloads failed; sleeping before retrying ==" + sleep 60 + done +} + +validate-hash() { + local -r file="$1" + local -r expected="$2" + local actual + + actual=$(sha256sum "${file}" | awk '{ print $1 }') || true + if [[ "${actual}" != "${expected}" ]]; then + echo "== File ${file} is corrupted; hash ${actual} doesn't match expected ${expected} ==" + return 1 + fi +} + +function download-release() { + case "$(uname -m)" in + x86_64*|i?86_64*|amd64*) + NODEUP_URL="${NODEUP_URL_AMD64}" + NODEUP_HASH="${NODEUP_HASH_AMD64}" + ;; + aarch64*|arm64*) + NODEUP_URL="${NODEUP_URL_ARM64}" + NODEUP_HASH="${NODEUP_HASH_ARM64}" + ;; + *) + echo "Unsupported host arch: $(uname -m)" >&2 + exit 1 + ;; + esac + + cd ${INSTALL_DIR}/bin + download-or-bust nodeup "${NODEUP_HASH}" "${NODEUP_URL}" + + chmod +x nodeup + + echo "== Running nodeup ==" + # We can't run in the foreground because of https://github.com/docker/docker/issues/23793 + ( cd ${INSTALL_DIR}/bin; ./nodeup --install-systemd-unit --conf=${INSTALL_DIR}/conf/kube_env.yaml --v=8 ) +} + +#################################################################################### + +/bin/systemd-machine-id-setup || echo "== Failed to initialize the machine ID; ensure machine-id configured ==" + +echo "== nodeup node config starting ==" +ensure-install-dir + +cat > conf/kube_env.yaml << '__EOF_KUBE_ENV' +CloudProvider: azure +ClusterName: minimal-azure.example.com +ConfigBase: memfs://tests/minimal-azure.example.com +InstanceGroupName: control-plane-eastus-1 +InstanceGroupRole: ControlPlane +NodeupConfigHash: 4UV+G3bsNg4BjIo/PmocE7UsT0pVzwcZx5AROG8aKS8= + +__EOF_KUBE_ENV + +download-release +echo "== nodeup node config done ==" diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_linux_virtual_machine_scale_set_nodes.minimal-azure.example.com_user_data b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_linux_virtual_machine_scale_set_nodes.minimal-azure.example.com_user_data new file mode 100644 index 0000000000000..ed2ee50dbfb1b --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_linux_virtual_machine_scale_set_nodes.minimal-azure.example.com_user_data @@ -0,0 +1,157 @@ +#!/bin/bash +set -o errexit +set -o nounset +set -o pipefail + +NODEUP_URL_AMD64=https://artifacts.k8s.io/binaries/kops/1.34.0-beta.1/linux/amd64/nodeup,https://github.com/kubernetes/kops/releases/download/v1.34.0-beta.1/nodeup-linux-amd64 +NODEUP_HASH_AMD64=c86e072f622b91546b7b3f3cb1a0f8a131e48b966ad018a0ac1520ceedf37725 +NODEUP_URL_ARM64=https://artifacts.k8s.io/binaries/kops/1.34.0-beta.1/linux/arm64/nodeup,https://github.com/kubernetes/kops/releases/download/v1.34.0-beta.1/nodeup-linux-arm64 +NODEUP_HASH_ARM64=64a9a9510538a449e85d05e13e3cd98b80377d68a673447c26821d40f00f0075 + +export AZURE_STORAGE_ACCOUNT=teststorage + + + + +sysctl -w net.core.rmem_max=16777216 || true +sysctl -w net.core.wmem_max=16777216 || true +sysctl -w net.ipv4.tcp_rmem='4096 87380 16777216' || true +sysctl -w net.ipv4.tcp_wmem='4096 87380 16777216' || true + + +function ensure-install-dir() { + INSTALL_DIR="/opt/kops" + # On ContainerOS, we install under /var/lib/toolbox; /opt is ro and noexec + if [[ -d /var/lib/toolbox ]]; then + INSTALL_DIR="/var/lib/toolbox/kops" + fi + mkdir -p ${INSTALL_DIR}/bin + mkdir -p ${INSTALL_DIR}/conf + cd ${INSTALL_DIR} +} + +# Retry a download until we get it. args: name, sha, urls +download-or-bust() { + echo "== Downloading $1 with hash $2 from $3 ==" + local -r file="$1" + local -r hash="$2" + local -a urls + IFS=, read -r -a urls <<< "$3" + + if [[ -f "${file}" ]]; then + if ! validate-hash "${file}" "${hash}"; then + rm -f "${file}" + else + return 0 + fi + fi + + while true; do + for url in "${urls[@]}"; do + commands=( + "curl -f --compressed -Lo ${file} --connect-timeout 20 --retry 6 --retry-delay 10" + "wget --compression=auto -O ${file} --connect-timeout=20 --tries=6 --wait=10" + "curl -f -Lo ${file} --connect-timeout 20 --retry 6 --retry-delay 10" + "wget -O ${file} --connect-timeout=20 --tries=6 --wait=10" + ) + for cmd in "${commands[@]}"; do + echo "== Downloading ${url} using ${cmd} ==" + if ! (${cmd} "${url}"); then + echo "== Failed to download ${url} using ${cmd} ==" + continue + fi + if ! validate-hash "${file}" "${hash}"; then + echo "== Failed to validate hash for ${url} ==" + rm -f "${file}" + else + echo "== Downloaded ${url} with hash ${hash} ==" + return 0 + fi + done + done + + echo "== All downloads failed; sleeping before retrying ==" + sleep 60 + done +} + +validate-hash() { + local -r file="$1" + local -r expected="$2" + local actual + + actual=$(sha256sum "${file}" | awk '{ print $1 }') || true + if [[ "${actual}" != "${expected}" ]]; then + echo "== File ${file} is corrupted; hash ${actual} doesn't match expected ${expected} ==" + return 1 + fi +} + +function download-release() { + case "$(uname -m)" in + x86_64*|i?86_64*|amd64*) + NODEUP_URL="${NODEUP_URL_AMD64}" + NODEUP_HASH="${NODEUP_HASH_AMD64}" + ;; + aarch64*|arm64*) + NODEUP_URL="${NODEUP_URL_ARM64}" + NODEUP_HASH="${NODEUP_HASH_ARM64}" + ;; + *) + echo "Unsupported host arch: $(uname -m)" >&2 + exit 1 + ;; + esac + + cd ${INSTALL_DIR}/bin + download-or-bust nodeup "${NODEUP_HASH}" "${NODEUP_URL}" + + chmod +x nodeup + + echo "== Running nodeup ==" + # We can't run in the foreground because of https://github.com/docker/docker/issues/23793 + ( cd ${INSTALL_DIR}/bin; ./nodeup --install-systemd-unit --conf=${INSTALL_DIR}/conf/kube_env.yaml --v=8 ) +} + +#################################################################################### + +/bin/systemd-machine-id-setup || echo "== Failed to initialize the machine ID; ensure machine-id configured ==" + +echo "== nodeup node config starting ==" +ensure-install-dir + +cat > conf/kube_env.yaml << '__EOF_KUBE_ENV' +CloudProvider: azure +ClusterName: minimal-azure.example.com +ConfigServer: + CACertificates: | + -----BEGIN CERTIFICATE----- + MIIBbjCCARigAwIBAgIMFpANqBD8NSD82AUSMA0GCSqGSIb3DQEBCwUAMBgxFjAU + BgNVBAMTDWt1YmVybmV0ZXMtY2EwHhcNMjEwNzA3MDcwODAwWhcNMzEwNzA3MDcw + ODAwWjAYMRYwFAYDVQQDEw1rdWJlcm5ldGVzLWNhMFwwDQYJKoZIhvcNAQEBBQAD + SwAwSAJBANFI3zr0Tk8krsW8vwjfMpzJOlWQ8616vG3YPa2qAgI7V4oKwfV0yIg1 + jt+H6f4P/wkPAPTPTfRp9Iy8oHEEFw0CAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEG + MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNG3zVjTcLlJwDsJ4/K9DV7KohUA + MA0GCSqGSIb3DQEBCwUAA0EAB8d03fY2w7WKpfO29qI295pu2C4ca9AiVGOpgSc8 + tmQsq6rcxt3T+rb589PVtz0mw/cKTxOk6gH2CCC+yHfy2w== + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIBbjCCARigAwIBAgIMFpANvmSa0OAlYmXKMA0GCSqGSIb3DQEBCwUAMBgxFjAU + BgNVBAMTDWt1YmVybmV0ZXMtY2EwHhcNMjEwNzA3MDcwOTM2WhcNMzEwNzA3MDcw + OTM2WjAYMRYwFAYDVQQDEw1rdWJlcm5ldGVzLWNhMFwwDQYJKoZIhvcNAQEBBQAD + SwAwSAJBAMF6F4aZdpe0RUpyykaBpWwZCnwbffhYGOw+fs6RdLuUq7QCNmJm/Eq7 + WWOziMYDiI9SbclpD+6QiJ0N3EqppVUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEG + MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLImp6ARjPDAH6nhI+scWVt3Q9bn + MA0GCSqGSIb3DQEBCwUAA0EAVQVx5MUtuAIeePuP9o51xtpT2S6Fvfi8J4ICxnlA + 9B7UD2ushcVFPtaeoL9Gfu8aY4KJBeqqg5ojl4qmRnThjw== + -----END CERTIFICATE----- + servers: + - https://kops-controller.internal.minimal-azure.example.com:3988/ +InstanceGroupName: nodes +InstanceGroupRole: Node +NodeupConfigHash: zEPsuZ/dS0S1DKOCx6k664pxqEVGq7XpVY1UJ0LRI4w= + +__EOF_KUBE_ENV + +download-release +echo "== nodeup node config done ==" diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_cluster-completed.spec_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_cluster-completed.spec_source new file mode 100644 index 0000000000000..aabfaa1e84f9a --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_cluster-completed.spec_source @@ -0,0 +1,200 @@ +apiVersion: kops.k8s.io/v1alpha2 +kind: Cluster +metadata: + creationTimestamp: "2017-01-01T00:00:00Z" + name: minimal-azure.example.com +spec: + api: + dns: {} + loadBalancer: + type: Public + authorization: + rbac: {} + channel: stable + cloudConfig: + azure: + adminUser: admin-user + storageAccountID: /subscriptions/sub-321/resourceGroups/resource-group-name/providers/Microsoft.Storage/storageAccounts/teststorage + subscriptionId: sub-123 + tenantId: tenant-123 + manageStorageClasses: true + cloudControllerManager: + azureNodeManagerImage: mcr.microsoft.com/oss/kubernetes/azure-cloud-node-manager:v1.34.3 + image: mcr.microsoft.com/oss/kubernetes/azure-cloud-controller-manager:v1.34.3 + leaderElection: + leaderElect: true + logLevel: 2 + cloudProvider: azure + clusterDNSDomain: cluster.local + configBase: memfs://tests/minimal-azure.example.com + containerd: + logLevel: info + runc: + version: 1.3.4 + sandboxImage: registry.k8s.io/pause:3.10.1 + version: 2.1.6 + etcdClusters: + - backups: + backupStore: memfs://tests/minimal-azure.example.com/backups/etcd/main + cpuRequest: 200m + etcdMembers: + - encryptedVolume: true + instanceGroup: control-plane-eastus-1 + name: a + manager: + backupRetentionDays: 90 + memoryRequest: 100Mi + name: main + version: 3.6.6 + - backups: + backupStore: memfs://tests/minimal-azure.example.com/backups/etcd/events + cpuRequest: 100m + etcdMembers: + - encryptedVolume: true + instanceGroup: control-plane-eastus-1 + name: a + manager: + backupRetentionDays: 90 + memoryRequest: 100Mi + name: events + version: 3.6.6 + iam: + legacy: false + useServiceAccountExternalPermissions: true + keyStore: memfs://tests/minimal-azure.example.com/pki + kubeAPIServer: + allowPrivileged: true + anonymousAuth: false + apiAudiences: + - kubernetes.svc.default + apiServerCount: 1 + authorizationMode: Node,RBAC + bindAddress: 0.0.0.0 + enableAdmissionPlugins: + - DefaultStorageClass + - DefaultTolerationSeconds + - LimitRanger + - MutatingAdmissionWebhook + - NamespaceLifecycle + - NodeRestriction + - ResourceQuota + - RuntimeClass + - ServiceAccount + - ValidatingAdmissionPolicy + - ValidatingAdmissionWebhook + etcdServers: + - https://127.0.0.1:4001 + etcdServersOverrides: + - /events#https://127.0.0.1:4002 + image: registry.k8s.io/kube-apiserver:v1.35.0 + kubeletPreferredAddressTypes: + - InternalIP + - Hostname + - ExternalIP + logLevel: 2 + requestheaderAllowedNames: + - aggregator + requestheaderExtraHeaderPrefixes: + - X-Remote-Extra- + requestheaderGroupHeaders: + - X-Remote-Group + requestheaderUsernameHeaders: + - X-Remote-User + securePort: 443 + serviceAccountIssuer: https://discovery.example.com/minimal-azure.example.com + serviceAccountJWKSURI: https://discovery.example.com/minimal-azure.example.com/openid/v1/jwks + serviceClusterIPRange: 100.64.0.0/13 + storageBackend: etcd3 + kubeControllerManager: + allocateNodeCIDRs: true + attachDetachReconcileSyncPeriod: 1m0s + cloudProvider: external + clusterCIDR: 100.96.0.0/11 + clusterName: minimal-azure.example.com + configureCloudRoutes: false + image: registry.k8s.io/kube-controller-manager:v1.35.0 + leaderElection: + leaderElect: true + logLevel: 2 + useServiceAccountCredentials: true + kubeDNS: + cacheMaxConcurrent: 150 + cacheMaxSize: 1000 + cpuRequest: 100m + domain: cluster.local + memoryLimit: 170Mi + memoryRequest: 70Mi + nodeLocalDNS: + cpuRequest: 25m + enabled: false + image: registry.k8s.io/dns/k8s-dns-node-cache:1.26.0 + memoryRequest: 5Mi + provider: CoreDNS + serverIP: 100.64.0.10 + kubeProxy: + clusterCIDR: 100.96.0.0/11 + cpuRequest: 100m + image: registry.k8s.io/kube-proxy:v1.35.0 + logLevel: 2 + kubeScheduler: + image: registry.k8s.io/kube-scheduler:v1.35.0 + leaderElection: + leaderElect: true + logLevel: 2 + kubelet: + anonymousAuth: false + cgroupDriver: systemd + cgroupRoot: / + cloudProvider: external + clusterDNS: 100.64.0.10 + clusterDomain: cluster.local + enableDebuggingHandlers: true + evictionHard: memory.available<100Mi,nodefs.available<10%,nodefs.inodesFree<5%,imagefs.available<10%,imagefs.inodesFree<5% + kubeconfigPath: /var/lib/kubelet/kubeconfig + logLevel: 2 + podManifestPath: /etc/kubernetes/manifests + protectKernelDefaults: true + shutdownGracePeriod: 30s + shutdownGracePeriodCriticalPods: 10s + kubernetesApiAccess: + - 0.0.0.0/0 + - ::/0 + kubernetesVersion: 1.35.0 + masterKubelet: + anonymousAuth: false + cgroupDriver: systemd + cgroupRoot: / + cloudProvider: external + clusterDNS: 100.64.0.10 + clusterDomain: cluster.local + enableDebuggingHandlers: true + evictionHard: memory.available<100Mi,nodefs.available<10%,nodefs.inodesFree<5%,imagefs.available<10%,imagefs.inodesFree<5% + featureGates: + ImageVolume: "true" + kubeconfigPath: /var/lib/kubelet/kubeconfig + logLevel: 2 + podManifestPath: /etc/kubernetes/manifests + protectKernelDefaults: true + shutdownGracePeriod: 30s + shutdownGracePeriodCriticalPods: 10s + masterPublicName: api.minimal-azure.example.com + networkCIDR: 10.0.0.0/16 + networking: + cni: {} + nonMasqueradeCIDR: 100.64.0.0/10 + podCIDR: 100.96.0.0/11 + secretStore: memfs://tests/minimal-azure.example.com/secrets + serviceAccountIssuerDiscovery: + discoveryStore: memfs://discovery.example.com/minimal-azure.example.com + serviceClusterIPRange: 100.64.0.0/13 + sshAccess: + - 0.0.0.0/0 + - ::/0 + subnets: + - cidr: 10.0.0.0/24 + name: eastus + region: eastus + type: Public + topology: + dns: + type: None diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_discovery.json_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_discovery.json_source new file mode 100644 index 0000000000000..abdb3c1d0d9f8 --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_discovery.json_source @@ -0,0 +1,18 @@ +{ +"issuer": "https://discovery.example.com/minimal-azure.example.com", +"jwks_uri": "https://discovery.example.com/minimal-azure.example.com/openid/v1/jwks", +"authorization_endpoint": "urn:kubernetes:programmatic_authorization", +"response_types_supported": [ +"id_token" +], +"subject_types_supported": [ +"public" +], +"id_token_signing_alg_values_supported": [ +"RS256" +], +"claims_supported": [ +"sub", +"iss" +] +} diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_etcd-cluster-spec-events_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_etcd-cluster-spec-events_source new file mode 100644 index 0000000000000..e2a9cbd557016 --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_etcd-cluster-spec-events_source @@ -0,0 +1,4 @@ +{ + "memberCount": 1, + "etcdVersion": "3.6.6" +} diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_etcd-cluster-spec-main_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_etcd-cluster-spec-main_source new file mode 100644 index 0000000000000..e2a9cbd557016 --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_etcd-cluster-spec-main_source @@ -0,0 +1,4 @@ +{ + "memberCount": 1, + "etcdVersion": "3.6.6" +} diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_keys.json_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_keys.json_source new file mode 100644 index 0000000000000..ddcbc6ed75733 --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_keys.json_source @@ -0,0 +1,20 @@ +{ +"keys": [ +{ +"use": "sig", +"kty": "RSA", +"kid": "3mNcULfgtWECYyZWY5ow1rOHjiRwEZHx28HQcRec3Ew", +"alg": "RS256", +"n": "2JbeF8dNwqfEKKD65aGlVs58fWkA0qZdVLKw8qATzRBJTi1nqbj2kAR4gyy_C8Mxouxva_om9d7Sq8Ka55T7-w", +"e": "AQAB" +}, +{ +"use": "sig", +"kty": "RSA", +"kid": "G-cZ10iKJqrXhR15ivI7Lg2q_cuL0zN9ouL0vF67FLc", +"alg": "RS256", +"n": "o4Tridlsf4Yz3UAiup_scSTiG_OqxkUW3Fz7zGKvVcLeYj9GEIKuzoB1VFk1nboDq4cCuGLfdzaQdCQKPIsDuw", +"e": "AQAB" +} +] +} diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_kops-version.txt_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_kops-version.txt_source new file mode 100644 index 0000000000000..86942cf6d89c9 --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_kops-version.txt_source @@ -0,0 +1 @@ +1.34.0-beta.1 diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_manifests-etcdmanager-events-control-plane-eastus-1_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_manifests-etcdmanager-events-control-plane-eastus-1_source new file mode 100644 index 0000000000000..ca5eb0adcd2a2 --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_manifests-etcdmanager-events-control-plane-eastus-1_source @@ -0,0 +1,115 @@ +apiVersion: v1 +kind: Pod +metadata: + labels: + k8s-app: etcd-manager-events + name: etcd-manager-events + namespace: kube-system +spec: + containers: + - command: + - /bin/sh + - -c + - mkfifo /tmp/pipe; (tee -a /var/log/etcd.log < /tmp/pipe & ) ; exec /ko-app/etcd-manager + --backup-store=memfs://tests/minimal-azure.example.com/backups/etcd/events --client-urls=https://__name__:4002 + --cluster-name=etcd-events --containerized=true --dns-suffix=.internal.minimal-azure.example.com + --grpc-port=3997 --peer-urls=https://__name__:2381 --quarantine-client-urls=https://__name__:3995 + --v=6 --volume-name-tag=k8s.io_etcd_events --volume-provider=azure --volume-tag=k8s.io_etcd_events + --volume-tag=k8s.io_role_control_plane=1 --volume-tag=kubernetes.io_cluster_minimal-azure.example.com=owned + > /tmp/pipe 2>&1 + env: + - name: AZURE_STORAGE_ACCOUNT + value: teststorage + - name: ETCD_MANAGER_DAILY_BACKUPS_RETENTION + value: 90d + image: registry.k8s.io/etcd-manager/etcd-manager-slim:v3.0.20260227 + name: etcd-manager + resources: + requests: + cpu: 100m + memory: 100Mi + securityContext: + privileged: true + volumeMounts: + - mountPath: /rootfs + name: rootfs + - mountPath: /run + name: run + - mountPath: /etc/kubernetes/pki/etcd-manager + name: pki + - mountPath: /opt + name: opt + - mountPath: /opt/etcd-v3.4.13 + name: etcd-v3-4-13 + - mountPath: /opt/etcd-v3.4.3 + name: etcd-v3-4-13 + - mountPath: /opt/etcd-v3.5.0 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.1 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.13 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.17 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.21 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.23 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.24 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.25 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.3 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.4 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.6 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.7 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.9 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.6.5 + name: etcd-v3-6-6 + - mountPath: /opt/etcd-v3.6.6 + name: etcd-v3-6-6 + - mountPath: /var/log/etcd.log + name: varlogetcd + hostNetwork: true + hostPID: true + priorityClassName: system-cluster-critical + tolerations: + - key: CriticalAddonsOnly + operator: Exists + volumes: + - hostPath: + path: / + type: Directory + name: rootfs + - hostPath: + path: /run + type: DirectoryOrCreate + name: run + - hostPath: + path: /etc/kubernetes/pki/etcd-manager-events + type: DirectoryOrCreate + name: pki + - emptyDir: {} + name: opt + - image: + pullPolicy: IfNotPresent + reference: registry.k8s.io/etcd:v3.4.13 + name: etcd-v3-4-13 + - image: + pullPolicy: IfNotPresent + reference: registry.k8s.io/etcd:v3.5.25 + name: etcd-v3-5-25 + - image: + pullPolicy: IfNotPresent + reference: registry.k8s.io/etcd:v3.6.6 + name: etcd-v3-6-6 + - hostPath: + path: /var/log/etcd-events.log + type: FileOrCreate + name: varlogetcd +status: {} diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_manifests-etcdmanager-main-control-plane-eastus-1_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_manifests-etcdmanager-main-control-plane-eastus-1_source new file mode 100644 index 0000000000000..4c981b1f8b0d2 --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_manifests-etcdmanager-main-control-plane-eastus-1_source @@ -0,0 +1,115 @@ +apiVersion: v1 +kind: Pod +metadata: + labels: + k8s-app: etcd-manager-main + name: etcd-manager-main + namespace: kube-system +spec: + containers: + - command: + - /bin/sh + - -c + - mkfifo /tmp/pipe; (tee -a /var/log/etcd.log < /tmp/pipe & ) ; exec /ko-app/etcd-manager + --backup-store=memfs://tests/minimal-azure.example.com/backups/etcd/main --client-urls=https://__name__:4001 + --cluster-name=etcd --containerized=true --dns-suffix=.internal.minimal-azure.example.com + --grpc-port=3996 --peer-urls=https://__name__:2380 --quarantine-client-urls=https://__name__:3994 + --v=6 --volume-name-tag=k8s.io_etcd_main --volume-provider=azure --volume-tag=k8s.io_etcd_main + --volume-tag=k8s.io_role_control_plane=1 --volume-tag=kubernetes.io_cluster_minimal-azure.example.com=owned + > /tmp/pipe 2>&1 + env: + - name: AZURE_STORAGE_ACCOUNT + value: teststorage + - name: ETCD_MANAGER_DAILY_BACKUPS_RETENTION + value: 90d + image: registry.k8s.io/etcd-manager/etcd-manager-slim:v3.0.20260227 + name: etcd-manager + resources: + requests: + cpu: 200m + memory: 100Mi + securityContext: + privileged: true + volumeMounts: + - mountPath: /rootfs + name: rootfs + - mountPath: /run + name: run + - mountPath: /etc/kubernetes/pki/etcd-manager + name: pki + - mountPath: /opt + name: opt + - mountPath: /opt/etcd-v3.4.13 + name: etcd-v3-4-13 + - mountPath: /opt/etcd-v3.4.3 + name: etcd-v3-4-13 + - mountPath: /opt/etcd-v3.5.0 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.1 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.13 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.17 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.21 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.23 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.24 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.25 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.3 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.4 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.6 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.7 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.5.9 + name: etcd-v3-5-25 + - mountPath: /opt/etcd-v3.6.5 + name: etcd-v3-6-6 + - mountPath: /opt/etcd-v3.6.6 + name: etcd-v3-6-6 + - mountPath: /var/log/etcd.log + name: varlogetcd + hostNetwork: true + hostPID: true + priorityClassName: system-cluster-critical + tolerations: + - key: CriticalAddonsOnly + operator: Exists + volumes: + - hostPath: + path: / + type: Directory + name: rootfs + - hostPath: + path: /run + type: DirectoryOrCreate + name: run + - hostPath: + path: /etc/kubernetes/pki/etcd-manager-main + type: DirectoryOrCreate + name: pki + - emptyDir: {} + name: opt + - image: + pullPolicy: IfNotPresent + reference: registry.k8s.io/etcd:v3.4.13 + name: etcd-v3-4-13 + - image: + pullPolicy: IfNotPresent + reference: registry.k8s.io/etcd:v3.5.25 + name: etcd-v3-5-25 + - image: + pullPolicy: IfNotPresent + reference: registry.k8s.io/etcd:v3.6.6 + name: etcd-v3-6-6 + - hostPath: + path: /var/log/etcd.log + type: FileOrCreate + name: varlogetcd +status: {} diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_manifests-static-kube-apiserver-healthcheck_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_manifests-static-kube-apiserver-healthcheck_source new file mode 100644 index 0000000000000..7867d3e53581a --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_manifests-static-kube-apiserver-healthcheck_source @@ -0,0 +1,32 @@ +apiVersion: v1 +kind: Pod +metadata: {} +spec: + containers: + - args: + - --ca-cert=/secrets/ca.crt + - --client-cert=/secrets/client.crt + - --client-key=/secrets/client.key + image: registry.k8s.io/kops/kube-apiserver-healthcheck:1.34.0-beta.1 + livenessProbe: + httpGet: + host: 127.0.0.1 + path: /.kube-apiserver-healthcheck/healthz + port: 3990 + initialDelaySeconds: 5 + timeoutSeconds: 5 + name: healthcheck + resources: {} + securityContext: + runAsNonRoot: true + runAsUser: 10012 + volumeMounts: + - mountPath: /secrets + name: healthcheck-secrets + readOnly: true + volumes: + - hostPath: + path: /etc/kubernetes/kube-apiserver-healthcheck/secrets + type: Directory + name: healthcheck-secrets +status: {} diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-azure-cloud-controller.addons.k8s.io-k8s-1.31_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-azure-cloud-controller.addons.k8s.io-k8s-1.31_source new file mode 100644 index 0000000000000..46690f6918287 --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-azure-cloud-controller.addons.k8s.io-k8s-1.31_source @@ -0,0 +1,426 @@ +apiVersion: v1 +kind: Secret +metadata: + labels: + addon.kops.k8s.io/name: azure-cloud-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: azure-cloud-controller.addons.k8s.io + name: azure-cloud-provider + namespace: kube-system +stringData: + cloud-config: |- + { + "tenantId": "tenant-123", + "subscriptionId": "sub-123", + "resourceGroup": "minimal-azure.example.com", + "location": "eastus", + "vnetName": "minimal-azure.example.com", + "subnetName": "eastus", + "securityGroupName": "minimal-azure.example.com", + "useFederatedWorkloadIdentityExtension": true, + "aadFederatedTokenFile": "/var/run/secrets/azure/tokens/azure-identity-token", + "aadClientID": "", + "useInstanceMetadata": true, + "disableAvailabilitySetNodes": true + } +type: Opaque + +--- + +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + addon.kops.k8s.io/name: azure-cloud-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: azure-cloud-controller.addons.k8s.io + name: cloud-controller-manager + namespace: kube-system + +--- + +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + addon.kops.k8s.io/name: azure-cloud-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: azure-cloud-controller.addons.k8s.io + k8s-app: cloud-node-manager + kubernetes.io/cluster-service: "true" + name: cloud-node-manager + namespace: kube-system + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + addon.kops.k8s.io/name: azure-cloud-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: azure-cloud-controller.addons.k8s.io + k8s-app: cloud-node-manager + kubernetes.io/cluster-service: "true" + name: cloud-node-manager +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - watch + - list + - get + - update + - patch +- apiGroups: + - "" + resources: + - nodes/status + verbs: + - patch + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + annotations: + rbac.authorization.kubernetes.io/autoupdate: "true" + labels: + addon.kops.k8s.io/name: azure-cloud-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: azure-cloud-controller.addons.k8s.io + k8s-app: cloud-controller-manager + name: system:cloud-controller-manager +rules: +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - update +- apiGroups: + - "" + resources: + - nodes + verbs: + - '*' +- apiGroups: + - "" + resources: + - nodes/status + verbs: + - patch +- apiGroups: + - "" + resources: + - services + verbs: + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - services/status + verbs: + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - create + - get + - list + - watch + - update +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list + - update + - watch +- apiGroups: + - "" + resources: + - endpoints + verbs: + - create + - get + - list + - watch + - update +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - create + - update +- apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - get + - list + - watch + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + addon.kops.k8s.io/name: azure-cloud-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: azure-cloud-controller.addons.k8s.io + name: system:cloud-controller-manager:extension-apiserver-authentication-reader + namespace: kube-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: extension-apiserver-authentication-reader +subjects: +- kind: ServiceAccount + name: cloud-controller-manager + namespace: kube-system +- apiGroup: "" + kind: User + name: cloud-controller-manager + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + addon.kops.k8s.io/name: azure-cloud-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: azure-cloud-controller.addons.k8s.io + k8s-app: cloud-node-manager + kubernetes.io/cluster-service: "true" + name: cloud-node-manager +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cloud-node-manager +subjects: +- kind: ServiceAccount + name: cloud-node-manager + namespace: kube-system + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + addon.kops.k8s.io/name: azure-cloud-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: azure-cloud-controller.addons.k8s.io + name: system:cloud-controller-manager +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:cloud-controller-manager +subjects: +- kind: ServiceAccount + name: cloud-controller-manager + namespace: kube-system +- kind: User + name: cloud-controller-manager + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + addon.kops.k8s.io/name: azure-cloud-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + component: cloud-controller-manager + k8s-addon: azure-cloud-controller.addons.k8s.io + name: cloud-controller-manager + namespace: kube-system +spec: + replicas: 1 + selector: + matchLabels: + component: cloud-controller-manager + tier: control-plane + template: + metadata: + labels: + component: cloud-controller-manager + kops.k8s.io/managed-by: kops + tier: control-plane + spec: + containers: + - args: + - --allocate-node-cidrs=false + - --cloud-config=/etc/kubernetes/azure.json + - --cloud-provider=azure + - --cluster-cidr=100.96.0.0/11 + - --cluster-name=minimal-azure.example.com + - --configure-cloud-routes=false + - --controllers=*,-cloud-node + - --leader-elect=true + - --route-reconciliation-period=10s + - --secure-port=10268 + - --v=2 + command: + - cloud-controller-manager + env: + - name: KUBERNETES_SERVICE_HOST + value: 127.0.0.1 + image: mcr.microsoft.com/oss/kubernetes/azure-cloud-controller-manager:v1.34.3 + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + path: /healthz + port: 10268 + scheme: HTTPS + initialDelaySeconds: 20 + periodSeconds: 10 + timeoutSeconds: 5 + name: cloud-controller-manager + resources: + limits: + cpu: 4 + memory: 2Gi + requests: + cpu: 100m + memory: 128Mi + volumeMounts: + - mountPath: /etc/kubernetes + name: azure-cloud-config + readOnly: true + - mountPath: /etc/ssl + name: ssl-mount + readOnly: true + - mountPath: /var/run/secrets/azure/tokens + name: azure-identity-token + readOnly: true + hostNetwork: true + nodeSelector: + node-role.kubernetes.io/control-plane: "" + priorityClassName: system-node-critical + serviceAccountName: cloud-controller-manager + tolerations: + - effect: NoSchedule + key: node-role.kubernetes.io/master + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + - effect: NoExecute + key: node-role.kubernetes.io/etcd + topologySpreadConstraints: + - labelSelector: + matchLabels: + component: cloud-controller-manager + tier: control-plane + maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + volumes: + - name: azure-cloud-config + secret: + items: + - key: cloud-config + path: azure.json + secretName: azure-cloud-provider + - hostPath: + path: /etc/ssl + name: ssl-mount + - name: azure-identity-token + projected: + defaultMode: 420 + sources: + - serviceAccountToken: + audience: api://AzureADTokenExchange + expirationSeconds: 3600 + path: azure-identity-token + +--- + +apiVersion: apps/v1 +kind: DaemonSet +metadata: + labels: + addon.kops.k8s.io/name: azure-cloud-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + component: cloud-node-manager + k8s-addon: azure-cloud-controller.addons.k8s.io + kubernetes.io/cluster-service: "true" + name: cloud-node-manager + namespace: kube-system +spec: + selector: + matchLabels: + k8s-app: cloud-node-manager + template: + metadata: + annotations: + cluster-autoscaler.kubernetes.io/daemonset-pod: "true" + labels: + k8s-app: cloud-node-manager + kops.k8s.io/managed-by: kops + spec: + containers: + - args: + - --node-name=$(NODE_NAME) + - --v=2 + command: + - cloud-node-manager + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + image: mcr.microsoft.com/oss/kubernetes/azure-cloud-node-manager:v1.34.3 + imagePullPolicy: IfNotPresent + name: cloud-node-manager + resources: + limits: + cpu: 2 + memory: 512Mi + requests: + cpu: 50m + memory: 50Mi + hostNetwork: true + nodeSelector: + kubernetes.io/os: linux + priorityClassName: system-node-critical + serviceAccountName: cloud-node-manager + tolerations: + - key: CriticalAddonsOnly + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/master + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + - effect: NoExecute + operator: Exists + - effect: NoSchedule + operator: Exists diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-azuredisk-csi-driver.addons.k8s.io-k8s-1.31_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-azuredisk-csi-driver.addons.k8s.io-k8s-1.31_source new file mode 100644 index 0000000000000..1cf8bddd1c1c9 --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-azuredisk-csi-driver.addons.k8s.io-k8s-1.31_source @@ -0,0 +1,1044 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/instance: azuredisk-csi-driver + app.kubernetes.io/managed-by: kops + app.kubernetes.io/name: azuredisk-csi-driver + app.kubernetes.io/version: 1.34.0 + helm.sh/chart: azuredisk-csi-driver-1.34.0 + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: csi-azuredisk-controller-sa + namespace: kube-system + +--- + +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/instance: azuredisk-csi-driver + app.kubernetes.io/managed-by: kops + app.kubernetes.io/name: azuredisk-csi-driver + app.kubernetes.io/version: 1.34.0 + helm.sh/chart: azuredisk-csi-driver-1.34.0 + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: csi-azuredisk-node-sa + namespace: kube-system + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/instance: azuredisk-csi-driver + app.kubernetes.io/managed-by: kops + app.kubernetes.io/name: azuredisk-csi-driver + app.kubernetes.io/version: 1.34.0 + helm.sh/chart: azuredisk-csi-driver-1.34.0 + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: azuredisk-external-provisioner-role +rules: +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list + - watch + - create + - patch + - delete +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - get + - list + - watch + - update +- apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - get + - list + - watch + - create + - update + - patch +- apiGroups: + - storage.k8s.io + resources: + - csinodes + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - snapshot.storage.k8s.io + resources: + - volumesnapshots + verbs: + - get + - list +- apiGroups: + - snapshot.storage.k8s.io + resources: + - volumesnapshotcontents + verbs: + - get + - list +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - watch + - list + - delete + - update + - create + - patch + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/instance: azuredisk-csi-driver + app.kubernetes.io/managed-by: kops + app.kubernetes.io/name: azuredisk-csi-driver + app.kubernetes.io/version: 1.34.0 + helm.sh/chart: azuredisk-csi-driver-1.34.0 + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: azuredisk-external-attacher-role +rules: +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list + - watch + - update +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch +- apiGroups: + - csi.storage.k8s.io + resources: + - csinodeinfos + verbs: + - get + - list + - watch +- apiGroups: + - storage.k8s.io + resources: + - volumeattachments + verbs: + - get + - list + - watch + - update + - patch +- apiGroups: + - storage.k8s.io + resources: + - volumeattachments/status + verbs: + - get + - list + - watch + - update + - patch +- apiGroups: + - storage.k8s.io + resources: + - volumeattributesclasses + verbs: + - get +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - watch + - list + - delete + - update + - create + - patch + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/instance: azuredisk-csi-driver + app.kubernetes.io/managed-by: kops + app.kubernetes.io/name: azuredisk-csi-driver + app.kubernetes.io/version: 1.34.0 + helm.sh/chart: azuredisk-csi-driver-1.34.0 + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: azuredisk-external-snapshotter-role +rules: +- apiGroups: + - "" + resources: + - events + verbs: + - list + - watch + - create + - update + - patch +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list +- apiGroups: + - snapshot.storage.k8s.io + resources: + - volumesnapshotclasses + verbs: + - get + - list + - watch +- apiGroups: + - snapshot.storage.k8s.io + resources: + - volumesnapshotcontents + verbs: + - create + - get + - list + - watch + - update + - delete + - patch +- apiGroups: + - snapshot.storage.k8s.io + resources: + - volumesnapshotcontents/status + verbs: + - update + - patch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - watch + - list + - delete + - update + - create + - patch + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/instance: azuredisk-csi-driver + app.kubernetes.io/managed-by: kops + app.kubernetes.io/name: azuredisk-csi-driver + app.kubernetes.io/version: 1.34.0 + helm.sh/chart: azuredisk-csi-driver-1.34.0 + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: azuredisk-external-resizer-role +rules: +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list + - watch + - update + - patch +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - persistentvolumeclaims/status + verbs: + - update + - patch +- apiGroups: + - "" + resources: + - events + verbs: + - list + - watch + - create + - update + - patch +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - watch + - list + - delete + - update + - create + - patch +- apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch +- apiGroups: + - storage.k8s.io + resources: + - volumeattributesclasses + verbs: + - get + - list + - watch + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: csi-azuredisk-controller-secret-role +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: csi-azuredisk-node-role +rules: +- apiGroups: + - "" + resources: + - secrets + verbs: + - get +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - patch +- apiGroups: + - storage.k8s.io + resources: + - csinodes + verbs: + - get +- apiGroups: + - storage.k8s.io + resources: + - volumeattachments + verbs: + - get + - list + - watch + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/instance: azuredisk-csi-driver + app.kubernetes.io/managed-by: kops + app.kubernetes.io/name: azuredisk-csi-driver + app.kubernetes.io/version: 1.34.0 + helm.sh/chart: azuredisk-csi-driver-1.34.0 + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: azuredisk-csi-provisioner-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: azuredisk-external-provisioner-role +subjects: +- kind: ServiceAccount + name: csi-azuredisk-controller-sa + namespace: kube-system + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/instance: azuredisk-csi-driver + app.kubernetes.io/managed-by: kops + app.kubernetes.io/name: azuredisk-csi-driver + app.kubernetes.io/version: 1.34.0 + helm.sh/chart: azuredisk-csi-driver-1.34.0 + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: azuredisk-csi-attacher-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: azuredisk-external-attacher-role +subjects: +- kind: ServiceAccount + name: csi-azuredisk-controller-sa + namespace: kube-system + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/instance: azuredisk-csi-driver + app.kubernetes.io/managed-by: kops + app.kubernetes.io/name: azuredisk-csi-driver + app.kubernetes.io/version: 1.34.0 + helm.sh/chart: azuredisk-csi-driver-1.34.0 + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: azuredisk-csi-snapshotter-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: azuredisk-external-snapshotter-role +subjects: +- kind: ServiceAccount + name: csi-azuredisk-controller-sa + namespace: kube-system + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/instance: azuredisk-csi-driver + app.kubernetes.io/managed-by: kops + app.kubernetes.io/name: azuredisk-csi-driver + app.kubernetes.io/version: 1.34.0 + helm.sh/chart: azuredisk-csi-driver-1.34.0 + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: azuredisk-csi-resizer-role +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: azuredisk-external-resizer-role +subjects: +- kind: ServiceAccount + name: csi-azuredisk-controller-sa + namespace: kube-system + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: csi-azuredisk-controller-secret-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: csi-azuredisk-controller-secret-role +subjects: +- kind: ServiceAccount + name: csi-azuredisk-controller-sa + namespace: kube-system + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: csi-azuredisk-node-secret-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: csi-azuredisk-node-role +subjects: +- kind: ServiceAccount + name: csi-azuredisk-node-sa + namespace: kube-system + +--- + +apiVersion: apps/v1 +kind: DaemonSet +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/instance: azuredisk-csi-driver + app.kubernetes.io/managed-by: kops + app.kubernetes.io/name: azuredisk-csi-driver + app.kubernetes.io/version: 1.34.0 + helm.sh/chart: azuredisk-csi-driver-1.34.0 + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: csi-azuredisk-node + namespace: kube-system +spec: + selector: + matchLabels: + app: csi-azuredisk-node + template: + metadata: + labels: + app: csi-azuredisk-node + app.kubernetes.io/instance: azuredisk-csi-driver + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: azuredisk-csi-driver + app.kubernetes.io/version: 1.34.0 + helm.sh/chart: azuredisk-csi-driver-1.34.0 + kops.k8s.io/managed-by: kops + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: type + operator: NotIn + values: + - virtual-kubelet + containers: + - args: + - --csi-address=/csi/csi.sock + - --probe-timeout=10s + - --health-port=29603 + - --v=2 + image: mcr.microsoft.com/oss/v2/kubernetes-csi/livenessprobe:v2.17.0 + name: liveness-probe + resources: + limits: + memory: 100Mi + requests: + cpu: 10m + memory: 20Mi + securityContext: + capabilities: + drop: + - ALL + volumeMounts: + - mountPath: /csi + name: socket-dir + - args: + - --csi-address=$(ADDRESS) + - --kubelet-registration-path=$(DRIVER_REG_SOCK_PATH) + - --v=2 + env: + - name: ADDRESS + value: /csi/csi.sock + - name: DRIVER_REG_SOCK_PATH + value: /var/lib/kubelet/plugins/disk.csi.azure.com/csi.sock + image: mcr.microsoft.com/oss/v2/kubernetes-csi/csi-node-driver-registrar:v2.15.0 + name: node-driver-registrar + resources: + limits: + memory: 100Mi + requests: + cpu: 10m + memory: 20Mi + securityContext: + capabilities: + drop: + - ALL + volumeMounts: + - mountPath: /csi + name: socket-dir + - mountPath: /registration + name: registration-dir + - args: + - --v=5 + - --endpoint=$(CSI_ENDPOINT) + - --nodeid=$(KUBE_NODE_NAME) + - --enable-perf-optimization=true + - --drivername=disk.csi.azure.com + - --volume-attach-limit=-1 + - --reserved-data-disk-slot-num=0 + - --cloud-config-secret-name= + - --cloud-config-secret-namespace= + - --custom-user-agent= + - --user-agent-suffix=kops + - --allow-empty-cloud-config=true + - --support-zone=true + - --get-node-info-from-labels=false + - --get-nodeid-from-imds=false + - --enable-otel-tracing=false + - --metrics-address=0.0.0.0:29605 + - --remove-not-ready-taint=true + env: + - name: AZURE_CREDENTIAL_FILE + valueFrom: + configMapKeyRef: + key: path + name: azure-cred-file + optional: true + - name: CSI_ENDPOINT + value: unix:///csi/csi.sock + - name: KUBE_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: AZURE_GO_SDK_LOG_LEVEL + value: null + image: mcr.microsoft.com/oss/v2/kubernetes-csi/azuredisk-csi:v1.34.0 + imagePullPolicy: IfNotPresent + lifecycle: + preStop: + exec: + command: + - /azurediskplugin + - --pre-stop-hook=true + livenessProbe: + failureThreshold: 5 + httpGet: + path: /healthz + port: healthz + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 30 + name: azuredisk + ports: + - containerPort: 29603 + name: healthz + protocol: TCP + - containerPort: 29605 + name: metrics + protocol: TCP + resources: + limits: + memory: 1000Mi + requests: + cpu: 10m + memory: 20Mi + securityContext: + privileged: true + volumeMounts: + - mountPath: /csi + name: socket-dir + - mountPath: /var/lib/kubelet/ + mountPropagation: Bidirectional + name: mountpoint-dir + - mountPath: /etc/kubernetes/ + name: azure-cred + - mountPath: /dev + name: device-dir + - mountPath: /sys/bus/scsi/devices + name: sys-devices-dir + - mountPath: /sys/class/ + name: sys-class + dnsPolicy: Default + hostNetwork: true + nodeSelector: + kubernetes.io/os: linux + priorityClassName: system-node-critical + securityContext: + seccompProfile: + type: RuntimeDefault + serviceAccountName: csi-azuredisk-node-sa + tolerations: + - operator: Exists + volumes: + - hostPath: + path: /var/lib/kubelet/plugins/disk.csi.azure.com + type: DirectoryOrCreate + name: socket-dir + - hostPath: + path: /var/lib/kubelet/ + type: DirectoryOrCreate + name: mountpoint-dir + - hostPath: + path: /var/lib/kubelet/plugins_registry/ + type: DirectoryOrCreate + name: registration-dir + - hostPath: + path: /etc/kubernetes/ + type: DirectoryOrCreate + name: azure-cred + - hostPath: + path: /dev + type: Directory + name: device-dir + - hostPath: + path: /sys/bus/scsi/devices + type: Directory + name: sys-devices-dir + - hostPath: + path: /sys/class/ + type: Directory + name: sys-class + updateStrategy: + rollingUpdate: + maxUnavailable: 1 + type: RollingUpdate + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/instance: azuredisk-csi-driver + app.kubernetes.io/managed-by: kops + app.kubernetes.io/name: azuredisk-csi-driver + app.kubernetes.io/version: 1.34.0 + helm.sh/chart: azuredisk-csi-driver-1.34.0 + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: csi-azuredisk-controller + namespace: kube-system +spec: + replicas: 1 + selector: + matchLabels: + app: csi-azuredisk-controller + template: + metadata: + labels: + app: csi-azuredisk-controller + app.kubernetes.io/instance: azuredisk-csi-driver + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: azuredisk-csi-driver + app.kubernetes.io/version: 1.34.0 + helm.sh/chart: azuredisk-csi-driver-1.34.0 + kops.k8s.io/managed-by: kops + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: node-role.kubernetes.io/control-plane + operator: Exists + containers: + - args: + - --feature-gates=Topology=true,HonorPVReclaimPolicy=true,VolumeAttributesClass=true + - --csi-address=$(ADDRESS) + - --v=2 + - --timeout=30s + - --leader-election + - --leader-election-namespace=kube-system + - --worker-threads=100 + - --extra-create-metadata=true + - --strict-topology=true + - --kube-api-qps=50 + - --kube-api-burst=100 + - --retry-interval-max=30m + env: + - name: ADDRESS + value: /csi/csi.sock + image: mcr.microsoft.com/oss/v2/kubernetes-csi/csi-provisioner:v6.1.0 + name: csi-provisioner + resources: + limits: + memory: 500Mi + requests: + cpu: 10m + memory: 20Mi + securityContext: + capabilities: + drop: + - ALL + volumeMounts: + - mountPath: /csi + name: socket-dir + - args: + - -v=2 + - -csi-address=$(ADDRESS) + - -timeout=600s + - -leader-election + - --leader-election-namespace=kube-system + - -worker-threads=1000 + - -kube-api-qps=200 + - -kube-api-burst=400 + env: + - name: ADDRESS + value: /csi/csi.sock + image: mcr.microsoft.com/oss/v2/kubernetes-csi/csi-attacher:v4.10.0 + name: csi-attacher + resources: + limits: + memory: 500Mi + requests: + cpu: 10m + memory: 20Mi + securityContext: + capabilities: + drop: + - ALL + volumeMounts: + - mountPath: /csi + name: socket-dir + - args: + - -csi-address=$(ADDRESS) + - -leader-election + - --leader-election-namespace=kube-system + - -v=2 + - --timeout=1200s + - --extra-create-metadata=true + - --retry-interval-max=30m + - --worker-threads=250 + env: + - name: ADDRESS + value: /csi/csi.sock + image: mcr.microsoft.com/oss/v2/kubernetes-csi/csi-snapshotter:v8.4.0 + name: csi-snapshotter + resources: + limits: + memory: 400Mi + requests: + cpu: 10m + memory: 20Mi + securityContext: + capabilities: + drop: + - ALL + volumeMounts: + - mountPath: /csi + name: socket-dir + - args: + - -csi-address=$(ADDRESS) + - -v=2 + - -leader-election + - --leader-election-namespace=kube-system + - -handle-volume-inuse-error=false + - --feature-gates=RecoverVolumeExpansionFailure=true,VolumeAttributesClass=true + - -timeout=240s + - --retry-interval-max=30m + env: + - name: ADDRESS + value: /csi/csi.sock + image: mcr.microsoft.com/oss/v2/kubernetes-csi/csi-resizer:v2.0.0 + name: csi-resizer + resources: + limits: + memory: 500Mi + requests: + cpu: 10m + memory: 20Mi + securityContext: + capabilities: + drop: + - ALL + volumeMounts: + - mountPath: /csi + name: socket-dir + - args: + - --csi-address=/csi/csi.sock + - --probe-timeout=3s + - --http-endpoint=localhost:29602 + - --v=2 + image: mcr.microsoft.com/oss/v2/kubernetes-csi/livenessprobe:v2.17.0 + name: liveness-probe + resources: + limits: + memory: 100Mi + requests: + cpu: 10m + memory: 20Mi + securityContext: + capabilities: + drop: + - ALL + volumeMounts: + - mountPath: /csi + name: socket-dir + - args: + - --v=5 + - --endpoint=$(CSI_ENDPOINT) + - --metrics-address=0.0.0.0:29604 + - --disable-avset-nodes=true + - --vm-type= + - --drivername=disk.csi.azure.com + - --cloud-config-secret-name=azure-cloud-provider + - --cloud-config-secret-namespace=kube-system + - --custom-user-agent= + - --user-agent-suffix=kops + - --allow-empty-cloud-config=false + - --vmss-cache-ttl-seconds=60 + - --enable-traffic-manager=false + - --traffic-manager-port=7788 + - --enable-otel-tracing=false + - --check-disk-lun-collision=true + - --vmss-detach-timeout-seconds=20 + env: + - name: AZURE_CREDENTIAL_FILE + valueFrom: + configMapKeyRef: + key: path + name: azure-cred-file + optional: true + - name: CSI_ENDPOINT + value: unix:///csi/csi.sock + - name: AZURE_GO_SDK_LOG_LEVEL + value: null + image: mcr.microsoft.com/oss/v2/kubernetes-csi/azuredisk-csi:v1.34.0 + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 5 + httpGet: + host: localhost + path: /healthz + port: 29602 + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + name: azuredisk + ports: + - containerPort: 29604 + name: metrics + protocol: TCP + resources: + limits: + memory: 500Mi + requests: + cpu: 10m + memory: 20Mi + securityContext: + capabilities: + drop: + - ALL + volumeMounts: + - mountPath: /csi + name: socket-dir + - mountPath: /etc/kubernetes/ + name: azure-cred + - mountPath: /var/run/secrets/azure/tokens + name: azure-identity-token + readOnly: true + hostNetwork: true + nodeSelector: + kubernetes.io/os: linux + priorityClassName: system-cluster-critical + securityContext: + seccompProfile: + type: RuntimeDefault + serviceAccountName: csi-azuredisk-controller-sa + tolerations: + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/controlplane + operator: Exists + - effect: NoSchedule + key: node-role.kubernetes.io/control-plane + operator: Exists + - effect: NoSchedule + key: CriticalAddonsOnly + operator: Exists + volumes: + - emptyDir: {} + name: socket-dir + - hostPath: + path: /etc/kubernetes/ + type: DirectoryOrCreate + name: azure-cred + - name: azure-identity-token + projected: + defaultMode: 420 + sources: + - serviceAccountToken: + audience: api://AzureADTokenExchange + expirationSeconds: 3600 + path: azure-identity-token + +--- + +apiVersion: storage.k8s.io/v1 +kind: CSIDriver +metadata: + annotations: + csiDriver: v1.34.0 + snapshot: v8.4.0 + labels: + addon.kops.k8s.io/name: azuredisk-csi-driver.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: azuredisk-csi-driver.addons.k8s.io + name: disk.csi.azure.com +spec: + attachRequired: true + fsGroupPolicy: File + podInfoOnMount: false diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-bootstrap_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-bootstrap_source new file mode 100644 index 0000000000000..889225a1e028b --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-bootstrap_source @@ -0,0 +1,136 @@ +kind: Addons +metadata: + name: bootstrap +spec: + addons: + - id: k8s-1.16 + manifest: kops-controller.addons.k8s.io/k8s-1.16.yaml + manifestHash: a2057dc197671928da52652a46ee4b94a0a7fec30e3bab523d3d5fefbde6eb2d + name: kops-controller.addons.k8s.io + needsRollingUpdate: control-plane + selector: + k8s-addon: kops-controller.addons.k8s.io + version: 9.99.0 + - id: k8s-1.12 + manifest: coredns.addons.k8s.io/k8s-1.12.yaml + manifestHash: 789ad3c2dd2f9efc6d834138323e4b788db7e73677e07715f498f53418c16447 + name: coredns.addons.k8s.io + selector: + k8s-addon: coredns.addons.k8s.io + version: 9.99.0 + - id: k8s-1.9 + manifest: kubelet-api.rbac.addons.k8s.io/k8s-1.9.yaml + manifestHash: da91eb5cf9a29f1b03510007d6d54603aef2fc23a305abc9ba496c510dfd3bc7 + name: kubelet-api.rbac.addons.k8s.io + selector: + k8s-addon: kubelet-api.rbac.addons.k8s.io + version: 9.99.0 + - manifest: limit-range.addons.k8s.io/v1.5.0.yaml + manifestHash: 686cc69e559a1c6f5e8b94e38de54a575a25c432ed5ceec565244b965fb5f07f + name: limit-range.addons.k8s.io + selector: + k8s-addon: limit-range.addons.k8s.io + version: 9.99.0 + - id: k8s-1.31 + manifest: storage-azure.addons.k8s.io/k8s-1.31.yaml + manifestHash: 4d21016eb0e4e957280c1ece90eacdc127c40a11c9e8686d7075df47949e4ada + name: storage-azure.addons.k8s.io + prune: + kinds: + - kind: ConfigMap + labelSelector: addon.kops.k8s.io/name=storage-azure.addons.k8s.io,app.kubernetes.io/managed-by=kops + - kind: Service + labelSelector: addon.kops.k8s.io/name=storage-azure.addons.k8s.io,app.kubernetes.io/managed-by=kops + - kind: ServiceAccount + labelSelector: addon.kops.k8s.io/name=storage-azure.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: admissionregistration.k8s.io + kind: MutatingWebhookConfiguration + labelSelector: addon.kops.k8s.io/name=storage-azure.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: admissionregistration.k8s.io + kind: ValidatingWebhookConfiguration + labelSelector: addon.kops.k8s.io/name=storage-azure.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: apps + kind: DaemonSet + labelSelector: addon.kops.k8s.io/name=storage-azure.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: apps + kind: Deployment + labelSelector: addon.kops.k8s.io/name=storage-azure.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: apps + kind: StatefulSet + labelSelector: addon.kops.k8s.io/name=storage-azure.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: policy + kind: PodDisruptionBudget + labelSelector: addon.kops.k8s.io/name=storage-azure.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: rbac.authorization.k8s.io + kind: ClusterRole + labelSelector: addon.kops.k8s.io/name=storage-azure.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: rbac.authorization.k8s.io + kind: ClusterRoleBinding + labelSelector: addon.kops.k8s.io/name=storage-azure.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: rbac.authorization.k8s.io + kind: Role + labelSelector: addon.kops.k8s.io/name=storage-azure.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: rbac.authorization.k8s.io + kind: RoleBinding + labelSelector: addon.kops.k8s.io/name=storage-azure.addons.k8s.io,app.kubernetes.io/managed-by=kops + selector: + k8s-addon: storage-azure.addons.k8s.io + version: 9.99.0 + - id: k8s-1.31 + manifest: azure-cloud-controller.addons.k8s.io/k8s-1.31.yaml + manifestHash: f937ba013cd383694b317c3f14198cb202791c1dbce7ec81f6577e45515b7635 + name: azure-cloud-controller.addons.k8s.io + selector: + k8s-addon: azure-cloud-controller.addons.k8s.io + version: 9.99.0 + - id: k8s-1.31 + manifest: azuredisk-csi-driver.addons.k8s.io/k8s-1.31.yaml + manifestHash: e48a7bda27c1b2ba5f26f9f1d67e945824645300a40892ae0386afb84ec309b3 + name: azuredisk-csi-driver.addons.k8s.io + prune: + kinds: + - kind: ConfigMap + labelSelector: addon.kops.k8s.io/name=azuredisk-csi-driver.addons.k8s.io,app.kubernetes.io/managed-by=kops + - kind: Service + labelSelector: addon.kops.k8s.io/name=azuredisk-csi-driver.addons.k8s.io,app.kubernetes.io/managed-by=kops + - kind: ServiceAccount + labelSelector: addon.kops.k8s.io/name=azuredisk-csi-driver.addons.k8s.io,app.kubernetes.io/managed-by=kops + namespaces: + - kube-system + - group: admissionregistration.k8s.io + kind: MutatingWebhookConfiguration + labelSelector: addon.kops.k8s.io/name=azuredisk-csi-driver.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: admissionregistration.k8s.io + kind: ValidatingWebhookConfiguration + labelSelector: addon.kops.k8s.io/name=azuredisk-csi-driver.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: apps + kind: DaemonSet + labelSelector: addon.kops.k8s.io/name=azuredisk-csi-driver.addons.k8s.io,app.kubernetes.io/managed-by=kops + namespaces: + - kube-system + - group: apps + kind: Deployment + labelSelector: addon.kops.k8s.io/name=azuredisk-csi-driver.addons.k8s.io,app.kubernetes.io/managed-by=kops + namespaces: + - kube-system + - group: apps + kind: StatefulSet + labelSelector: addon.kops.k8s.io/name=azuredisk-csi-driver.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: policy + kind: PodDisruptionBudget + labelSelector: addon.kops.k8s.io/name=azuredisk-csi-driver.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: rbac.authorization.k8s.io + kind: ClusterRole + labelSelector: addon.kops.k8s.io/name=azuredisk-csi-driver.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: rbac.authorization.k8s.io + kind: ClusterRoleBinding + labelSelector: addon.kops.k8s.io/name=azuredisk-csi-driver.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: rbac.authorization.k8s.io + kind: Role + labelSelector: addon.kops.k8s.io/name=azuredisk-csi-driver.addons.k8s.io,app.kubernetes.io/managed-by=kops + - group: rbac.authorization.k8s.io + kind: RoleBinding + labelSelector: addon.kops.k8s.io/name=azuredisk-csi-driver.addons.k8s.io,app.kubernetes.io/managed-by=kops + selector: + k8s-addon: azuredisk-csi-driver.addons.k8s.io + version: 9.99.0 diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-coredns.addons.k8s.io-k8s-1.12_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-coredns.addons.k8s.io-k8s-1.12_source new file mode 100644 index 0000000000000..3da21aaef7f1b --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-coredns.addons.k8s.io-k8s-1.12_source @@ -0,0 +1,384 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + addon.kops.k8s.io/name: coredns.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: coredns.addons.k8s.io + kubernetes.io/cluster-service: "true" + name: coredns + namespace: kube-system + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + addon.kops.k8s.io/name: coredns.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: coredns.addons.k8s.io + kubernetes.io/bootstrapping: rbac-defaults + name: system:coredns +rules: +- apiGroups: + - "" + resources: + - endpoints + - services + - pods + - namespaces + verbs: + - list + - watch +- apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + annotations: + rbac.authorization.kubernetes.io/autoupdate: "true" + labels: + addon.kops.k8s.io/name: coredns.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: coredns.addons.k8s.io + kubernetes.io/bootstrapping: rbac-defaults + name: system:coredns +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:coredns +subjects: +- kind: ServiceAccount + name: coredns + namespace: kube-system + +--- + +apiVersion: v1 +data: + Corefile: |- + .:53 { + errors + health { + lameduck 10s + } + ready + kubernetes cluster.local. in-addr.arpa ip6.arpa { + pods insecure + fallthrough in-addr.arpa ip6.arpa + ttl 30 + } + hosts /rootfs/etc/hosts minimal-azure.example.com { + ttl 30 + fallthrough + } + prometheus :9153 + forward . /etc/resolv.conf { + max_concurrent 1000 + } + cache 30 + loop + reload + loadbalance + } +kind: ConfigMap +metadata: + labels: + addon.kops.k8s.io/name: coredns.addons.k8s.io + addonmanager.kubernetes.io/mode: EnsureExists + app.kubernetes.io/managed-by: kops + k8s-addon: coredns.addons.k8s.io + name: coredns + namespace: kube-system + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + addon.kops.k8s.io/name: coredns.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: coredns.addons.k8s.io + k8s-app: kube-dns + kubernetes.io/cluster-service: "true" + kubernetes.io/name: CoreDNS + name: coredns + namespace: kube-system +spec: + selector: + matchLabels: + k8s-app: kube-dns + strategy: + rollingUpdate: + maxSurge: 10% + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + k8s-app: kube-dns + kops.k8s.io/managed-by: kops + spec: + containers: + - args: + - -conf + - /etc/coredns/Corefile + image: registry.k8s.io/coredns/coredns:v1.13.2 + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 5 + httpGet: + path: /health + port: 8080 + scheme: HTTP + initialDelaySeconds: 60 + successThreshold: 1 + timeoutSeconds: 5 + name: coredns + ports: + - containerPort: 53 + name: dns + protocol: UDP + - containerPort: 53 + name: dns-tcp + protocol: TCP + - containerPort: 9153 + name: metrics + protocol: TCP + readinessProbe: + failureThreshold: 1 + httpGet: + path: /ready + port: 8181 + scheme: HTTP + periodSeconds: 5 + timeoutSeconds: 5 + resources: + limits: + memory: 170Mi + requests: + cpu: 100m + memory: 70Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + add: + - NET_BIND_SERVICE + drop: + - all + readOnlyRootFilesystem: true + volumeMounts: + - mountPath: /etc/coredns + name: config-volume + readOnly: true + - mountPath: /rootfs/etc/hosts + name: etc-hosts + readOnly: true + dnsPolicy: Default + nodeSelector: + kubernetes.io/os: linux + priorityClassName: system-cluster-critical + serviceAccountName: coredns + tolerations: + - key: CriticalAddonsOnly + operator: Exists + topologySpreadConstraints: + - labelSelector: + matchLabels: + k8s-app: kube-dns + maxSkew: 1 + topologyKey: topology.kubernetes.io/zone + whenUnsatisfiable: ScheduleAnyway + - labelSelector: + matchLabels: + k8s-app: kube-dns + maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + volumes: + - configMap: + name: coredns + name: config-volume + - hostPath: + path: /etc/hosts + type: File + name: etc-hosts + +--- + +apiVersion: v1 +kind: Service +metadata: + annotations: + prometheus.io/port: "9153" + prometheus.io/scrape: "true" + labels: + addon.kops.k8s.io/name: coredns.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: coredns.addons.k8s.io + k8s-app: kube-dns + kubernetes.io/cluster-service: "true" + kubernetes.io/name: CoreDNS + name: kube-dns + namespace: kube-system + resourceVersion: "0" +spec: + clusterIP: 100.64.0.10 + ports: + - name: dns + port: 53 + protocol: UDP + - name: dns-tcp + port: 53 + protocol: TCP + - name: metrics + port: 9153 + protocol: TCP + selector: + k8s-app: kube-dns + +--- + +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + labels: + addon.kops.k8s.io/name: coredns.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: coredns.addons.k8s.io + name: kube-dns + namespace: kube-system +spec: + maxUnavailable: 50% + selector: + matchLabels: + k8s-app: kube-dns + +--- + +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + addon.kops.k8s.io/name: coredns.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: coredns.addons.k8s.io + name: coredns-autoscaler + namespace: kube-system + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + addon.kops.k8s.io/name: coredns.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: coredns.addons.k8s.io + name: coredns-autoscaler +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - list + - watch +- apiGroups: + - "" + resources: + - replicationcontrollers/scale + verbs: + - get + - update +- apiGroups: + - extensions + - apps + resources: + - deployments/scale + - replicasets/scale + verbs: + - get + - update +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - create + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + addon.kops.k8s.io/name: coredns.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: coredns.addons.k8s.io + name: coredns-autoscaler +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: coredns-autoscaler +subjects: +- kind: ServiceAccount + name: coredns-autoscaler + namespace: kube-system + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + addon.kops.k8s.io/name: coredns.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: coredns.addons.k8s.io + k8s-app: coredns-autoscaler + kubernetes.io/cluster-service: "true" + name: coredns-autoscaler + namespace: kube-system +spec: + selector: + matchLabels: + k8s-app: coredns-autoscaler + template: + metadata: + labels: + k8s-app: coredns-autoscaler + kops.k8s.io/managed-by: kops + spec: + containers: + - command: + - /cluster-proportional-autoscaler + - --namespace=kube-system + - --configmap=coredns-autoscaler + - --target=Deployment/coredns + - --default-params={"linear":{"coresPerReplica":256,"nodesPerReplica":16,"preventSinglePointFailure":true}} + - --logtostderr=true + - --v=2 + image: registry.k8s.io/cpa/cluster-proportional-autoscaler:v1.9.0 + name: autoscaler + resources: + requests: + cpu: 20m + memory: 10Mi + nodeSelector: + kubernetes.io/os: linux + priorityClassName: system-cluster-critical + serviceAccountName: coredns-autoscaler + tolerations: + - key: CriticalAddonsOnly + operator: Exists diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-kops-controller.addons.k8s.io-k8s-1.16_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-kops-controller.addons.k8s.io-k8s-1.16_source new file mode 100644 index 0000000000000..32f8923766d20 --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-kops-controller.addons.k8s.io-k8s-1.16_source @@ -0,0 +1,221 @@ +apiVersion: v1 +data: + config.yaml: | + {"clusterName":"minimal-azure.example.com","cloud":"azure","configBase":"memfs://tests/minimal-azure.example.com","secretStore":"memfs://tests/minimal-azure.example.com/secrets","server":{"Listen":":3988","provider":{"azure":{"clusterName":"minimal-azure.example.com"}},"serverKeyPath":"/etc/kubernetes/kops-controller/pki/kops-controller.key","serverCertificatePath":"/etc/kubernetes/kops-controller/pki/kops-controller.crt","caBasePath":"/etc/kubernetes/kops-controller/pki","signingCAs":["kubernetes-ca"],"certNames":["kubelet","kubelet-server","kube-proxy"]}} +kind: ConfigMap +metadata: + labels: + addon.kops.k8s.io/name: kops-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: kops-controller.addons.k8s.io + name: kops-controller + namespace: kube-system + +--- + +apiVersion: apps/v1 +kind: DaemonSet +metadata: + labels: + addon.kops.k8s.io/name: kops-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: kops-controller.addons.k8s.io + k8s-app: kops-controller + version: v1.34.0-beta.1 + name: kops-controller + namespace: kube-system +spec: + selector: + matchLabels: + k8s-app: kops-controller + template: + metadata: + annotations: + dns.alpha.kubernetes.io/internal: kops-controller.internal.minimal-azure.example.com + labels: + k8s-addon: kops-controller.addons.k8s.io + k8s-app: kops-controller + kops.k8s.io/managed-by: kops + version: v1.34.0-beta.1 + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: node-role.kubernetes.io/control-plane + operator: Exists + - key: kops.k8s.io/kops-controller-pki + operator: Exists + - matchExpressions: + - key: node-role.kubernetes.io/master + operator: Exists + - key: kops.k8s.io/kops-controller-pki + operator: Exists + containers: + - args: + - --v=2 + - --conf=/etc/kubernetes/kops-controller/config/config.yaml + command: null + env: + - name: KUBERNETES_SERVICE_HOST + value: 127.0.0.1 + - name: AZURE_STORAGE_ACCOUNT + value: teststorage + - name: KOPS_RUN_TOO_NEW_VERSION + value: "1" + image: registry.k8s.io/kops/kops-controller:1.34.0-beta.1 + name: kops-controller + resources: + requests: + cpu: 50m + memory: 50Mi + securityContext: + runAsNonRoot: true + runAsUser: 10011 + volumeMounts: + - mountPath: /etc/kubernetes/kops-controller/config/ + name: kops-controller-config + - mountPath: /etc/kubernetes/kops-controller/pki/ + name: kops-controller-pki + dnsPolicy: Default + hostNetwork: true + nodeSelector: null + priorityClassName: system-cluster-critical + serviceAccount: kops-controller + tolerations: + - key: node.cloudprovider.kubernetes.io/uninitialized + operator: Exists + - key: node.kubernetes.io/not-ready + operator: Exists + - key: node-role.kubernetes.io/master + operator: Exists + - key: node-role.kubernetes.io/control-plane + operator: Exists + volumes: + - configMap: + name: kops-controller + name: kops-controller-config + - hostPath: + path: /etc/kubernetes/kops-controller/ + type: Directory + name: kops-controller-pki + updateStrategy: + type: OnDelete + +--- + +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + addon.kops.k8s.io/name: kops-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: kops-controller.addons.k8s.io + name: kops-controller + namespace: kube-system + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + addon.kops.k8s.io/name: kops-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: kops-controller.addons.k8s.io + name: kops-controller +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch + - patch + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + addon.kops.k8s.io/name: kops-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: kops-controller.addons.k8s.io + name: kops-controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kops-controller +subjects: +- apiGroup: rbac.authorization.k8s.io + kind: User + name: system:serviceaccount:kube-system:kops-controller + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + addon.kops.k8s.io/name: kops-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: kops-controller.addons.k8s.io + name: kops-controller + namespace: kube-system +rules: +- apiGroups: + - "" + resources: + - events + verbs: + - get + - list + - watch + - create +- apiGroups: + - "" + - coordination.k8s.io + resourceNames: + - kops-controller-leader + resources: + - configmaps + - leases + verbs: + - get + - list + - watch + - patch + - update + - delete +- apiGroups: + - "" + - coordination.k8s.io + resources: + - configmaps + - leases + verbs: + - create + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + addon.kops.k8s.io/name: kops-controller.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: kops-controller.addons.k8s.io + name: kops-controller + namespace: kube-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: kops-controller +subjects: +- apiGroup: rbac.authorization.k8s.io + kind: User + name: system:serviceaccount:kube-system:kops-controller diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-kubelet-api.rbac.addons.k8s.io-k8s-1.9_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-kubelet-api.rbac.addons.k8s.io-k8s-1.9_source new file mode 100644 index 0000000000000..bb4d69ef0ef88 --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-kubelet-api.rbac.addons.k8s.io-k8s-1.9_source @@ -0,0 +1,16 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + addon.kops.k8s.io/name: kubelet-api.rbac.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: kubelet-api.rbac.addons.k8s.io + name: kops:system:kubelet-api-admin +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: system:kubelet-api-admin +subjects: +- apiGroup: rbac.authorization.k8s.io + kind: User + name: kubelet-api diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-limit-range.addons.k8s.io_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-limit-range.addons.k8s.io_source new file mode 100644 index 0000000000000..c8939ddffd3ad --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-limit-range.addons.k8s.io_source @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: LimitRange +metadata: + labels: + addon.kops.k8s.io/name: limit-range.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: limit-range.addons.k8s.io + name: limits + namespace: default +spec: + limits: + - defaultRequest: + cpu: 100m + type: Container diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-storage-azure.addons.k8s.io-k8s-1.31_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-storage-azure.addons.k8s.io-k8s-1.31_source new file mode 100644 index 0000000000000..25f1476260987 --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_minimal-azure.example.com-addons-storage-azure.addons.k8s.io-k8s-1.31_source @@ -0,0 +1,16 @@ +allowVolumeExpansion: true +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + annotations: + storageclass.kubernetes.io/is-default-class: "true" + labels: + addon.kops.k8s.io/name: storage-azure.addons.k8s.io + app.kubernetes.io/managed-by: kops + k8s-addon: storage-azure.addons.k8s.io + name: managed-csi +parameters: + skuName: StandardSSD_LRS +provisioner: disk.csi.azure.com +reclaimPolicy: Delete +volumeBindingMode: WaitForFirstConsumer diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_nodeupconfig-control-plane-eastus-1_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_nodeupconfig-control-plane-eastus-1_source new file mode 100644 index 0000000000000..69bf79f27a6c5 --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_nodeupconfig-control-plane-eastus-1_source @@ -0,0 +1,328 @@ +APIServerConfig: + API: + dns: {} + publicName: api.minimal-azure.example.com + ClusterDNSDomain: cluster.local + KubeAPIServer: + allowPrivileged: true + anonymousAuth: false + apiAudiences: + - kubernetes.svc.default + apiServerCount: 1 + authorizationMode: Node,RBAC + bindAddress: 0.0.0.0 + enableAdmissionPlugins: + - DefaultStorageClass + - DefaultTolerationSeconds + - LimitRanger + - MutatingAdmissionWebhook + - NamespaceLifecycle + - NodeRestriction + - ResourceQuota + - RuntimeClass + - ServiceAccount + - ValidatingAdmissionPolicy + - ValidatingAdmissionWebhook + etcdServers: + - https://127.0.0.1:4001 + etcdServersOverrides: + - /events#https://127.0.0.1:4002 + image: registry.k8s.io/kube-apiserver:v1.35.0 + kubeletPreferredAddressTypes: + - InternalIP + - Hostname + - ExternalIP + logLevel: 2 + requestheaderAllowedNames: + - aggregator + requestheaderExtraHeaderPrefixes: + - X-Remote-Extra- + requestheaderGroupHeaders: + - X-Remote-Group + requestheaderUsernameHeaders: + - X-Remote-User + securePort: 443 + serviceAccountIssuer: https://discovery.example.com/minimal-azure.example.com + serviceAccountJWKSURI: https://discovery.example.com/minimal-azure.example.com/openid/v1/jwks + serviceClusterIPRange: 100.64.0.0/13 + storageBackend: etcd3 + ServiceAccountPublicKeys: | + -----BEGIN RSA PUBLIC KEY----- + MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANiW3hfHTcKnxCig+uWhpVbOfH1pANKm + XVSysPKgE80QSU4tZ6m49pAEeIMsvwvDMaLsb2v6JvXe0qvCmueU+/sCAwEAAQ== + -----END RSA PUBLIC KEY----- + -----BEGIN RSA PUBLIC KEY----- + MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKOE64nZbH+GM91AIrqf7HEk4hvzqsZF + Ftxc+8xir1XC3mI/RhCCrs6AdVRZNZ26A6uHArhi33c2kHQkCjyLA7sCAwEAAQ== + -----END RSA PUBLIC KEY----- +Assets: + amd64: + - 2f4ed7778681649b81244426c29c5d98df60ccabf83d561d69e61c1cbb943ddf@https://dl.k8s.io/release/v1.35.0/bin/linux/amd64/kubelet + - a2e984a18a0c063279d692533031c1eff93a262afcc0afdc517375432d060989@https://dl.k8s.io/release/v1.35.0/bin/linux/amd64/kubectl + - b8e811578fb66023f90d2e238d80cec3bdfca4b44049af74c374d4fae0f9c090@https://github.com/containernetworking/plugins/releases/download/v1.6.2/cni-plugins-linux-amd64-v1.6.2.tgz + - 4793dc5c1f34ebf8402990d0050f3c294aa3c794cd5a4baa403c1cf10602326d@https://github.com/containerd/containerd/releases/download/v2.1.6/containerd-2.1.6-linux-amd64.tar.gz + - 5966ca40b6187b30e33bfc299c5f1fe72e8c1aa01cf3fefdadf391668f47f103@https://github.com/opencontainers/runc/releases/download/v1.3.4/runc.amd64 + - 86189e1e8de9692eb02daf2f06db8495f687ce2c4ba09a6b64f135990dfb315d@https://artifacts.k8s.io/binaries/kops/1.34.0-beta.1/linux/amd64/protokube,https://github.com/kubernetes/kops/releases/download/v1.34.0-beta.1/protokube-linux-amd64 + - 0172d3c560aebe1eb4e8599f71c0d8fc68e4eca880add8031de41c8057ca8e3c@https://artifacts.k8s.io/binaries/kops/1.34.0-beta.1/linux/amd64/channels,https://github.com/kubernetes/kops/releases/download/v1.34.0-beta.1/channels-linux-amd64 + arm64: + - aa658d077348b43d238f50966a583f4244b2a7d45590c77b3b165b7d44983ab8@https://dl.k8s.io/release/v1.35.0/bin/linux/arm64/kubelet + - 58f82f9fe796c375c5c4b8439850b0f3f4d401a52434052f2df46035a8789e25@https://dl.k8s.io/release/v1.35.0/bin/linux/arm64/kubectl + - 01e0e22acc7f7004e4588c1fe1871cc86d7ab562cd858e1761c4641d89ebfaa4@https://github.com/containernetworking/plugins/releases/download/v1.6.2/cni-plugins-linux-arm64-v1.6.2.tgz + - 88d6e32348c36628c8500a630c6dd4b3cb8c680b1d18dc8d1d19041f67757c6e@https://github.com/containerd/containerd/releases/download/v2.1.6/containerd-2.1.6-linux-arm64.tar.gz + - d6dcab36d1b6af1b72c7f0662e5fcf446a291271ba6006532b95c4144e19d428@https://github.com/opencontainers/runc/releases/download/v1.3.4/runc.arm64 + - 25b57b0555fad42e5762246334681bf1c943794fcecdb680a79e482be5c08815@https://artifacts.k8s.io/binaries/kops/1.34.0-beta.1/linux/arm64/protokube,https://github.com/kubernetes/kops/releases/download/v1.34.0-beta.1/protokube-linux-arm64 + - 04470f8313796032fce85b974da4fc26420f36931e574fff6d117d21caf22770@https://artifacts.k8s.io/binaries/kops/1.34.0-beta.1/linux/arm64/channels,https://github.com/kubernetes/kops/releases/download/v1.34.0-beta.1/channels-linux-arm64 +AzureAdminUser: admin-user +AzureLocation: eastus +AzureResourceGroup: minimal-azure.example.com +AzureRouteTableName: minimal-azure.example.com +AzureSecurityGroupName: minimal-azure.example.com +AzureSubnetName: eastus +AzureSubscriptionID: sub-123 +AzureTenantID: tenant-123 +CAs: + apiserver-aggregator-ca: | + -----BEGIN CERTIFICATE----- + MIIBgjCCASygAwIBAgIMFo3gINaZLHjisEcbMA0GCSqGSIb3DQEBCwUAMCIxIDAe + BgNVBAMTF2FwaXNlcnZlci1hZ2dyZWdhdG9yLWNhMB4XDTIxMDYzMDA0NTExMloX + DTMxMDYzMDA0NTExMlowIjEgMB4GA1UEAxMXYXBpc2VydmVyLWFnZ3JlZ2F0b3It + Y2EwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAyyE71AOU3go5XFegLQ6fidI0LhhM + x7CzpTzh2xWKcHUfbNI7itgJvC/+GlyG5W+DF5V7ba0IJiQLsFve0oLdewIDAQAB + o0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU + ALfqF5ZmfqvqORuJIFilZYKF3d0wDQYJKoZIhvcNAQELBQADQQAHAomFKsF4jvYX + WM/UzQXDj9nSAFTf8dBPCXyZZNotsOH7+P6W4mMiuVs8bAuGiXGUdbsQ2lpiT/Rk + CzMeMdr4 + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIBgjCCASygAwIBAgIMFo3gM0nxQpiX/agfMA0GCSqGSIb3DQEBCwUAMCIxIDAe + BgNVBAMTF2FwaXNlcnZlci1hZ2dyZWdhdG9yLWNhMB4XDTIxMDYzMDA0NTIzMVoX + DTMxMDYzMDA0NTIzMVowIjEgMB4GA1UEAxMXYXBpc2VydmVyLWFnZ3JlZ2F0b3It + Y2EwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAyyE71AOU3go5XFegLQ6fidI0LhhM + x7CzpTzh2xWKcHUfbNI7itgJvC/+GlyG5W+DF5V7ba0IJiQLsFve0oLdewIDAQAB + o0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU + ALfqF5ZmfqvqORuJIFilZYKF3d0wDQYJKoZIhvcNAQELBQADQQCXsoezoxXu2CEN + QdlXZOfmBT6cqxIX/RMHXhpHwRiqPsTO8IO2bVA8CSzxNwMuSv/ZtrMHoh8+PcVW + HLtkTXH8 + -----END CERTIFICATE----- + etcd-clients-ca: | + -----BEGIN CERTIFICATE----- + MIIBcjCCARygAwIBAgIMFo1ogHnr26DL9YkqMA0GCSqGSIb3DQEBCwUAMBoxGDAW + BgNVBAMTD2V0Y2QtY2xpZW50cy1jYTAeFw0yMTA2MjgxNjE5MDFaFw0zMTA2Mjgx + NjE5MDFaMBoxGDAWBgNVBAMTD2V0Y2QtY2xpZW50cy1jYTBcMA0GCSqGSIb3DQEB + AQUAA0sAMEgCQQDYlt4Xx03Cp8QooPrloaVWznx9aQDSpl1UsrDyoBPNEElOLWep + uPaQBHiDLL8LwzGi7G9r+ib13tKrwprnlPv7AgMBAAGjQjBAMA4GA1UdDwEB/wQE + AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQjlt4Ue54AbJPWlDpRM51s + x+PeBDANBgkqhkiG9w0BAQsFAANBAAZAdf8ROEVkr3Rf7I+s+CQOil2toadlKWOY + qCeJ2XaEROfp9aUTEIU1MGM3g57MPyAPPU7mURskuOQz6B1UFaY= + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIBcjCCARygAwIBAgIMFo1olfBnC/CsT+dqMA0GCSqGSIb3DQEBCwUAMBoxGDAW + BgNVBAMTD2V0Y2QtY2xpZW50cy1jYTAeFw0yMTA2MjgxNjIwMzNaFw0zMTA2Mjgx + NjIwMzNaMBoxGDAWBgNVBAMTD2V0Y2QtY2xpZW50cy1jYTBcMA0GCSqGSIb3DQEB + AQUAA0sAMEgCQQDYlt4Xx03Cp8QooPrloaVWznx9aQDSpl1UsrDyoBPNEElOLWep + uPaQBHiDLL8LwzGi7G9r+ib13tKrwprnlPv7AgMBAAGjQjBAMA4GA1UdDwEB/wQE + AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQjlt4Ue54AbJPWlDpRM51s + x+PeBDANBgkqhkiG9w0BAQsFAANBAF1xUz77PlUVUnd9duF8F7plou0TONC9R6/E + YQ8C6vM1b+9NSDGjCW8YmwEU2fBgskb/BBX2lwVZ32/RUEju4Co= + -----END CERTIFICATE----- + etcd-manager-ca-events: | + -----BEGIN CERTIFICATE----- + MIIBgDCCASqgAwIBAgIMFo+bKjm04vB4rNtaMA0GCSqGSIb3DQEBCwUAMCExHzAd + BgNVBAMTFmV0Y2QtbWFuYWdlci1jYS1ldmVudHMwHhcNMjEwNzA1MjAwOTU2WhcN + MzEwNzA1MjAwOTU2WjAhMR8wHQYDVQQDExZldGNkLW1hbmFnZXItY2EtZXZlbnRz + MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKiC8tndMlEFZ7qzeKxeKqFVjaYpsh/H + g7RxWo15+1kgH3suO0lxp9+RxSVv97hnsfbySTPZVhy2cIQj7eZtZt8CAwEAAaNC + MEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFBg6 + CEZkQNnRkARBwFce03AEWa+sMA0GCSqGSIb3DQEBCwUAA0EAJMnBThok/uUe8q8O + sS5q19KUuE8YCTUzMDj36EBKf6NX4NoakCa1h6kfQVtlMtEIMWQZCjbm8xGK5ffs + GS/VUw== + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIBgDCCASqgAwIBAgIMFo+bQ+EgIiBmGghjMA0GCSqGSIb3DQEBCwUAMCExHzAd + BgNVBAMTFmV0Y2QtbWFuYWdlci1jYS1ldmVudHMwHhcNMjEwNzA1MjAxMTQ2WhcN + MzEwNzA1MjAxMTQ2WjAhMR8wHQYDVQQDExZldGNkLW1hbmFnZXItY2EtZXZlbnRz + MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKFhHVVxxDGv8d1jBvtdSxz7KIVoBOjL + DMxsmTsINiQkTQaFlb+XPlnY1ar4+RhE519AFUkqfhypk4Zxqf1YFXUCAwEAAaNC + MEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNuW + LLH5c8kDubDbr6BHgedW0iJ9MA0GCSqGSIb3DQEBCwUAA0EAiKUoBoaGu7XzboFE + hjfKlX0TujqWuW3qMxDEJwj4dVzlSLrAoB/G01MJ+xxYKh456n48aG6N827UPXhV + cPfVNg== + -----END CERTIFICATE----- + etcd-manager-ca-main: | + -----BEGIN CERTIFICATE----- + MIIBfDCCASagAwIBAgIMFo+bKjm1c3jfv6hIMA0GCSqGSIb3DQEBCwUAMB8xHTAb + BgNVBAMTFGV0Y2QtbWFuYWdlci1jYS1tYWluMB4XDTIxMDcwNTIwMDk1NloXDTMx + MDcwNTIwMDk1NlowHzEdMBsGA1UEAxMUZXRjZC1tYW5hZ2VyLWNhLW1haW4wXDAN + BgkqhkiG9w0BAQEFAANLADBIAkEAxbkDbGYmCSShpRG3r+lzTOFujyuruRfjOhYm + ZRX4w1Utd5y63dUc98sjc9GGUYMHd+0k1ql/a48tGhnK6N6jJwIDAQABo0IwQDAO + BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWZLkbBFx + GAgPU4i62c52unSo7RswDQYJKoZIhvcNAQELBQADQQAj6Pgd0va/8FtkyMlnohLu + Gf4v8RJO6zk3Y6jJ4+cwWziipFM1ielMzSOZfFcCZgH3m5Io40is4hPSqyq2TOA6 + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIBfDCCASagAwIBAgIMFo+bQ+Eg8Si30gr4MA0GCSqGSIb3DQEBCwUAMB8xHTAb + BgNVBAMTFGV0Y2QtbWFuYWdlci1jYS1tYWluMB4XDTIxMDcwNTIwMTE0NloXDTMx + MDcwNTIwMTE0NlowHzEdMBsGA1UEAxMUZXRjZC1tYW5hZ2VyLWNhLW1haW4wXDAN + BgkqhkiG9w0BAQEFAANLADBIAkEAw33jzcd/iosN04b0WXbDt7B0c3sJ3aafcGLP + vG3xRB9N5bYr9+qZAq3mzAFkxscn4j1ce5b1/GKTDEAClmZgdQIDAQABo0IwQDAO + BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUE/h+3gDP + DvKwHRyiYlXM8voZ1wowDQYJKoZIhvcNAQELBQADQQBXuimeEoAOu5HN4hG7NqL9 + t40K3ZRhRZv3JQWnRVJCBDjg1rD0GQJR/n+DoWvbeijI5C9pNjr2pWSIYR1eYCvd + -----END CERTIFICATE----- + etcd-peers-ca-events: | + -----BEGIN CERTIFICATE----- + MIIBfDCCASagAwIBAgIMFo+bKjmxTPh3/lYJMA0GCSqGSIb3DQEBCwUAMB8xHTAb + BgNVBAMTFGV0Y2QtcGVlcnMtY2EtZXZlbnRzMB4XDTIxMDcwNTIwMDk1NloXDTMx + MDcwNTIwMDk1NlowHzEdMBsGA1UEAxMUZXRjZC1wZWVycy1jYS1ldmVudHMwXDAN + BgkqhkiG9w0BAQEFAANLADBIAkEAv5g4HF2xmrYyouJfY9jXx1M3gPLD/pupvxPY + xyjJw5pNCy5M5XGS3iTqRD5RDE0fWudVHFZKLIe8WPc06NApXwIDAQABo0IwQDAO + BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUf6xiDI+O + Yph1ziCGr2hZaQYt+fUwDQYJKoZIhvcNAQELBQADQQBBxj5hqEQstonTb8lnqeGB + DEYtUeAk4eR/HzvUMjF52LVGuvN3XVt+JTrFeKNvb6/RDUbBNRj3azalcUkpPh6V + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIBfDCCASagAwIBAgIMFo+bQ+Eq69jgzpKwMA0GCSqGSIb3DQEBCwUAMB8xHTAb + BgNVBAMTFGV0Y2QtcGVlcnMtY2EtZXZlbnRzMB4XDTIxMDcwNTIwMTE0NloXDTMx + MDcwNTIwMTE0NlowHzEdMBsGA1UEAxMUZXRjZC1wZWVycy1jYS1ldmVudHMwXDAN + BgkqhkiG9w0BAQEFAANLADBIAkEAo5Nj2CjX1qp3mEPw1H5nHAFWLoGNSLSlRFJW + 03NxaNPMFzL5PrCoyOXrX8/MWczuZYw0Crf8EPOOQWi2+W0XLwIDAQABo0IwQDAO + BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUxauhhKQh + cvdZND78rHe0RQVTTiswDQYJKoZIhvcNAQELBQADQQB+cq4jIS9q0zXslaRa+ViI + J+dviA3sMygbmSJO0s4DxYmoazKJblux5q0ASSvS9iL1l9ShuZ1dWyp2tpZawHyb + -----END CERTIFICATE----- + etcd-peers-ca-main: | + -----BEGIN CERTIFICATE----- + MIIBeDCCASKgAwIBAgIMFo+bKjmuLDDLcDHsMA0GCSqGSIb3DQEBCwUAMB0xGzAZ + BgNVBAMTEmV0Y2QtcGVlcnMtY2EtbWFpbjAeFw0yMTA3MDUyMDA5NTZaFw0zMTA3 + MDUyMDA5NTZaMB0xGzAZBgNVBAMTEmV0Y2QtcGVlcnMtY2EtbWFpbjBcMA0GCSqG + SIb3DQEBAQUAA0sAMEgCQQCyRaXWpwgN6INQqws9p/BvPElJv2Rno9dVTFhlQqDA + aUJXe7MBmiO4NJcW76EozeBh5ztR3/4NE1FM2x8TisS3AgMBAAGjQjBAMA4GA1Ud + DwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQtE1d49uSvpURf + OQ25Vlu6liY20DANBgkqhkiG9w0BAQsFAANBAAgLVaetJZcfOA3OIMMvQbz2Ydrt + uWF9BKkIad8jrcIrm3IkOtR8bKGmDIIaRKuG/ZUOL6NMe2fky3AAfKwleL4= + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIBeDCCASKgAwIBAgIMFo+bQ+EuVthBfuZvMA0GCSqGSIb3DQEBCwUAMB0xGzAZ + BgNVBAMTEmV0Y2QtcGVlcnMtY2EtbWFpbjAeFw0yMTA3MDUyMDExNDZaFw0zMTA3 + MDUyMDExNDZaMB0xGzAZBgNVBAMTEmV0Y2QtcGVlcnMtY2EtbWFpbjBcMA0GCSqG + SIb3DQEBAQUAA0sAMEgCQQCxNbycDZNx5V1ZOiXxZSvaFpHRwKeHDfcuMUitdoPt + naVMlMTGDWAMuCVmFHFAWohIYynemEegmZkZ15S7AErfAgMBAAGjQjBAMA4GA1Ud + DwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTAjQ8T4HclPIsC + qipEfUIcLP6jqTANBgkqhkiG9w0BAQsFAANBAJdZ17TN3HlWrH7HQgfR12UBwz8K + G9DurDznVaBVUYaHY8Sg5AvAXeb+yIF2JMmRR+bK+/G1QYY2D3/P31Ic2Oo= + -----END CERTIFICATE----- + kubernetes-ca: | + -----BEGIN CERTIFICATE----- + MIIBbjCCARigAwIBAgIMFpANqBD8NSD82AUSMA0GCSqGSIb3DQEBCwUAMBgxFjAU + BgNVBAMTDWt1YmVybmV0ZXMtY2EwHhcNMjEwNzA3MDcwODAwWhcNMzEwNzA3MDcw + ODAwWjAYMRYwFAYDVQQDEw1rdWJlcm5ldGVzLWNhMFwwDQYJKoZIhvcNAQEBBQAD + SwAwSAJBANFI3zr0Tk8krsW8vwjfMpzJOlWQ8616vG3YPa2qAgI7V4oKwfV0yIg1 + jt+H6f4P/wkPAPTPTfRp9Iy8oHEEFw0CAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEG + MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNG3zVjTcLlJwDsJ4/K9DV7KohUA + MA0GCSqGSIb3DQEBCwUAA0EAB8d03fY2w7WKpfO29qI295pu2C4ca9AiVGOpgSc8 + tmQsq6rcxt3T+rb589PVtz0mw/cKTxOk6gH2CCC+yHfy2w== + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIBbjCCARigAwIBAgIMFpANvmSa0OAlYmXKMA0GCSqGSIb3DQEBCwUAMBgxFjAU + BgNVBAMTDWt1YmVybmV0ZXMtY2EwHhcNMjEwNzA3MDcwOTM2WhcNMzEwNzA3MDcw + OTM2WjAYMRYwFAYDVQQDEw1rdWJlcm5ldGVzLWNhMFwwDQYJKoZIhvcNAQEBBQAD + SwAwSAJBAMF6F4aZdpe0RUpyykaBpWwZCnwbffhYGOw+fs6RdLuUq7QCNmJm/Eq7 + WWOziMYDiI9SbclpD+6QiJ0N3EqppVUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEG + MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLImp6ARjPDAH6nhI+scWVt3Q9bn + MA0GCSqGSIb3DQEBCwUAA0EAVQVx5MUtuAIeePuP9o51xtpT2S6Fvfi8J4ICxnlA + 9B7UD2ushcVFPtaeoL9Gfu8aY4KJBeqqg5ojl4qmRnThjw== + -----END CERTIFICATE----- +ClusterName: minimal-azure.example.com +ControlPlaneConfig: + KubeControllerManager: + allocateNodeCIDRs: true + attachDetachReconcileSyncPeriod: 1m0s + cloudProvider: external + clusterCIDR: 100.96.0.0/11 + clusterName: minimal-azure.example.com + configureCloudRoutes: false + image: registry.k8s.io/kube-controller-manager:v1.35.0 + leaderElection: + leaderElect: true + logLevel: 2 + useServiceAccountCredentials: true + KubeScheduler: + image: registry.k8s.io/kube-scheduler:v1.35.0 + leaderElection: + leaderElect: true + logLevel: 2 +EtcdClusterNames: +- main +- events +FileAssets: +- content: | + apiVersion: kubescheduler.config.k8s.io/v1 + clientConnection: + kubeconfig: /var/lib/kube-scheduler/kubeconfig + kind: KubeSchedulerConfiguration + path: /var/lib/kube-scheduler/config.yaml +Hooks: +- null +- null +InstallCNIAssets: true +KeypairIDs: + apiserver-aggregator-ca: "6980187172486667078076483355" + etcd-clients-ca: "6979622252718071085282986282" + etcd-manager-ca-events: "6982279354000777253151890266" + etcd-manager-ca-main: "6982279354000936168671127624" + etcd-peers-ca-events: "6982279353999767935825892873" + etcd-peers-ca-main: "6982279353998887468930183660" + kubernetes-ca: "6982820025135291416230495506" + service-account: "2" +KubeProxy: + clusterCIDR: 100.96.0.0/11 + cpuRequest: 100m + image: registry.k8s.io/kube-proxy:v1.35.0 + logLevel: 2 +KubeletConfig: + anonymousAuth: false + cgroupDriver: systemd + cgroupRoot: / + cloudProvider: external + clusterDNS: 100.64.0.10 + clusterDomain: cluster.local + enableDebuggingHandlers: true + evictionHard: memory.available<100Mi,nodefs.available<10%,nodefs.inodesFree<5%,imagefs.available<10%,imagefs.inodesFree<5% + featureGates: + ImageVolume: "true" + kubeconfigPath: /var/lib/kubelet/kubeconfig + logLevel: 2 + nodeLabels: + kops.k8s.io/kops-controller-pki: "" + node-role.kubernetes.io/control-plane: "" + node.kubernetes.io/exclude-from-external-load-balancers: "" + podManifestPath: /etc/kubernetes/manifests + protectKernelDefaults: true + shutdownGracePeriod: 30s + shutdownGracePeriodCriticalPods: 10s + taints: + - node-role.kubernetes.io/control-plane=:NoSchedule +KubernetesVersion: 1.35.0 +Networking: + nonMasqueradeCIDR: 100.64.0.0/10 + serviceClusterIPRange: 100.64.0.0/13 +UpdatePolicy: automatic +channels: +- memfs://tests/minimal-azure.example.com/addons/bootstrap-channel.yaml +configStore: + keypairs: memfs://tests/minimal-azure.example.com/pki + secrets: memfs://tests/minimal-azure.example.com/secrets +containerdConfig: + logLevel: info + runc: + version: 1.3.4 + sandboxImage: registry.k8s.io/pause:3.10.1 + version: 2.1.6 +etcdManifests: +- memfs://tests/minimal-azure.example.com/manifests/etcd/main-control-plane-eastus-1.yaml +- memfs://tests/minimal-azure.example.com/manifests/etcd/events-control-plane-eastus-1.yaml +staticManifests: +- key: kube-apiserver-healthcheck + path: manifests/static/kube-apiserver-healthcheck.yaml +usesLegacyGossip: false +usesNoneDNS: true diff --git a/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_nodeupconfig-nodes_source b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_nodeupconfig-nodes_source new file mode 100644 index 0000000000000..554fed9e31d5a --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/data/azurerm_storage_blob_nodeupconfig-nodes_source @@ -0,0 +1,64 @@ +Assets: + amd64: + - 2f4ed7778681649b81244426c29c5d98df60ccabf83d561d69e61c1cbb943ddf@https://dl.k8s.io/release/v1.35.0/bin/linux/amd64/kubelet + - a2e984a18a0c063279d692533031c1eff93a262afcc0afdc517375432d060989@https://dl.k8s.io/release/v1.35.0/bin/linux/amd64/kubectl + - b8e811578fb66023f90d2e238d80cec3bdfca4b44049af74c374d4fae0f9c090@https://github.com/containernetworking/plugins/releases/download/v1.6.2/cni-plugins-linux-amd64-v1.6.2.tgz + - 4793dc5c1f34ebf8402990d0050f3c294aa3c794cd5a4baa403c1cf10602326d@https://github.com/containerd/containerd/releases/download/v2.1.6/containerd-2.1.6-linux-amd64.tar.gz + - 5966ca40b6187b30e33bfc299c5f1fe72e8c1aa01cf3fefdadf391668f47f103@https://github.com/opencontainers/runc/releases/download/v1.3.4/runc.amd64 + arm64: + - aa658d077348b43d238f50966a583f4244b2a7d45590c77b3b165b7d44983ab8@https://dl.k8s.io/release/v1.35.0/bin/linux/arm64/kubelet + - 58f82f9fe796c375c5c4b8439850b0f3f4d401a52434052f2df46035a8789e25@https://dl.k8s.io/release/v1.35.0/bin/linux/arm64/kubectl + - 01e0e22acc7f7004e4588c1fe1871cc86d7ab562cd858e1761c4641d89ebfaa4@https://github.com/containernetworking/plugins/releases/download/v1.6.2/cni-plugins-linux-arm64-v1.6.2.tgz + - 88d6e32348c36628c8500a630c6dd4b3cb8c680b1d18dc8d1d19041f67757c6e@https://github.com/containerd/containerd/releases/download/v2.1.6/containerd-2.1.6-linux-arm64.tar.gz + - d6dcab36d1b6af1b72c7f0662e5fcf446a291271ba6006532b95c4144e19d428@https://github.com/opencontainers/runc/releases/download/v1.3.4/runc.arm64 +AzureAdminUser: admin-user +AzureLocation: eastus +AzureResourceGroup: minimal-azure.example.com +AzureRouteTableName: minimal-azure.example.com +AzureSecurityGroupName: minimal-azure.example.com +AzureSubnetName: eastus +AzureSubscriptionID: sub-123 +AzureTenantID: tenant-123 +CAs: {} +ClusterName: minimal-azure.example.com +Hooks: +- null +- null +InstallCNIAssets: true +KeypairIDs: + kubernetes-ca: "6982820025135291416230495506" +KubeProxy: + clusterCIDR: 100.96.0.0/11 + cpuRequest: 100m + image: registry.k8s.io/kube-proxy:v1.35.0 + logLevel: 2 +KubeletConfig: + anonymousAuth: false + cgroupDriver: systemd + cgroupRoot: / + cloudProvider: external + clusterDNS: 100.64.0.10 + clusterDomain: cluster.local + enableDebuggingHandlers: true + evictionHard: memory.available<100Mi,nodefs.available<10%,nodefs.inodesFree<5%,imagefs.available<10%,imagefs.inodesFree<5% + kubeconfigPath: /var/lib/kubelet/kubeconfig + logLevel: 2 + nodeLabels: + node-role.kubernetes.io/node: "" + podManifestPath: /etc/kubernetes/manifests + protectKernelDefaults: true + shutdownGracePeriod: 30s + shutdownGracePeriodCriticalPods: 10s +KubernetesVersion: 1.35.0 +Networking: + nonMasqueradeCIDR: 100.64.0.0/10 + serviceClusterIPRange: 100.64.0.0/13 +UpdatePolicy: automatic +containerdConfig: + logLevel: info + runc: + version: 1.3.4 + sandboxImage: registry.k8s.io/pause:3.10.1 + version: 2.1.6 +usesLegacyGossip: false +usesNoneDNS: true diff --git a/tests/integration/update_cluster/minimal_azure_wi/id_rsa.pub b/tests/integration/update_cluster/minimal_azure_wi/id_rsa.pub new file mode 100644 index 0000000000000..607adf95499bd --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDbY5rS+D/y2tqFq1aaugISaPcuMUIDiS76GjAI0dz08d7D5iy+LgWjhSqiZPGzObLdO6BNlKSHxYlZQBu7bxsyojQtk5F8Tjkdpgfpi9E+GD4Hl8UU8aQc1t0vK2KP/J2u2ZVc+AKISSdGQoQ95m2CYcQ6iLShzI+H25PJEEaIRAacWhmiNoo4Dku1wnlPS34Etudtro07uvLWHcD5rihlQ48VUWJr4in3ejeGzHTS5iOa1PQgddzd4uT2kd6m4rYa2NI0Ar7CJH772lYG5SF7cZgaV3bHkRNiRgMaj52bhyd0lfhioaJIaehk+IdcCaIO0EVJxr+Dnwt97oEKNOqT chacman@M1Book.local diff --git a/tests/integration/update_cluster/minimal_azure_wi/in-v1alpha3.yaml b/tests/integration/update_cluster/minimal_azure_wi/in-v1alpha3.yaml new file mode 100644 index 0000000000000..d79680e88de16 --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/in-v1alpha3.yaml @@ -0,0 +1,104 @@ +apiVersion: kops.k8s.io/v1alpha3 +kind: Cluster +metadata: + creationTimestamp: "2017-01-01T00:00:00Z" + name: minimal-azure.example.com +spec: + api: + access: + - 0.0.0.0/0 + - ::/0 + dns: {} + loadBalancer: + type: Public + publicName: api.minimal-azure.example.com + authorization: + rbac: {} + channel: stable + cloudProvider: + azure: + adminUser: admin-user + storageAccountID: /subscriptions/sub-321/resourceGroups/resource-group-name/providers/Microsoft.Storage/storageAccounts/teststorage + subscriptionID: sub-123 + tenantID: tenant-123 + configStore: + base: memfs://tests/minimal-azure.example.com + etcdClusters: + - cpuRequest: 200m + etcdMembers: + - encryptedVolume: true + instanceGroup: control-plane-eastus-1 + name: a + memoryRequest: 100Mi + name: main + - cpuRequest: 100m + etcdMembers: + - encryptedVolume: true + instanceGroup: control-plane-eastus-1 + name: a + memoryRequest: 100Mi + name: events + iam: + legacy: false + useServiceAccountExternalPermissions: true + kubelet: + anonymousAuth: false + kubernetesVersion: v1.35.0 + networking: + cni: {} + networkCIDR: 10.0.0.0/16 + nonMasqueradeCIDR: 100.64.0.0/10 + subnets: + - cidr: 10.0.0.0/24 + name: eastus + region: eastus + type: Public + topology: + dns: None + serviceAccountIssuerDiscovery: + discoveryStore: memfs://discovery.example.com/minimal-azure.example.com + sshAccess: + - 0.0.0.0/0 + - ::/0 + +--- + +apiVersion: kops.k8s.io/v1alpha3 +kind: InstanceGroup +metadata: + creationTimestamp: "2017-01-01T00:00:00Z" + labels: + kops.k8s.io/cluster: minimal-azure.example.com + name: control-plane-eastus-1 +spec: + associatePublicIP: true + image: Canonical:UbuntuServer:18.04-LTS:latest + machineType: Standard_B2s + maxSize: 1 + minSize: 1 + role: ControlPlane + subnets: + - eastus + zones: + - eastus-1 + +--- + +apiVersion: kops.k8s.io/v1alpha3 +kind: InstanceGroup +metadata: + creationTimestamp: "2017-01-01T00:00:00Z" + labels: + kops.k8s.io/cluster: minimal-azure.example.com + name: nodes +spec: + associatePublicIP: true + image: Canonical:UbuntuServer:18.04-LTS:latest + machineType: Standard_B2s + maxSize: 1 + minSize: 1 + role: Node + subnets: + - eastus + zones: + - eastus-1 diff --git a/tests/integration/update_cluster/minimal_azure_wi/kubernetes.tf b/tests/integration/update_cluster/minimal_azure_wi/kubernetes.tf new file mode 100644 index 0000000000000..7ba413c0fa78b --- /dev/null +++ b/tests/integration/update_cluster/minimal_azure_wi/kubernetes.tf @@ -0,0 +1,738 @@ +locals { + cluster_name = "minimal-azure.example.com" + region = "eastus" +} + +output "cluster_name" { + value = "minimal-azure.example.com" +} + +output "region" { + value = "eastus" +} + +provider "azurerm" { + features { + } + subscription_id = "sub-123" +} + +provider "azurerm" { + alias = "files" + features { + } + subscription_id = "sub-321" +} + +resource "azurerm_application_security_group" "control-plane-minimal-azure-example-com" { + location = "eastus" + name = "control-plane.minimal-azure.example.com" + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + tags = { + "KubernetesCluster" = "test-cluster.k8s" + } +} + +resource "azurerm_application_security_group" "nodes-minimal-azure-example-com" { + location = "eastus" + name = "nodes.minimal-azure.example.com" + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + tags = { + "KubernetesCluster" = "test-cluster.k8s" + } +} + +resource "azurerm_federated_identity_credential" "fic-ccm" { + audience = ["api://AzureADTokenExchange"] + issuer = "https://discovery.example.com/minimal-azure.example.com" + name = "fic-ccm" + parent_id = azurerm_user_assigned_identity.wi-minimal-azure-example-com.id + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + subject = "system:serviceaccount:kube-system:cloud-controller-manager" +} + +resource "azurerm_federated_identity_credential" "fic-csi-azuredisk" { + audience = ["api://AzureADTokenExchange"] + issuer = "https://discovery.example.com/minimal-azure.example.com" + name = "fic-csi-azuredisk" + parent_id = azurerm_user_assigned_identity.wi-minimal-azure-example-com.id + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + subject = "system:serviceaccount:kube-system:csi-azuredisk-controller-sa" +} + +resource "azurerm_lb" "api-minimal-azure-example-com" { + frontend_ip_configuration { + name = "LoadBalancerFrontEnd" + public_ip_address_id = azurerm_public_ip.api-minimal-azure-example-com.id + } + location = "eastus" + name = "api-minimal-azure.example.com" + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + sku = "Standard" + tags = { + "KubernetesCluster" = "test-cluster.k8s" + } +} + +resource "azurerm_lb_backend_address_pool" "api-minimal-azure-example-com-backend-pool" { + loadbalancer_id = azurerm_lb.api-minimal-azure-example-com.id + name = "LoadBalancerBackEnd" +} + +resource "azurerm_lb_probe" "api-minimal-azure-example-com-Health-HTTPS-3988" { + interval_in_seconds = 15 + loadbalancer_id = azurerm_lb.api-minimal-azure-example-com.id + name = "Health-HTTPS-3988" + number_of_probes = 4 + port = 3988 + protocol = "Https" + request_path = "/healthz" +} + +resource "azurerm_lb_probe" "api-minimal-azure-example-com-Health-TCP-443" { + interval_in_seconds = 15 + loadbalancer_id = azurerm_lb.api-minimal-azure-example-com.id + name = "Health-TCP-443" + number_of_probes = 4 + port = 443 + protocol = "Tcp" +} + +resource "azurerm_lb_rule" "api-minimal-azure-example-com-TCP-3988" { + backend_address_pool_ids = [azurerm_lb_backend_address_pool.api-minimal-azure-example-com-backend-pool.id] + backend_port = 3988 + floating_ip_enabled = false + frontend_ip_configuration_name = "LoadBalancerFrontEnd" + frontend_port = 3988 + idle_timeout_in_minutes = 4 + load_distribution = "Default" + loadbalancer_id = azurerm_lb.api-minimal-azure-example-com.id + name = "TCP-3988" + probe_id = azurerm_lb_probe.api-minimal-azure-example-com-Health-HTTPS-3988.id + protocol = "Tcp" +} + +resource "azurerm_lb_rule" "api-minimal-azure-example-com-TCP-443" { + backend_address_pool_ids = [azurerm_lb_backend_address_pool.api-minimal-azure-example-com-backend-pool.id] + backend_port = 443 + floating_ip_enabled = false + frontend_ip_configuration_name = "LoadBalancerFrontEnd" + frontend_port = 443 + idle_timeout_in_minutes = 4 + load_distribution = "Default" + loadbalancer_id = azurerm_lb.api-minimal-azure-example-com.id + name = "TCP-443" + probe_id = azurerm_lb_probe.api-minimal-azure-example-com-Health-TCP-443.id + protocol = "Tcp" +} + +resource "azurerm_linux_virtual_machine_scale_set" "control-plane-eastus-1-masters-minimal-azure-example-com" { + admin_ssh_key { + public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDbY5rS+D/y2tqFq1aaugISaPcuMUIDiS76GjAI0dz08d7D5iy+LgWjhSqiZPGzObLdO6BNlKSHxYlZQBu7bxsyojQtk5F8Tjkdpgfpi9E+GD4Hl8UU8aQc1t0vK2KP/J2u2ZVc+AKISSdGQoQ95m2CYcQ6iLShzI+H25PJEEaIRAacWhmiNoo4Dku1wnlPS34Etudtro07uvLWHcD5rihlQ48VUWJr4in3ejeGzHTS5iOa1PQgddzd4uT2kd6m4rYa2NI0Ar7CJH772lYG5SF7cZgaV3bHkRNiRgMaj52bhyd0lfhioaJIaehk+IdcCaIO0EVJxr+Dnwt97oEKNOqT chacman@M1Book.local" + username = "admin-user" + } + admin_username = "admin-user" + computer_name_prefix = "control-plane-eastus-1" + disable_password_authentication = true + identity { + type = "SystemAssigned" + } + instances = 1 + location = "eastus" + name = "control-plane-eastus-1.masters.minimal-azure.example.com" + network_interface { + enable_ip_forwarding = true + ip_configuration { + application_security_group_ids = [azurerm_application_security_group.control-plane-minimal-azure-example-com.id] + load_balancer_backend_address_pool_ids = [azurerm_lb_backend_address_pool.api-minimal-azure-example-com-backend-pool.id] + name = "control-plane-eastus-1.masters.minimal-azure.example.com" + primary = true + public_ip_address { + name = "control-plane-eastus-1.masters.minimal-azure.example.com" + version = "IPv4" + } + subnet_id = azurerm_subnet.eastus.id + } + name = "control-plane-eastus-1.masters.minimal-azure.example.com" + primary = true + } + os_disk { + caching = "ReadWrite" + disk_size_gb = 64 + storage_account_type = "StandardSSD_LRS" + } + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + sku = "Standard_B2s" + source_image_reference { + offer = "UbuntuServer" + publisher = "Canonical" + sku = "18.04-LTS" + version = "latest" + } + tags = { + "KubernetesCluster" = "test-cluster.k8s" + "k8s.io_role_control-plane" = "1" + "k8s.io_role_master" = "1" + "kops.k8s.io_instancegroup" = "control-plane-eastus-1" + } + upgrade_mode = "Manual" + user_data = filebase64("${path.module}/data/azurerm_linux_virtual_machine_scale_set_control-plane-eastus-1.masters.minimal-azure.example.com_user_data") + zones = ["1"] +} + +resource "azurerm_linux_virtual_machine_scale_set" "nodes-minimal-azure-example-com" { + admin_ssh_key { + public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDbY5rS+D/y2tqFq1aaugISaPcuMUIDiS76GjAI0dz08d7D5iy+LgWjhSqiZPGzObLdO6BNlKSHxYlZQBu7bxsyojQtk5F8Tjkdpgfpi9E+GD4Hl8UU8aQc1t0vK2KP/J2u2ZVc+AKISSdGQoQ95m2CYcQ6iLShzI+H25PJEEaIRAacWhmiNoo4Dku1wnlPS34Etudtro07uvLWHcD5rihlQ48VUWJr4in3ejeGzHTS5iOa1PQgddzd4uT2kd6m4rYa2NI0Ar7CJH772lYG5SF7cZgaV3bHkRNiRgMaj52bhyd0lfhioaJIaehk+IdcCaIO0EVJxr+Dnwt97oEKNOqT chacman@M1Book.local" + username = "admin-user" + } + admin_username = "admin-user" + computer_name_prefix = "nodes" + disable_password_authentication = true + identity { + type = "SystemAssigned" + } + instances = 1 + location = "eastus" + name = "nodes.minimal-azure.example.com" + network_interface { + enable_ip_forwarding = true + ip_configuration { + application_security_group_ids = [azurerm_application_security_group.nodes-minimal-azure-example-com.id] + name = "nodes.minimal-azure.example.com" + primary = true + public_ip_address { + name = "nodes.minimal-azure.example.com" + version = "IPv4" + } + subnet_id = azurerm_subnet.eastus.id + } + name = "nodes.minimal-azure.example.com" + primary = true + } + os_disk { + caching = "ReadWrite" + disk_size_gb = 128 + storage_account_type = "StandardSSD_LRS" + } + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + sku = "Standard_B2s" + source_image_reference { + offer = "UbuntuServer" + publisher = "Canonical" + sku = "18.04-LTS" + version = "latest" + } + tags = { + "KubernetesCluster" = "test-cluster.k8s" + "k8s.io_role_node" = "1" + "kops.k8s.io_instancegroup" = "nodes" + } + upgrade_mode = "Manual" + user_data = filebase64("${path.module}/data/azurerm_linux_virtual_machine_scale_set_nodes.minimal-azure.example.com_user_data") + zones = ["1"] +} + +resource "azurerm_managed_disk" "a-etcd-events-minimal-azure-example-com" { + create_option = "Empty" + disk_size_gb = 20 + location = "eastus" + name = "a.etcd-events.minimal-azure.example.com" + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + storage_account_type = "StandardSSD_LRS" + tags = { + "KubernetesCluster" = "test-cluster.k8s" + "k8s.io_etcd_events" = "a/a" + "k8s.io_role_control_plane" = "1" + "k8s.io_role_master" = "1" + "kubernetes.io_cluster_minimal-azure.example.com" = "owned" + } + zone = "1" +} + +resource "azurerm_managed_disk" "a-etcd-main-minimal-azure-example-com" { + create_option = "Empty" + disk_size_gb = 20 + location = "eastus" + name = "a.etcd-main.minimal-azure.example.com" + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + storage_account_type = "StandardSSD_LRS" + tags = { + "KubernetesCluster" = "test-cluster.k8s" + "k8s.io_etcd_main" = "a/a" + "k8s.io_role_control_plane" = "1" + "k8s.io_role_master" = "1" + "kubernetes.io_cluster_minimal-azure.example.com" = "owned" + } + zone = "1" +} + +resource "azurerm_nat_gateway" "minimal-azure-example-com" { + location = "eastus" + name = "minimal-azure.example.com" + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + sku_name = "Standard" + tags = { + "KubernetesCluster" = "test-cluster.k8s" + } +} + +resource "azurerm_nat_gateway_public_ip_association" "minimal-azure-example-com-minimal-azure-example-com" { + nat_gateway_id = azurerm_nat_gateway.minimal-azure-example-com.id + public_ip_address_id = azurerm_public_ip.minimal-azure-example-com.id +} + +resource "azurerm_network_security_group" "minimal-azure-example-com" { + location = "eastus" + name = "minimal-azure.example.com" + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + security_rule { + access = "Allow" + destination_application_security_group_ids = [azurerm_application_security_group.control-plane-minimal-azure-example-com.id, azurerm_application_security_group.nodes-minimal-azure-example-com.id] + destination_port_range = "22" + direction = "Inbound" + name = "AllowSSH" + priority = 100 + protocol = "Tcp" + source_address_prefixes = ["0.0.0.0/0"] + source_port_range = "*" + } + security_rule { + access = "Allow" + destination_application_security_group_ids = [azurerm_application_security_group.control-plane-minimal-azure-example-com.id, azurerm_application_security_group.nodes-minimal-azure-example-com.id] + destination_port_range = "22" + direction = "Inbound" + name = "AllowSSH_v6" + priority = 101 + protocol = "Tcp" + source_address_prefixes = ["::/0"] + source_port_range = "*" + } + security_rule { + access = "Allow" + destination_application_security_group_ids = [azurerm_application_security_group.control-plane-minimal-azure-example-com.id] + destination_port_range = "443" + direction = "Inbound" + name = "AllowKubernetesAPI" + priority = 200 + protocol = "Tcp" + source_address_prefixes = ["0.0.0.0/0"] + source_port_range = "*" + } + security_rule { + access = "Allow" + destination_application_security_group_ids = [azurerm_application_security_group.control-plane-minimal-azure-example-com.id] + destination_port_range = "443" + direction = "Inbound" + name = "AllowKubernetesAPI_v6" + priority = 201 + protocol = "Tcp" + source_address_prefixes = ["::/0"] + source_port_range = "*" + } + security_rule { + access = "Allow" + destination_application_security_group_ids = [azurerm_application_security_group.control-plane-minimal-azure-example-com.id] + destination_port_range = "*" + direction = "Inbound" + name = "AllowControlPlaneToControlPlane" + priority = 1000 + protocol = "*" + source_application_security_group_ids = [azurerm_application_security_group.control-plane-minimal-azure-example-com.id] + source_port_range = "*" + } + security_rule { + access = "Allow" + destination_application_security_group_ids = [azurerm_application_security_group.nodes-minimal-azure-example-com.id] + destination_port_range = "*" + direction = "Inbound" + name = "AllowControlPlaneToNodes" + priority = 1001 + protocol = "*" + source_application_security_group_ids = [azurerm_application_security_group.control-plane-minimal-azure-example-com.id] + source_port_range = "*" + } + security_rule { + access = "Allow" + destination_application_security_group_ids = [azurerm_application_security_group.nodes-minimal-azure-example-com.id] + destination_port_range = "*" + direction = "Inbound" + name = "AllowNodesToNodes" + priority = 1002 + protocol = "*" + source_application_security_group_ids = [azurerm_application_security_group.nodes-minimal-azure-example-com.id] + source_port_range = "*" + } + security_rule { + access = "Deny" + destination_application_security_group_ids = [azurerm_application_security_group.control-plane-minimal-azure-example-com.id] + destination_port_range = "2380-2381" + direction = "Inbound" + name = "DenyNodesToEtcdManager" + priority = 1003 + protocol = "Tcp" + source_application_security_group_ids = [azurerm_application_security_group.nodes-minimal-azure-example-com.id] + source_port_range = "*" + } + security_rule { + access = "Deny" + destination_application_security_group_ids = [azurerm_application_security_group.control-plane-minimal-azure-example-com.id] + destination_port_range = "4000-4001" + direction = "Inbound" + name = "DenyNodesToEtcd" + priority = 1004 + protocol = "Tcp" + source_application_security_group_ids = [azurerm_application_security_group.nodes-minimal-azure-example-com.id] + source_port_range = "*" + } + security_rule { + access = "Allow" + destination_application_security_group_ids = [azurerm_application_security_group.control-plane-minimal-azure-example-com.id] + destination_port_range = "*" + direction = "Inbound" + name = "AllowNodesToControlPlane" + priority = 1005 + protocol = "*" + source_application_security_group_ids = [azurerm_application_security_group.nodes-minimal-azure-example-com.id] + source_port_range = "*" + } + security_rule { + access = "Allow" + destination_application_security_group_ids = [azurerm_application_security_group.control-plane-minimal-azure-example-com.id] + destination_port_range = "443" + direction = "Inbound" + name = "AllowNodesToKubernetesAPI" + priority = 2000 + protocol = "Tcp" + source_address_prefix = "*" + source_port_range = "*" + } + security_rule { + access = "Allow" + destination_application_security_group_ids = [azurerm_application_security_group.control-plane-minimal-azure-example-com.id] + destination_port_range = "3988" + direction = "Inbound" + name = "AllowNodesToKopsController" + priority = 2001 + protocol = "Tcp" + source_address_prefix = "*" + source_port_range = "*" + } + security_rule { + access = "Allow" + destination_address_prefix = "VirtualNetwork" + destination_port_range = "*" + direction = "Inbound" + name = "AllowAzureLoadBalancer" + priority = 4000 + protocol = "*" + source_address_prefix = "AzureLoadBalancer" + source_port_range = "*" + } + security_rule { + access = "Deny" + destination_application_security_group_ids = [azurerm_application_security_group.control-plane-minimal-azure-example-com.id] + destination_port_range = "*" + direction = "Inbound" + name = "DenyAllToControlPlane" + priority = 4001 + protocol = "*" + source_address_prefix = "*" + source_port_range = "*" + } + security_rule { + access = "Deny" + destination_application_security_group_ids = [azurerm_application_security_group.nodes-minimal-azure-example-com.id] + destination_port_range = "*" + direction = "Inbound" + name = "DenyAllToNodes" + priority = 4002 + protocol = "*" + source_address_prefix = "*" + source_port_range = "*" + } + tags = { + "KubernetesCluster" = "test-cluster.k8s" + } +} + +resource "azurerm_public_ip" "api-minimal-azure-example-com" { + allocation_method = "Static" + ip_version = "IPv4" + location = "eastus" + name = "api-minimal-azure.example.com" + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + sku = "Standard" + tags = { + "KubernetesCluster" = "test-cluster.k8s" + } +} + +resource "azurerm_public_ip" "minimal-azure-example-com" { + allocation_method = "Static" + ip_version = "IPv4" + location = "eastus" + name = "minimal-azure.example.com" + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + sku = "Standard" + tags = { + "KubernetesCluster" = "test-cluster.k8s" + } +} + +resource "azurerm_resource_group" "minimal-azure-example-com" { + location = "eastus" + name = "minimal-azure.example.com" + tags = { + "KubernetesCluster" = "test-cluster.k8s" + } +} + +resource "azurerm_role_assignment" "control-plane-eastus-1-masters-minimal-azure-example-com-blob" { + principal_id = azurerm_linux_virtual_machine_scale_set.control-plane-eastus-1-masters-minimal-azure-example-com.identity[0].principal_id + role_definition_id = "/subscriptions/sub-321/resourceGroups/resource-group-name/providers/Microsoft.Storage/storageAccounts/teststorage/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe" + scope = "/subscriptions/sub-321/resourceGroups/resource-group-name/providers/Microsoft.Storage/storageAccounts/teststorage" + skip_service_principal_aad_check = true +} + +resource "azurerm_role_assignment" "control-plane-eastus-1-masters-minimal-azure-example-com-owner" { + principal_id = azurerm_linux_virtual_machine_scale_set.control-plane-eastus-1-masters-minimal-azure-example-com.identity[0].principal_id + role_definition_id = "/subscriptions/sub-123/resourceGroups/minimal-azure.example.com/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635" + scope = "/subscriptions/sub-123/resourceGroups/minimal-azure.example.com" + skip_service_principal_aad_check = true +} + +resource "azurerm_role_assignment" "wi-uami-contributor" { + principal_id = azurerm_user_assigned_identity.wi-minimal-azure-example-com.principal_id + role_definition_id = "/subscriptions/sub-123/resourceGroups/minimal-azure.example.com/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c" + scope = "/subscriptions/sub-123/resourceGroups/minimal-azure.example.com" + skip_service_principal_aad_check = true +} + +resource "azurerm_route_table" "minimal-azure-example-com" { + location = "eastus" + name = "minimal-azure.example.com" + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + tags = { + "KubernetesCluster" = "test-cluster.k8s" + } +} + +resource "azurerm_storage_blob" "cluster-completed-spec" { + name = "tests/minimal-azure.example.com/cluster-completed.spec" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_cluster-completed.spec_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "discovery-json" { + name = "discovery.example.com/minimal-azure.example.com/.well-known/openid-configuration" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_discovery.json_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "etcd-cluster-spec-events" { + name = "tests/minimal-azure.example.com/backups/etcd/events/control/etcd-cluster-spec" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_etcd-cluster-spec-events_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "etcd-cluster-spec-main" { + name = "tests/minimal-azure.example.com/backups/etcd/main/control/etcd-cluster-spec" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_etcd-cluster-spec-main_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "keys-json" { + name = "discovery.example.com/minimal-azure.example.com/openid/v1/jwks" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_keys.json_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "kops-version-txt" { + name = "tests/minimal-azure.example.com/kops-version.txt" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_kops-version.txt_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "manifests-etcdmanager-events-control-plane-eastus-1" { + name = "tests/minimal-azure.example.com/manifests/etcd/events-control-plane-eastus-1.yaml" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_manifests-etcdmanager-events-control-plane-eastus-1_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "manifests-etcdmanager-main-control-plane-eastus-1" { + name = "tests/minimal-azure.example.com/manifests/etcd/main-control-plane-eastus-1.yaml" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_manifests-etcdmanager-main-control-plane-eastus-1_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "manifests-static-kube-apiserver-healthcheck" { + name = "tests/minimal-azure.example.com/manifests/static/kube-apiserver-healthcheck.yaml" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_manifests-static-kube-apiserver-healthcheck_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "minimal-azure-example-com-addons-azure-cloud-controller-addons-k8s-io-k8s-1-31" { + name = "tests/minimal-azure.example.com/addons/azure-cloud-controller.addons.k8s.io/k8s-1.31.yaml" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_minimal-azure.example.com-addons-azure-cloud-controller.addons.k8s.io-k8s-1.31_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "minimal-azure-example-com-addons-azuredisk-csi-driver-addons-k8s-io-k8s-1-31" { + name = "tests/minimal-azure.example.com/addons/azuredisk-csi-driver.addons.k8s.io/k8s-1.31.yaml" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_minimal-azure.example.com-addons-azuredisk-csi-driver.addons.k8s.io-k8s-1.31_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "minimal-azure-example-com-addons-bootstrap" { + name = "tests/minimal-azure.example.com/addons/bootstrap-channel.yaml" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_minimal-azure.example.com-addons-bootstrap_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "minimal-azure-example-com-addons-coredns-addons-k8s-io-k8s-1-12" { + name = "tests/minimal-azure.example.com/addons/coredns.addons.k8s.io/k8s-1.12.yaml" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_minimal-azure.example.com-addons-coredns.addons.k8s.io-k8s-1.12_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "minimal-azure-example-com-addons-kops-controller-addons-k8s-io-k8s-1-16" { + name = "tests/minimal-azure.example.com/addons/kops-controller.addons.k8s.io/k8s-1.16.yaml" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_minimal-azure.example.com-addons-kops-controller.addons.k8s.io-k8s-1.16_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "minimal-azure-example-com-addons-kubelet-api-rbac-addons-k8s-io-k8s-1-9" { + name = "tests/minimal-azure.example.com/addons/kubelet-api.rbac.addons.k8s.io/k8s-1.9.yaml" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_minimal-azure.example.com-addons-kubelet-api.rbac.addons.k8s.io-k8s-1.9_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "minimal-azure-example-com-addons-limit-range-addons-k8s-io" { + name = "tests/minimal-azure.example.com/addons/limit-range.addons.k8s.io/v1.5.0.yaml" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_minimal-azure.example.com-addons-limit-range.addons.k8s.io_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "minimal-azure-example-com-addons-storage-azure-addons-k8s-io-k8s-1-31" { + name = "tests/minimal-azure.example.com/addons/storage-azure.addons.k8s.io/k8s-1.31.yaml" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_minimal-azure.example.com-addons-storage-azure.addons.k8s.io-k8s-1.31_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "nodeupconfig-control-plane-eastus-1" { + name = "tests/minimal-azure.example.com/igconfig/control-plane/control-plane-eastus-1/nodeupconfig.yaml" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_nodeupconfig-control-plane-eastus-1_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_storage_blob" "nodeupconfig-nodes" { + name = "tests/minimal-azure.example.com/igconfig/node/nodes/nodeupconfig.yaml" + provider = azurerm.files + source = "${path.module}/data/azurerm_storage_blob_nodeupconfig-nodes_source" + storage_account_name = "teststorage" + storage_container_name = "testcontainer" + type = "Block" +} + +resource "azurerm_subnet" "eastus" { + address_prefixes = ["10.0.0.0/24"] + name = "eastus" + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + virtual_network_name = azurerm_virtual_network.minimal-azure-example-com.name +} + +resource "azurerm_subnet_nat_gateway_association" "minimal-azure-example-com-eastus-natgw" { + nat_gateway_id = azurerm_nat_gateway.minimal-azure-example-com.id + subnet_id = azurerm_subnet.eastus.id +} + +resource "azurerm_subnet_network_security_group_association" "minimal-azure-example-com-eastus-nsg" { + network_security_group_id = azurerm_network_security_group.minimal-azure-example-com.id + subnet_id = azurerm_subnet.eastus.id +} + +resource "azurerm_subnet_route_table_association" "minimal-azure-example-com-eastus-rt" { + route_table_id = azurerm_route_table.minimal-azure-example-com.id + subnet_id = azurerm_subnet.eastus.id +} + +resource "azurerm_user_assigned_identity" "wi-minimal-azure-example-com" { + location = "eastus" + name = "wi-minimal-azure-example-com" + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name +} + +resource "azurerm_virtual_network" "minimal-azure-example-com" { + address_space = ["10.0.0.0/16"] + location = "eastus" + name = "minimal-azure.example.com" + resource_group_name = azurerm_resource_group.minimal-azure-example-com.name + tags = { + "KubernetesCluster" = "test-cluster.k8s" + } +} + +terraform { + required_version = ">= 0.15.0" + required_providers { + azurerm = { + "configuration_aliases" = [azurerm.files] + "source" = "hashicorp/azurerm" + "version" = ">= 4.0.0" + } + } +} diff --git a/upup/models/cloudup/resources/addons/azure-cloud-controller.addons.k8s.io/k8s-1.31.yaml.template b/upup/models/cloudup/resources/addons/azure-cloud-controller.addons.k8s.io/k8s-1.31.yaml.template index fd747142a6cd9..44732e727e764 100644 --- a/upup/models/cloudup/resources/addons/azure-cloud-controller.addons.k8s.io/k8s-1.31.yaml.template +++ b/upup/models/cloudup/resources/addons/azure-cloud-controller.addons.k8s.io/k8s-1.31.yaml.template @@ -1,3 +1,28 @@ +{{ if UseServiceAccountExternalPermissions }} +apiVersion: v1 +kind: Secret +metadata: + name: azure-cloud-provider + namespace: kube-system +type: Opaque +stringData: + cloud-config: | + { + "tenantId": "{{ AzureTenantID }}", + "subscriptionId": "{{ AzureSubscriptionID }}", + "resourceGroup": "{{ AzureResourceGroupName }}", + "location": "{{ AzureLocation }}", + "vnetName": "{{ AzureVnetName }}", + "subnetName": "{{ AzureSubnetName }}", + "securityGroupName": "{{ AzureSecurityGroupName }}", + "useFederatedWorkloadIdentityExtension": true, + "aadFederatedTokenFile": "/var/run/secrets/azure/tokens/azure-identity-token", + "aadClientID": "{{ AzureWorkloadIdentityClientID }}", + "useInstanceMetadata": true, + "disableAvailabilitySetNodes": true + } +--- +{{ end }} apiVersion: v1 kind: ServiceAccount metadata: @@ -244,14 +269,22 @@ spec: cpu: '{{ or .ExternalCloudControllerManager.CPURequest "100m" }}' memory: 128Mi volumeMounts: +{{ if UseServiceAccountExternalPermissions }} + - mountPath: /etc/kubernetes + name: azure-cloud-config + readOnly: true +{{ else }} - mountPath: /etc/kubernetes name: etc-kubernetes +{{ end }} - mountPath: /etc/ssl name: ssl-mount readOnly: true +{{ if not (UseServiceAccountExternalPermissions) }} - mountPath: /var/lib/waagent/ManagedIdentity-Settings name: msi readOnly: true +{{ end }} - mountPath: /var/run/secrets/azure/tokens name: azure-identity-token readOnly: true @@ -276,15 +309,26 @@ spec: topologyKey: kubernetes.io/hostname whenUnsatisfiable: DoNotSchedule volumes: +{{ if UseServiceAccountExternalPermissions }} + - name: azure-cloud-config + secret: + secretName: azure-cloud-provider + items: + - key: cloud-config + path: azure.json +{{ else }} - hostPath: path: /etc/kubernetes name: etc-kubernetes +{{ end }} - hostPath: path: /etc/ssl name: ssl-mount +{{ if not (UseServiceAccountExternalPermissions) }} - hostPath: path: /var/lib/waagent/ManagedIdentity-Settings name: msi +{{ end }} - name: azure-identity-token projected: defaultMode: 420 diff --git a/upup/models/cloudup/resources/addons/azuredisk-csi-driver.addons.k8s.io/k8s-1.31.yaml.template b/upup/models/cloudup/resources/addons/azuredisk-csi-driver.addons.k8s.io/k8s-1.31.yaml.template index 8ae8595fb5722..3be81e165c62f 100644 --- a/upup/models/cloudup/resources/addons/azuredisk-csi-driver.addons.k8s.io/k8s-1.31.yaml.template +++ b/upup/models/cloudup/resources/addons/azuredisk-csi-driver.addons.k8s.io/k8s-1.31.yaml.template @@ -746,6 +746,11 @@ spec: name: socket-dir - mountPath: /etc/kubernetes/ name: azure-cred +{{ if UseServiceAccountExternalPermissions }} + - mountPath: /var/run/secrets/azure/tokens + name: azure-identity-token + readOnly: true +{{ end }} resources: limits: memory: 500Mi @@ -763,6 +768,16 @@ spec: hostPath: path: /etc/kubernetes/ type: DirectoryOrCreate +{{ if UseServiceAccountExternalPermissions }} + - name: azure-identity-token + projected: + defaultMode: 420 + sources: + - serviceAccountToken: + audience: api://AzureADTokenExchange + expirationSeconds: 3600 + path: azure-identity-token +{{ end }} --- # Source: azuredisk-csi-driver/templates/csi-azuredisk-driver.yaml apiVersion: storage.k8s.io/v1 diff --git a/upup/pkg/fi/cloudup/apply_cluster.go b/upup/pkg/fi/cloudup/apply_cluster.go index ea96879a3b0f9..92c2b59aeef6b 100644 --- a/upup/pkg/fi/cloudup/apply_cluster.go +++ b/upup/pkg/fi/cloudup/apply_cluster.go @@ -26,6 +26,7 @@ import ( "strings" "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + armmsilib "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi" "github.com/blang/semver/v4" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/klog/v2" @@ -467,6 +468,17 @@ func (c *ApplyClusterCmd) Run(ctx context.Context) (*ApplyResults, error) { if len(sshPublicKeys) != 1 { return nil, fmt.Errorf("exactly one 'admin' SSH public key can be specified when running with AzureCloud; please delete a key using `kops delete secret`") } + + // Pre-create the UAMI for workload identity so that the client ID + // is available to template functions during addon manifest generation. + if modelContext.UseServiceAccountExternalPermissions() { + azureCloud := cloud.(azure.AzureCloud) + clientID, err := ensureAzureWorkloadIdentity(ctx, azureCloud, cluster) + if err != nil { + return nil, fmt.Errorf("ensuring Azure workload identity: %w", err) + } + cluster.Spec.CloudProvider.Azure.WorkloadIdentityClientID = clientID + } } case kops.CloudProviderOpenstack: { @@ -681,8 +693,8 @@ func (c *ApplyClusterCmd) Run(ctx context.Context) (*ApplyResults, error) { &azuremodel.APILoadBalancerModelBuilder{AzureModelContext: azureModelContext, Lifecycle: clusterLifecycle}, &azuremodel.NetworkModelBuilder{AzureModelContext: azureModelContext, Lifecycle: clusterLifecycle}, &azuremodel.ResourceGroupModelBuilder{AzureModelContext: azureModelContext, Lifecycle: clusterLifecycle}, - &azuremodel.VMScaleSetModelBuilder{AzureModelContext: azureModelContext, BootstrapScriptBuilder: bootstrapScriptBuilder, Lifecycle: clusterLifecycle}, + &azuremodel.WorkloadIdentityModelBuilder{AzureModelContext: azureModelContext, Lifecycle: securityLifecycle}, ) case kops.CloudProviderOpenstack: openstackModelContext := &openstackmodel.OpenstackModelContext{ @@ -1085,3 +1097,57 @@ func ChannelForCluster(vfsContext *vfs.VFSContext, c *kops.Cluster) (*kops.Chann } return kops.LoadChannel(vfsContext, channelLocation) } + +// ensureAzureWorkloadIdentity tries to find or create a User-Assigned Managed Identity for workload identity +// and returns its client ID. If the resource group doesn't exist yet (first run), returns empty string — +// the task graph will create the RG and UAMI, and the client ID will be available on the next run. +func ensureAzureWorkloadIdentity(ctx context.Context, cloud azure.AzureCloud, cluster *kops.Cluster) (string, error) { + // If the client ID is already known (e.g. from a previous run), return it. + if cluster.Spec.CloudProvider.Azure.WorkloadIdentityClientID != "" { + return cluster.Spec.CloudProvider.Azure.WorkloadIdentityClientID, nil + } + + rgName := cluster.AzureResourceGroupName() + identityName := cluster.AzureWorkloadIdentityName() + + // Try to find an existing identity. + found, err := cloud.ManagedIdentity().Get(ctx, rgName, identityName) + if err == nil && found.Properties != nil && found.Properties.ClientID != nil { + klog.Infof("Found existing Azure workload identity %q with client ID %s", identityName, *found.Properties.ClientID) + return *found.Properties.ClientID, nil + } + + // Check if the resource group exists. If not, we can't create the identity yet. + rgs, err := cloud.ResourceGroup().List(ctx) + if err != nil { + return "", fmt.Errorf("listing resource groups: %w", err) + } + rgExists := false + for _, rg := range rgs { + if rg.Name != nil && *rg.Name == rgName { + rgExists = true + break + } + } + if !rgExists { + klog.Infof("Resource group %q does not exist yet; workload identity will be configured on next cluster update", rgName) + return "", nil + } + + klog.Infof("Creating Azure workload identity %q in resource group %q", identityName, rgName) + identity := armmsilib.Identity{ + Location: fi.PtrTo(cloud.Region()), + Tags: map[string]*string{}, + } + cloud.AddClusterTags(identity.Tags) + result, err := cloud.ManagedIdentity().CreateOrUpdate(ctx, rgName, identityName, identity) + if err != nil { + return "", fmt.Errorf("creating managed identity %q: %w", identityName, err) + } + if result.Properties == nil || result.Properties.ClientID == nil { + return "", fmt.Errorf("managed identity %q created but client ID not returned", identityName) + } + + klog.Infof("Created Azure workload identity %q with client ID %s", identityName, *result.Properties.ClientID) + return *result.Properties.ClientID, nil +} diff --git a/upup/pkg/fi/cloudup/azure/azure_cloud.go b/upup/pkg/fi/cloudup/azure/azure_cloud.go index 7c81a99e1c695..7fafb08f04928 100644 --- a/upup/pkg/fi/cloudup/azure/azure_cloud.go +++ b/upup/pkg/fi/cloudup/azure/azure_cloud.go @@ -63,6 +63,8 @@ type AzureCloud interface { LoadBalancer() LoadBalancersClient PublicIPAddress() PublicIPAddressesClient NatGateway() NatGatewaysClient + ManagedIdentity() ManagedIdentityClient + FederatedIdentityCredential() FederatedIdentityCredentialClient } type azureCloudImplementation struct { @@ -85,6 +87,9 @@ type azureCloudImplementation struct { publicIPAddressesClient PublicIPAddressesClient natGatewaysClient NatGatewaysClient storageAccountsClient StorageAccountsClient + // Azure Workload Identity + managedIdentityClient ManagedIdentityClient + federatedIdentityCredentialClient FederatedIdentityCredentialClient } var _ fi.Cloud = (*azureCloudImplementation)(nil) @@ -168,6 +173,12 @@ func newAzureCloud(subscriptionID, resourceGroupName, location string, tags map[ if azureCloudImpl.storageAccountsClient, err = newStorageAccountsClientImpl(subscriptionID, cred); err != nil { return nil, err } + if azureCloudImpl.managedIdentityClient, err = newManagedIdentityClientImpl(subscriptionID, cred); err != nil { + return nil, err + } + if azureCloudImpl.federatedIdentityCredentialClient, err = newFederatedIdentityCredentialClientImpl(subscriptionID, cred); err != nil { + return nil, err + } CacheAzureCloudInstance(subscriptionID, resourceGroupName, azureCloudImpl) @@ -421,3 +432,11 @@ func (c *azureCloudImplementation) PublicIPAddress() PublicIPAddressesClient { func (c *azureCloudImplementation) NatGateway() NatGatewaysClient { return c.natGatewaysClient } + +func (c *azureCloudImplementation) ManagedIdentity() ManagedIdentityClient { + return c.managedIdentityClient +} + +func (c *azureCloudImplementation) FederatedIdentityCredential() FederatedIdentityCredentialClient { + return c.federatedIdentityCredentialClient +} diff --git a/upup/pkg/fi/cloudup/azure/managedidentity.go b/upup/pkg/fi/cloudup/azure/managedidentity.go new file mode 100644 index 0000000000000..63b28a7781c6b --- /dev/null +++ b/upup/pkg/fi/cloudup/azure/managedidentity.go @@ -0,0 +1,129 @@ +/* +Copyright 2026 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package azure + +import ( + "context" + "fmt" + + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi" +) + +// ManagedIdentityClient is a client for managing user-assigned managed identities. +type ManagedIdentityClient interface { + Get(ctx context.Context, resourceGroupName, identityName string) (*armmsi.Identity, error) + CreateOrUpdate(ctx context.Context, resourceGroupName, identityName string, parameters armmsi.Identity) (*armmsi.Identity, error) + Delete(ctx context.Context, resourceGroupName, identityName string) error +} + +type managedIdentityClientImpl struct { + c *armmsi.UserAssignedIdentitiesClient +} + +var _ ManagedIdentityClient = (*managedIdentityClientImpl)(nil) + +func (c *managedIdentityClientImpl) Get(ctx context.Context, resourceGroupName, identityName string) (*armmsi.Identity, error) { + resp, err := c.c.Get(ctx, resourceGroupName, identityName, nil) + if err != nil { + return nil, err + } + return &resp.Identity, nil +} + +func (c *managedIdentityClientImpl) CreateOrUpdate(ctx context.Context, resourceGroupName, identityName string, parameters armmsi.Identity) (*armmsi.Identity, error) { + resp, err := c.c.CreateOrUpdate(ctx, resourceGroupName, identityName, parameters, nil) + if err != nil { + return nil, err + } + return &resp.Identity, nil +} + +func (c *managedIdentityClientImpl) Delete(ctx context.Context, resourceGroupName, identityName string) error { + _, err := c.c.Delete(ctx, resourceGroupName, identityName, nil) + if err != nil { + return fmt.Errorf("deleting managed identity: %w", err) + } + return nil +} + +func newManagedIdentityClientImpl(subscriptionID string, cred *azidentity.DefaultAzureCredential) (*managedIdentityClientImpl, error) { + c, err := armmsi.NewUserAssignedIdentitiesClient(subscriptionID, cred, nil) + if err != nil { + return nil, fmt.Errorf("creating managed identity client: %w", err) + } + return &managedIdentityClientImpl{c: c}, nil +} + +// FederatedIdentityCredentialClient is a client for managing federated identity credentials. +type FederatedIdentityCredentialClient interface { + Get(ctx context.Context, resourceGroupName, identityName, credentialName string) (*armmsi.FederatedIdentityCredential, error) + CreateOrUpdate(ctx context.Context, resourceGroupName, identityName, credentialName string, parameters armmsi.FederatedIdentityCredential) (*armmsi.FederatedIdentityCredential, error) + Delete(ctx context.Context, resourceGroupName, identityName, credentialName string) error + List(ctx context.Context, resourceGroupName, identityName string) ([]*armmsi.FederatedIdentityCredential, error) +} + +type federatedIdentityCredentialClientImpl struct { + c *armmsi.FederatedIdentityCredentialsClient +} + +var _ FederatedIdentityCredentialClient = (*federatedIdentityCredentialClientImpl)(nil) + +func (c *federatedIdentityCredentialClientImpl) Get(ctx context.Context, resourceGroupName, identityName, credentialName string) (*armmsi.FederatedIdentityCredential, error) { + resp, err := c.c.Get(ctx, resourceGroupName, identityName, credentialName, nil) + if err != nil { + return nil, err + } + return &resp.FederatedIdentityCredential, nil +} + +func (c *federatedIdentityCredentialClientImpl) CreateOrUpdate(ctx context.Context, resourceGroupName, identityName, credentialName string, parameters armmsi.FederatedIdentityCredential) (*armmsi.FederatedIdentityCredential, error) { + resp, err := c.c.CreateOrUpdate(ctx, resourceGroupName, identityName, credentialName, parameters, nil) + if err != nil { + return nil, err + } + return &resp.FederatedIdentityCredential, nil +} + +func (c *federatedIdentityCredentialClientImpl) Delete(ctx context.Context, resourceGroupName, identityName, credentialName string) error { + _, err := c.c.Delete(ctx, resourceGroupName, identityName, credentialName, nil) + if err != nil { + return fmt.Errorf("deleting federated identity credential: %w", err) + } + return nil +} + +func (c *federatedIdentityCredentialClientImpl) List(ctx context.Context, resourceGroupName, identityName string) ([]*armmsi.FederatedIdentityCredential, error) { + var l []*armmsi.FederatedIdentityCredential + pager := c.c.NewListPager(resourceGroupName, identityName, nil) + for pager.More() { + resp, err := pager.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("listing federated identity credentials: %w", err) + } + l = append(l, resp.Value...) + } + return l, nil +} + +func newFederatedIdentityCredentialClientImpl(subscriptionID string, cred *azidentity.DefaultAzureCredential) (*federatedIdentityCredentialClientImpl, error) { + c, err := armmsi.NewFederatedIdentityCredentialsClient(subscriptionID, cred, nil) + if err != nil { + return nil, fmt.Errorf("creating federated identity credential client: %w", err) + } + return &federatedIdentityCredentialClientImpl{c: c}, nil +} diff --git a/upup/pkg/fi/cloudup/azuretasks/federatedidentitycredential.go b/upup/pkg/fi/cloudup/azuretasks/federatedidentitycredential.go new file mode 100644 index 0000000000000..72eaca5a5fe34 --- /dev/null +++ b/upup/pkg/fi/cloudup/azuretasks/federatedidentitycredential.go @@ -0,0 +1,130 @@ +/* +Copyright 2026 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package azuretasks + +import ( + "context" + "errors" + "fmt" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi" + "k8s.io/klog/v2" + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/upup/pkg/fi/cloudup/azure" +) + +// FederatedIdentityCredential links a Kubernetes service account to an Azure User-Assigned Managed Identity. +// +kops:fitask +type FederatedIdentityCredential struct { + Name *string + Lifecycle fi.Lifecycle + + ManagedIdentity *ManagedIdentity + ResourceGroup *ResourceGroup + Issuer *string + Subject *string + Audiences []*string +} + +var ( + _ fi.CloudupTask = &FederatedIdentityCredential{} + _ fi.CompareWithID = &FederatedIdentityCredential{} +) + +// CompareWithID returns the Name. +func (f *FederatedIdentityCredential) CompareWithID() *string { + return f.Name +} + +// Find discovers the FederatedIdentityCredential in the cloud provider. +func (f *FederatedIdentityCredential) Find(c *fi.CloudupContext) (*FederatedIdentityCredential, error) { + cloud := c.T.Cloud.(azure.AzureCloud) + found, err := cloud.FederatedIdentityCredential().Get( + context.TODO(), + fi.ValueOf(f.ResourceGroup.Name), + fi.ValueOf(f.ManagedIdentity.Name), + fi.ValueOf(f.Name), + ) + if err != nil { + var respErr *azcore.ResponseError + if ok := errors.As(err, &respErr); ok && respErr.StatusCode == 404 { + return nil, nil + } + return nil, fmt.Errorf("getting federated identity credential %q: %w", fi.ValueOf(f.Name), err) + } + + result := &FederatedIdentityCredential{ + Name: f.Name, + Lifecycle: f.Lifecycle, + ManagedIdentity: f.ManagedIdentity, + ResourceGroup: f.ResourceGroup, + } + if found.Properties != nil { + result.Issuer = found.Properties.Issuer + result.Subject = found.Properties.Subject + result.Audiences = found.Properties.Audiences + } + return result, nil +} + +// Run implements fi.Task.Run. +func (f *FederatedIdentityCredential) Run(c *fi.CloudupContext) error { + return fi.CloudupDefaultDeltaRunMethod(f, c) +} + +// CheckChanges returns an error if a change is not allowed. +func (*FederatedIdentityCredential) CheckChanges(a, e, changes *FederatedIdentityCredential) error { + if a == nil { + if e.Name == nil { + return fi.RequiredField("Name") + } + return nil + } + if changes.Name != nil { + return fi.CannotChangeField("Name") + } + return nil +} + +// RenderAzure creates or updates a federated identity credential. +func (*FederatedIdentityCredential) RenderAzure(t *azure.AzureAPITarget, a, e, changes *FederatedIdentityCredential) error { + if a == nil { + klog.Infof("Creating a new Federated Identity Credential with name: %s", fi.ValueOf(e.Name)) + } else { + klog.Infof("Updating a Federated Identity Credential with name: %s", fi.ValueOf(e.Name)) + } + + _, err := t.Cloud.FederatedIdentityCredential().CreateOrUpdate( + context.TODO(), + fi.ValueOf(e.ResourceGroup.Name), + fi.ValueOf(e.ManagedIdentity.Name), + fi.ValueOf(e.Name), + armmsi.FederatedIdentityCredential{ + Properties: &armmsi.FederatedIdentityCredentialProperties{ + Issuer: e.Issuer, + Subject: e.Subject, + Audiences: e.Audiences, + }, + }, + ) + if err != nil { + return fmt.Errorf("creating/updating federated identity credential: %w", err) + } + + return nil +} diff --git a/upup/pkg/fi/cloudup/azuretasks/federatedidentitycredential_fitask.go b/upup/pkg/fi/cloudup/azuretasks/federatedidentitycredential_fitask.go new file mode 100644 index 0000000000000..d216abf236eb2 --- /dev/null +++ b/upup/pkg/fi/cloudup/azuretasks/federatedidentitycredential_fitask.go @@ -0,0 +1,52 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by fitask. DO NOT EDIT. + +package azuretasks + +import ( + "k8s.io/kops/upup/pkg/fi" +) + +// FederatedIdentityCredential + +var _ fi.HasLifecycle = (*FederatedIdentityCredential)(nil) + +// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle +func (o *FederatedIdentityCredential) GetLifecycle() fi.Lifecycle { + return o.Lifecycle +} + +// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle +func (o *FederatedIdentityCredential) SetLifecycle(lifecycle fi.Lifecycle) { + o.Lifecycle = lifecycle +} + +var _ fi.HasName = (*FederatedIdentityCredential)(nil) + +// GetName returns the Name of the object, implementing fi.HasName +func (o *FederatedIdentityCredential) GetName() *string { + return o.Name +} + +// String is the stringer function for the task, producing readable output using fi.TaskAsString +func (o *FederatedIdentityCredential) String() string { + return fi.CloudupTaskAsString(o) +} diff --git a/upup/pkg/fi/cloudup/azuretasks/federatedidentitycredential_terraform.go b/upup/pkg/fi/cloudup/azuretasks/federatedidentitycredential_terraform.go new file mode 100644 index 0000000000000..ed2e23c8d997e --- /dev/null +++ b/upup/pkg/fi/cloudup/azuretasks/federatedidentitycredential_terraform.go @@ -0,0 +1,48 @@ +/* +Copyright 2026 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package azuretasks + +import ( + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/upup/pkg/fi/cloudup/terraform" + "k8s.io/kops/upup/pkg/fi/cloudup/terraformWriter" +) + +type terraformAzureFederatedIdentityCredential struct { + Name *string `cty:"name"` + ResourceGroupName *terraformWriter.Literal `cty:"resource_group_name"` + ParentID *terraformWriter.Literal `cty:"parent_id"` + Issuer *string `cty:"issuer"` + Subject *string `cty:"subject"` + Audience []string `cty:"audience"` +} + +func (*FederatedIdentityCredential) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *FederatedIdentityCredential) error { + audience := make([]string, 0, len(e.Audiences)) + for _, a := range e.Audiences { + audience = append(audience, fi.ValueOf(a)) + } + tf := &terraformAzureFederatedIdentityCredential{ + Name: e.Name, + ResourceGroupName: e.ResourceGroup.terraformName(), + ParentID: e.ManagedIdentity.terraformID(), + Issuer: e.Issuer, + Subject: e.Subject, + Audience: audience, + } + return t.RenderResource("azurerm_federated_identity_credential", fi.ValueOf(e.Name), tf) +} diff --git a/upup/pkg/fi/cloudup/azuretasks/managedidentity.go b/upup/pkg/fi/cloudup/azuretasks/managedidentity.go new file mode 100644 index 0000000000000..47ce240182c2d --- /dev/null +++ b/upup/pkg/fi/cloudup/azuretasks/managedidentity.go @@ -0,0 +1,135 @@ +/* +Copyright 2026 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package azuretasks + +import ( + "context" + "errors" + "fmt" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi" + "k8s.io/klog/v2" + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/upup/pkg/fi/cloudup/azure" +) + +// ManagedIdentity is an Azure User-Assigned Managed Identity. +// +kops:fitask +type ManagedIdentity struct { + Name *string + Lifecycle fi.Lifecycle + + ResourceGroup *ResourceGroup + Tags map[string]*string + + // ClientID is populated after creation — this is the UAMI's client ID + // needed for workload identity azure.json configuration. + ClientID *string + // PrincipalID is populated after creation — this is the UAMI's principal ID + // needed for role assignments. + PrincipalID *string +} + +var ( + _ fi.CloudupTask = &ManagedIdentity{} + _ fi.CompareWithID = &ManagedIdentity{} +) + +// CompareWithID returns the Name of the managed identity. +func (m *ManagedIdentity) CompareWithID() *string { + return m.Name +} + +// Find discovers the ManagedIdentity in the cloud provider. +func (m *ManagedIdentity) Find(c *fi.CloudupContext) (*ManagedIdentity, error) { + cloud := c.T.Cloud.(azure.AzureCloud) + found, err := cloud.ManagedIdentity().Get(context.TODO(), fi.ValueOf(m.ResourceGroup.Name), fi.ValueOf(m.Name)) + if err != nil { + var respErr *azcore.ResponseError + if ok := errors.As(err, &respErr); ok && respErr.StatusCode == 404 { + return nil, nil + } + return nil, fmt.Errorf("getting managed identity %q: %w", fi.ValueOf(m.Name), err) + } + + result := &ManagedIdentity{ + Name: m.Name, + Lifecycle: m.Lifecycle, + ResourceGroup: m.ResourceGroup, + Tags: found.Tags, + } + if found.Properties != nil { + result.ClientID = found.Properties.ClientID + result.PrincipalID = found.Properties.PrincipalID + // Also populate the expected task so dependent tasks (e.g. RoleAssignment) + // can read these server-generated IDs when RenderAzure is skipped because + // no changes are needed. Matches the VMScaleSet.Find convention. + m.ClientID = found.Properties.ClientID + m.PrincipalID = found.Properties.PrincipalID + } + return result, nil +} + +// Run implements fi.Task.Run. +func (m *ManagedIdentity) Run(c *fi.CloudupContext) error { + return fi.CloudupDefaultDeltaRunMethod(m, c) +} + +// CheckChanges returns an error if a change is not allowed. +func (*ManagedIdentity) CheckChanges(a, e, changes *ManagedIdentity) error { + if a == nil { + if e.Name == nil { + return fi.RequiredField("Name") + } + return nil + } + if changes.Name != nil { + return fi.CannotChangeField("Name") + } + return nil +} + +// RenderAzure creates or updates a managed identity. +func (*ManagedIdentity) RenderAzure(t *azure.AzureAPITarget, a, e, changes *ManagedIdentity) error { + if a == nil { + klog.Infof("Creating a new Managed Identity with name: %s", fi.ValueOf(e.Name)) + } else { + klog.Infof("Updating a Managed Identity with name: %s", fi.ValueOf(e.Name)) + } + + result, err := t.Cloud.ManagedIdentity().CreateOrUpdate( + context.TODO(), + fi.ValueOf(e.ResourceGroup.Name), + fi.ValueOf(e.Name), + armmsi.Identity{ + Location: to.Ptr(t.Cloud.Region()), + Tags: e.Tags, + }, + ) + if err != nil { + return fmt.Errorf("creating/updating managed identity: %w", err) + } + + if result.Properties != nil { + e.ClientID = result.Properties.ClientID + e.PrincipalID = result.Properties.PrincipalID + } + + return nil +} diff --git a/upup/pkg/fi/cloudup/azuretasks/managedidentity_fitask.go b/upup/pkg/fi/cloudup/azuretasks/managedidentity_fitask.go new file mode 100644 index 0000000000000..44e6fcc6b006f --- /dev/null +++ b/upup/pkg/fi/cloudup/azuretasks/managedidentity_fitask.go @@ -0,0 +1,52 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by fitask. DO NOT EDIT. + +package azuretasks + +import ( + "k8s.io/kops/upup/pkg/fi" +) + +// ManagedIdentity + +var _ fi.HasLifecycle = (*ManagedIdentity)(nil) + +// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle +func (o *ManagedIdentity) GetLifecycle() fi.Lifecycle { + return o.Lifecycle +} + +// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle +func (o *ManagedIdentity) SetLifecycle(lifecycle fi.Lifecycle) { + o.Lifecycle = lifecycle +} + +var _ fi.HasName = (*ManagedIdentity)(nil) + +// GetName returns the Name of the object, implementing fi.HasName +func (o *ManagedIdentity) GetName() *string { + return o.Name +} + +// String is the stringer function for the task, producing readable output using fi.TaskAsString +func (o *ManagedIdentity) String() string { + return fi.CloudupTaskAsString(o) +} diff --git a/upup/pkg/fi/cloudup/azuretasks/managedidentity_terraform.go b/upup/pkg/fi/cloudup/azuretasks/managedidentity_terraform.go new file mode 100644 index 0000000000000..9da3318f93e78 --- /dev/null +++ b/upup/pkg/fi/cloudup/azuretasks/managedidentity_terraform.go @@ -0,0 +1,52 @@ +/* +Copyright 2026 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package azuretasks + +import ( + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/upup/pkg/fi/cloudup/terraform" + "k8s.io/kops/upup/pkg/fi/cloudup/terraformWriter" +) + +type terraformAzureManagedIdentity struct { + Name *string `cty:"name"` + Location *string `cty:"location"` + ResourceGroupName *terraformWriter.Literal `cty:"resource_group_name"` + Tags map[string]string `cty:"tags"` +} + +func (*ManagedIdentity) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *ManagedIdentity) error { + tf := &terraformAzureManagedIdentity{ + Name: e.Name, + Location: fi.PtrTo(t.Cloud.Region()), + ResourceGroupName: e.ResourceGroup.terraformName(), + Tags: stringMap(e.Tags), + } + return t.RenderResource("azurerm_user_assigned_identity", fi.ValueOf(e.Name), tf) +} + +// terraformID returns a Literal that resolves to the UAMI resource's `id` attribute. +func (m *ManagedIdentity) terraformID() *terraformWriter.Literal { + return terraformWriter.LiteralProperty("azurerm_user_assigned_identity", fi.ValueOf(m.Name), "id") +} + +// terraformPrincipalID returns a Literal that resolves to the UAMI's `principal_id` attribute. +// Matches the signature of VMScaleSet.terraformPrincipalID so RoleAssignment can dispatch +// on either principal source. +func (m *ManagedIdentity) terraformPrincipalID() *terraformWriter.Literal { + return terraformWriter.LiteralProperty("azurerm_user_assigned_identity", fi.ValueOf(m.Name), "principal_id") +} diff --git a/upup/pkg/fi/cloudup/azuretasks/roleassignment.go b/upup/pkg/fi/cloudup/azuretasks/roleassignment.go index 5eb312c64d105..d97a29448e7fa 100644 --- a/upup/pkg/fi/cloudup/azuretasks/roleassignment.go +++ b/upup/pkg/fi/cloudup/azuretasks/roleassignment.go @@ -45,8 +45,11 @@ type RoleAssignment struct { Scope *string VMScaleSet *VMScaleSet - ID *string - RoleDefID *string + // ManagedIdentity is an alternative principal source to VMScaleSet. + // Exactly one of VMScaleSet or ManagedIdentity should be set. + ManagedIdentity *ManagedIdentity + ID *string + RoleDefID *string } var ( @@ -60,12 +63,22 @@ func (r *RoleAssignment) CompareWithID() *string { return r.Name } +// principalID returns the principal ID from either VMScaleSet or ManagedIdentity. +func (r *RoleAssignment) principalID() *string { + if r.ManagedIdentity != nil && r.ManagedIdentity.PrincipalID != nil { + return r.ManagedIdentity.PrincipalID + } + if r.VMScaleSet != nil && r.VMScaleSet.PrincipalID != nil { + return r.VMScaleSet.PrincipalID + } + return nil +} + // Find discovers the RoleAssignment in the cloud provider. func (r *RoleAssignment) Find(c *fi.CloudupContext) (*RoleAssignment, error) { - if r.VMScaleSet.PrincipalID == nil { - // PrincipalID of the VM Scale Set hasn't yet been - // populated. No corresponding Role Assignment - // shouldn't exist in Cloud. + pid := r.principalID() + if pid == nil { + // PrincipalID hasn't yet been populated. return nil, nil } @@ -75,15 +88,13 @@ func (r *RoleAssignment) Find(c *fi.CloudupContext) (*RoleAssignment, error) { return nil, err } - principalID := *r.VMScaleSet.PrincipalID + principalID := *pid var found *authz.RoleAssignment for i := range rs { ra := rs[i] if ra.Properties == nil { continue } - // Use a name constructed by VMSS and Role definition ID to find a Role Assignment. We cannot use ra.Name - // as it is set to a randomly generated GUID. l := strings.Split(*ra.Properties.RoleDefinitionID, "/") roleDefID := l[len(l)-1] if *ra.Properties.PrincipalID == principalID && roleDefID == *r.RoleDefID { @@ -95,36 +106,45 @@ func (r *RoleAssignment) Find(c *fi.CloudupContext) (*RoleAssignment, error) { return nil, nil } - // Query VM Scale Sets and find one that has matching Principal ID. - vs, err := cloud.VMScaleSet().List(context.TODO(), *r.VMScaleSet.ResourceGroup.Name) - if err != nil { - return nil, err + result := &RoleAssignment{ + Name: r.Name, + Lifecycle: r.Lifecycle, + Scope: found.Properties.Scope, + ID: found.ID, + RoleDefID: to.Ptr(filepath.Base(*found.Properties.RoleDefinitionID)), } - var foundVMSS *compute.VirtualMachineScaleSet - for _, v := range vs { - if v.Identity == nil { - continue + + // Populate the original principal source reference. + if r.ManagedIdentity != nil { + result.ManagedIdentity = &ManagedIdentity{ + Name: r.ManagedIdentity.Name, } - if *v.Identity.PrincipalID == principalID { - foundVMSS = v - break + } else if r.VMScaleSet != nil { + // Query VM Scale Sets and find one that has matching Principal ID. + vs, err := cloud.VMScaleSet().List(context.TODO(), *r.VMScaleSet.ResourceGroup.Name) + if err != nil { + return nil, err + } + var foundVMSS *compute.VirtualMachineScaleSet + for _, v := range vs { + if v.Identity == nil { + continue + } + if *v.Identity.PrincipalID == principalID { + foundVMSS = v + break + } + } + if foundVMSS == nil { + return nil, fmt.Errorf("corresponding VM Scale Set not found for Role Assignment: %s", *found.ID) + } + result.VMScaleSet = &VMScaleSet{ + Name: foundVMSS.Name, } - } - if foundVMSS == nil { - return nil, fmt.Errorf("corresponding VM Scale Set not found for Role Assignment: %s", *found.ID) } r.ID = found.ID - return &RoleAssignment{ - Name: r.Name, - Lifecycle: r.Lifecycle, - Scope: found.Properties.Scope, - VMScaleSet: &VMScaleSet{ - Name: foundVMSS.Name, - }, - ID: found.ID, - RoleDefID: to.Ptr(filepath.Base(*found.Properties.RoleDefinitionID)), - }, nil + return result, nil } // Run implements fi.Task.Run. @@ -169,7 +189,7 @@ func createNewRoleAssignment(t *azure.AzureAPITarget, e *RoleAssignment) error { roleAssignment := authz.RoleAssignmentCreateParameters{ Properties: &authz.RoleAssignmentProperties{ RoleDefinitionID: to.Ptr(roleDefID), - PrincipalID: e.VMScaleSet.PrincipalID, + PrincipalID: e.principalID(), }, } ra, err := t.Cloud.RoleAssignment().Create(context.TODO(), scope, roleAssignmentName, roleAssignment) diff --git a/upup/pkg/fi/cloudup/azuretasks/roleassignment_terraform.go b/upup/pkg/fi/cloudup/azuretasks/roleassignment_terraform.go index 639d64e6399f6..68f2c770f64ae 100644 --- a/upup/pkg/fi/cloudup/azuretasks/roleassignment_terraform.go +++ b/upup/pkg/fi/cloudup/azuretasks/roleassignment_terraform.go @@ -32,12 +32,33 @@ type terraformAzureRoleAssignment struct { } func (*RoleAssignment) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *RoleAssignment) error { + principalID, err := e.terraformPrincipalID() + if err != nil { + return err + } roleDefinitionID := fmt.Sprintf("%s/providers/Microsoft.Authorization/roleDefinitions/%s", fi.ValueOf(e.Scope), fi.ValueOf(e.RoleDefID)) tf := &terraformAzureRoleAssignment{ Scope: e.Scope, RoleDefinitionID: &roleDefinitionID, - PrincipalID: e.VMScaleSet.terraformPrincipalID(), + PrincipalID: principalID, SkipServicePrincipalAADCheck: fi.PtrTo(true), } return t.RenderResource("azurerm_role_assignment", fi.ValueOf(e.Name), tf) } + +// terraformPrincipalID resolves the principal ID Literal from whichever +// principal source (VMScaleSet or ManagedIdentity) is set. The fi-task-level +// invariant is that exactly one is set; if that's violated we return an error +// rather than silently picking one. +func (r *RoleAssignment) terraformPrincipalID() (*terraformWriter.Literal, error) { + switch { + case r.ManagedIdentity != nil && r.VMScaleSet != nil: + return nil, fmt.Errorf("RoleAssignment %q has both ManagedIdentity and VMScaleSet set; only one is allowed", fi.ValueOf(r.Name)) + case r.ManagedIdentity != nil: + return r.ManagedIdentity.terraformPrincipalID(), nil + case r.VMScaleSet != nil: + return r.VMScaleSet.terraformPrincipalID(), nil + default: + return nil, fmt.Errorf("RoleAssignment %q has no principal source set", fi.ValueOf(r.Name)) + } +} diff --git a/upup/pkg/fi/cloudup/azuretasks/testing.go b/upup/pkg/fi/cloudup/azuretasks/testing.go index 98e464f1620a3..30f8daae43a52 100644 --- a/upup/pkg/fi/cloudup/azuretasks/testing.go +++ b/upup/pkg/fi/cloudup/azuretasks/testing.go @@ -21,9 +21,11 @@ import ( "errors" "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" authz "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthorization/v3" compute "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi" network "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork" resources "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage" @@ -59,6 +61,9 @@ type MockAzureCloud struct { PublicIPAddressesClient *MockPublicIPAddressesClient NatGatewaysClient *MockNatGatewaysClient StorageAccountsClient *MockStorageAccountsClient + // Azure Workload Identity + ManagedIdentitiesClient *MockManagedIdentityClient + FederatedIdentityCredentialsClient *MockFederatedIdentityCredentialClient } var _ azure.AzureCloud = (*MockAzureCloud)(nil) @@ -120,6 +125,12 @@ func NewMockAzureCloud(location string) *MockAzureCloud { StorageAccountsClient: &MockStorageAccountsClient{ SAs: map[string]*armstorage.Account{}, }, + ManagedIdentitiesClient: &MockManagedIdentityClient{ + Identities: map[string]*armmsi.Identity{}, + }, + FederatedIdentityCredentialsClient: &MockFederatedIdentityCredentialClient{ + Credentials: map[string]*armmsi.FederatedIdentityCredential{}, + }, } } @@ -272,6 +283,16 @@ func (c *MockAzureCloud) NatGateway() azure.NatGatewaysClient { return c.NatGatewaysClient } +// ManagedIdentity returns the managed identity client. +func (c *MockAzureCloud) ManagedIdentity() azure.ManagedIdentityClient { + return c.ManagedIdentitiesClient +} + +// FederatedIdentityCredential returns the federated identity credential client. +func (c *MockAzureCloud) FederatedIdentityCredential() azure.FederatedIdentityCredentialClient { + return c.FederatedIdentityCredentialsClient +} + // MockResourceGroupsClient is a mock implementation of resource group client. type MockResourceGroupsClient struct { RGs map[string]*resources.ResourceGroup @@ -834,3 +855,70 @@ func (c *MockStorageAccountsClient) List(ctx context.Context) ([]*armstorage.Acc } return l, nil } + +// MockManagedIdentityClient is a mock implementation of managed identity client. +type MockManagedIdentityClient struct { + Identities map[string]*armmsi.Identity +} + +var _ azure.ManagedIdentityClient = (*MockManagedIdentityClient)(nil) + +func (c *MockManagedIdentityClient) Get(ctx context.Context, resourceGroupName, identityName string) (*armmsi.Identity, error) { + id, ok := c.Identities[identityName] + if !ok { + return nil, &azcore.ResponseError{StatusCode: 404, ErrorCode: "ResourceNotFound"} + } + return id, nil +} + +func (c *MockManagedIdentityClient) CreateOrUpdate(ctx context.Context, resourceGroupName, identityName string, parameters armmsi.Identity) (*armmsi.Identity, error) { + parameters.Name = &identityName + clientID := uuid.New().String() + principalID := uuid.New().String() + if parameters.Properties == nil { + parameters.Properties = &armmsi.UserAssignedIdentityProperties{} + } + parameters.Properties.ClientID = &clientID + parameters.Properties.PrincipalID = &principalID + c.Identities[identityName] = ¶meters + return ¶meters, nil +} + +func (c *MockManagedIdentityClient) Delete(ctx context.Context, resourceGroupName, identityName string) error { + delete(c.Identities, identityName) + return nil +} + +// MockFederatedIdentityCredentialClient is a mock implementation of federated identity credential client. +type MockFederatedIdentityCredentialClient struct { + Credentials map[string]*armmsi.FederatedIdentityCredential +} + +var _ azure.FederatedIdentityCredentialClient = (*MockFederatedIdentityCredentialClient)(nil) + +func (c *MockFederatedIdentityCredentialClient) Get(ctx context.Context, resourceGroupName, identityName, credentialName string) (*armmsi.FederatedIdentityCredential, error) { + cred, ok := c.Credentials[credentialName] + if !ok { + return nil, &azcore.ResponseError{StatusCode: 404, ErrorCode: "ResourceNotFound"} + } + return cred, nil +} + +func (c *MockFederatedIdentityCredentialClient) CreateOrUpdate(ctx context.Context, resourceGroupName, identityName, credentialName string, parameters armmsi.FederatedIdentityCredential) (*armmsi.FederatedIdentityCredential, error) { + parameters.Name = &credentialName + c.Credentials[credentialName] = ¶meters + return ¶meters, nil +} + +func (c *MockFederatedIdentityCredentialClient) Delete(ctx context.Context, resourceGroupName, identityName, credentialName string) error { + delete(c.Credentials, credentialName) + return nil +} + +func (c *MockFederatedIdentityCredentialClient) List(ctx context.Context, resourceGroupName, identityName string) ([]*armmsi.FederatedIdentityCredential, error) { + var l []*armmsi.FederatedIdentityCredential + for _, cred := range c.Credentials { + l = append(l, cred) + } + return l, nil +} diff --git a/upup/pkg/fi/cloudup/new_cluster.go b/upup/pkg/fi/cloudup/new_cluster.go index d6556aecc9d44..7776bc27bace5 100644 --- a/upup/pkg/fi/cloudup/new_cluster.go +++ b/upup/pkg/fi/cloudup/new_cluster.go @@ -402,6 +402,9 @@ func NewCluster(opt *NewClusterOptions, clientset simple.Clientset) (*NewCluster cluster.Spec.ServiceAccountIssuerDiscovery.EnableAWSOIDCProvider = true cluster.Spec.IAM.UseServiceAccountExternalPermissions = fi.PtrTo(true) } + if cluster.GetCloudProvider() == api.CloudProviderAzure { + cluster.Spec.IAM.UseServiceAccountExternalPermissions = fi.PtrTo(true) + } } err = setupVPC(opt, cluster, cloud) diff --git a/upup/pkg/fi/cloudup/template_functions.go b/upup/pkg/fi/cloudup/template_functions.go index 54ccfb2ff2239..3459bd8062dfb 100644 --- a/upup/pkg/fi/cloudup/template_functions.go +++ b/upup/pkg/fi/cloudup/template_functions.go @@ -352,6 +352,45 @@ func (tf *TemplateFunctions) AddTo(dest template.FuncMap, secretStore fi.SecretS dest["IsIPv6Only"] = tf.IsIPv6Only dest["UseServiceAccountExternalPermissions"] = tf.UseServiceAccountExternalPermissions + dest["AzureWorkloadIdentityClientID"] = func() string { + if cluster.Spec.CloudProvider.Azure != nil { + return cluster.Spec.CloudProvider.Azure.WorkloadIdentityClientID + } + return "" + } + dest["AzureTenantID"] = func() string { + if cluster.Spec.CloudProvider.Azure != nil { + return cluster.Spec.CloudProvider.Azure.TenantID + } + return "" + } + dest["AzureSubscriptionID"] = func() string { + if cluster.Spec.CloudProvider.Azure != nil { + return cluster.Spec.CloudProvider.Azure.SubscriptionID + } + return "" + } + dest["AzureResourceGroupName"] = func() string { + return cluster.AzureResourceGroupName() + } + dest["AzureVnetName"] = func() string { + return cluster.AzureNetworkName() + } + dest["AzureSubnetName"] = func() string { + if len(cluster.Spec.Networking.Subnets) > 0 { + return cluster.Spec.Networking.Subnets[0].Name + } + return "" + } + dest["AzureLocation"] = func() string { + if len(cluster.Spec.Networking.Subnets) > 0 { + return cluster.Spec.Networking.Subnets[0].Region + } + return "" + } + dest["AzureSecurityGroupName"] = func() string { + return cluster.AzureNetworkSecurityGroupName() + } if cluster.Spec.ClusterAutoscaler != nil { dest["ClusterAutoscalerPriorities"] = func() string { diff --git a/util/pkg/vfs/azureblob.go b/util/pkg/vfs/azureblob.go index 5ac19fd1ed03e..faab4adae94f4 100644 --- a/util/pkg/vfs/azureblob.go +++ b/util/pkg/vfs/azureblob.go @@ -89,6 +89,35 @@ func (p *AzureBlobPath) Path() string { return fmt.Sprintf("azureblob://%s/%s", p.container, p.key) } +// GetHTTPsUrl returns the public HTTPS URL for the Azure Blob path. +// The storage account name is read from the AZURE_STORAGE_ACCOUNT environment variable. +func (p *AzureBlobPath) GetHTTPsUrl() (string, error) { + storageAccountName := os.Getenv("AZURE_STORAGE_ACCOUNT") + if storageAccountName == "" { + return "", fmt.Errorf("AZURE_STORAGE_ACCOUNT is not set") + } + url := fmt.Sprintf("https://%s.blob.core.windows.net/%s/%s", storageAccountName, p.container, p.key) + return strings.TrimSuffix(url, "/"), nil +} + +// IsBucketPublic reports whether the Azure Blob container allows anonymous +// read access (either "blob" or "container" public access level). OIDC +// discovery endpoints require this; kops does not manage container ACLs +// itself, so callers that need public access must pre-configure the container. +func (p *AzureBlobPath) IsBucketPublic(ctx context.Context) (bool, error) { + client, err := p.getClient(ctx) + if err != nil { + return false, err + } + resp, err := client.ServiceClient().NewContainerClient(p.container).GetProperties(ctx, nil) + if err != nil { + return false, fmt.Errorf("getting properties for Azure Blob container %q: %w", p.container, err) + } + // BlobPublicAccess is nil for "private"; any non-nil value ("blob" or + // "container") permits anonymous reads of individual blobs. + return resp.BlobPublicAccess != nil, nil +} + // Join returns a new path that joins the current path and given relative paths. func (p *AzureBlobPath) Join(relativePath ...string) Path { args := []string{p.key} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/CHANGELOG.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/CHANGELOG.md new file mode 100644 index 0000000000000..34085f0955218 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/CHANGELOG.md @@ -0,0 +1,68 @@ +# Release History + +## 1.3.0 (2025-07-25) +### Features Added + +- New enum type `IsolationScope` with values `IsolationScopeNone`, `IsolationScopeRegional` +- New field `IsolationScope` in struct `UserAssignedIdentityProperties` + + +## 1.2.0 (2023-11-24) +### Features Added + +- Support for test fakes and OpenTelemetry trace spans. + + +## 1.1.0 (2023-03-31) +### Features Added + +- New struct `ClientFactory` which is a client factory used to create any client in this module + + +## 1.0.0 (2023-02-24) +### Breaking Changes + +- Function `*UserAssignedIdentitiesClient.NewListAssociatedResourcesPager` has been removed + +### Features Added + +- New type alias `CreatedByType` with values `CreatedByTypeApplication`, `CreatedByTypeKey`, `CreatedByTypeManagedIdentity`, `CreatedByTypeUser` +- New function `timeRFC3339.MarshalText() ([]byte, error)` +- New function `*timeRFC3339.Parse(string) error` +- New function `*timeRFC3339.UnmarshalText([]byte) error` +- New struct `SystemData` +- New field `SystemData` in struct `FederatedIdentityCredential` +- New field `SystemData` in struct `Identity` +- New field `SystemData` in struct `IdentityUpdate` +- New field `SystemData` in struct `SystemAssignedIdentity` + + +## 0.7.0 (2022-06-27) +### Features Added + +- New function `*FederatedIdentityCredentialsClient.Delete(context.Context, string, string, string, *FederatedIdentityCredentialsClientDeleteOptions) (FederatedIdentityCredentialsClientDeleteResponse, error)` +- New function `*FederatedIdentityCredentialsClient.CreateOrUpdate(context.Context, string, string, string, FederatedIdentityCredential, *FederatedIdentityCredentialsClientCreateOrUpdateOptions) (FederatedIdentityCredentialsClientCreateOrUpdateResponse, error)` +- New function `*FederatedIdentityCredentialsClient.NewListPager(string, string, *FederatedIdentityCredentialsClientListOptions) *runtime.Pager[FederatedIdentityCredentialsClientListResponse]` +- New function `NewFederatedIdentityCredentialsClient(string, azcore.TokenCredential, *arm.ClientOptions) (*FederatedIdentityCredentialsClient, error)` +- New function `*FederatedIdentityCredentialsClient.Get(context.Context, string, string, string, *FederatedIdentityCredentialsClientGetOptions) (FederatedIdentityCredentialsClientGetResponse, error)` +- New struct `FederatedIdentityCredential` +- New struct `FederatedIdentityCredentialProperties` +- New struct `FederatedIdentityCredentialsClient` +- New struct `FederatedIdentityCredentialsClientCreateOrUpdateOptions` +- New struct `FederatedIdentityCredentialsClientCreateOrUpdateResponse` +- New struct `FederatedIdentityCredentialsClientDeleteOptions` +- New struct `FederatedIdentityCredentialsClientDeleteResponse` +- New struct `FederatedIdentityCredentialsClientGetOptions` +- New struct `FederatedIdentityCredentialsClientGetResponse` +- New struct `FederatedIdentityCredentialsClientListOptions` +- New struct `FederatedIdentityCredentialsClientListResponse` +- New struct `FederatedIdentityCredentialsListResult` + + +## 0.6.0 (2022-05-17) + +The package of `github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi` is using our [next generation design principles](https://azure.github.io/azure-sdk/general_introduction.html) since version 0.6.0, which contains breaking changes. + +To migrate the existing applications to the latest version, please refer to [Migration Guide](https://aka.ms/azsdk/go/mgmt/migration). + +To learn more, please refer to our documentation [Quick Start](https://aka.ms/azsdk/go/mgmt). \ No newline at end of file diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/LICENSE.txt b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/LICENSE.txt new file mode 100644 index 0000000000000..dc0c2ffb3dc15 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/README.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/README.md new file mode 100644 index 0000000000000..8c86245d85a18 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/README.md @@ -0,0 +1,90 @@ +# Azure Managed Service Identity Module for Go + +The `armmsi` module provides operations for working with Azure Managed Service Identity. + +[Source code](https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/resourcemanager/msi/armmsi) + +# Getting started + +## Prerequisites + +- an [Azure subscription](https://azure.microsoft.com/free/) +- [Supported](https://aka.ms/azsdk/go/supported-versions) version of Go (You could download and install the latest version of Go from [here](https://go.dev/doc/install). It will replace the existing Go on your machine. If you want to install multiple Go versions on the same machine, you could refer this [doc](https://go.dev/doc/manage-install).) + +## Install the package + +This project uses [Go modules](https://github.com/golang/go/wiki/Modules) for versioning and dependency management. + +Install the Azure Managed Service Identity module: + +```sh +go get github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi +``` + +## Authorization + +When creating a client, you will need to provide a credential for authenticating with Azure Managed Service Identity. The `azidentity` module provides facilities for various ways of authenticating with Azure including client/secret, certificate, managed identity, and more. + +```go +cred, err := azidentity.NewDefaultAzureCredential(nil) +``` + +For more information on authentication, please see the documentation for `azidentity` at [pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity). + +## Client Factory + +Azure Managed Service Identity module consists of one or more clients. We provide a client factory which could be used to create any client in this module. + +```go +clientFactory, err := armmsi.NewClientFactory(, cred, nil) +``` + +You can use `ClientOptions` in package `github.com/Azure/azure-sdk-for-go/sdk/azcore/arm` to set endpoint to connect with public and sovereign clouds as well as Azure Stack. For more information, please see the documentation for `azcore` at [pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azcore](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azcore). + +```go +options := arm.ClientOptions { + ClientOptions: azcore.ClientOptions { + Cloud: cloud.AzureChina, + }, +} +clientFactory, err := armmsi.NewClientFactory(, cred, &options) +``` + +## Clients + +A client groups a set of related APIs, providing access to its functionality. Create one or more clients to access the APIs you require using client factory. + +```go +client := clientFactory.NewFederatedIdentityCredentialsClient() +``` + +## Fakes + +The fake package contains types used for constructing in-memory fake servers used in unit tests. +This allows writing tests to cover various success/error conditions without the need for connecting to a live service. + +Please see https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/samples/fakes for details and examples on how to use fakes. + +## Provide Feedback + +If you encounter bugs or have suggestions, please +[open an issue](https://github.com/Azure/azure-sdk-for-go/issues) and assign the `Managed Service Identity` label. + +# Contributing + +This project welcomes contributions and suggestions. Most contributions require +you to agree to a Contributor License Agreement (CLA) declaring that you have +the right to, and actually do, grant us the rights to use your contribution. +For details, visit [https://cla.microsoft.com](https://cla.microsoft.com). + +When you submit a pull request, a CLA-bot will automatically determine whether +you need to provide a CLA and decorate the PR appropriately (e.g., label, +comment). Simply follow the instructions provided by the bot. You will only +need to do this once across all repos using our CLA. + +This project has adopted the +[Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information, see the +[Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any +additional questions or comments. diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/assets.json b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/assets.json new file mode 100644 index 0000000000000..84324b8b7300e --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/assets.json @@ -0,0 +1,6 @@ +{ + "AssetsRepo": "Azure/azure-sdk-assets", + "AssetsRepoPrefixPath": "go", + "TagPrefix": "go/resourcemanager/msi/armmsi", + "Tag": "go/resourcemanager/msi/armmsi_dbfb7feb00" +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/autorest.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/autorest.md new file mode 100644 index 0000000000000..c735b46f0ab12 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/autorest.md @@ -0,0 +1,13 @@ +### AutoRest Configuration + +> see https://aka.ms/autorest + +``` yaml +azure-arm: true +require: +- https://github.com/Azure/azure-rest-api-specs/blob/44319b51c6f952fdc9543d3dc4fdd9959350d102/specification/msi/resource-manager/readme.md +- https://github.com/Azure/azure-rest-api-specs/blob/44319b51c6f952fdc9543d3dc4fdd9959350d102/specification/msi/resource-manager/readme.go.md +license-header: MICROSOFT_MIT_NO_VERSION +module-version: 1.3.0 +tag: package-2024-11-30 +``` \ No newline at end of file diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/build.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/build.go new file mode 100644 index 0000000000000..1c3e780b1829b --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/build.go @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +// This file enables 'go generate' to regenerate this specific SDK +//go:generate pwsh ../../../../eng/scripts/build.ps1 -skipBuild -cleanGenerated -format -tidy -generate -alwaysSetBodyParamRequired -removeUnreferencedTypes resourcemanager/msi/armmsi + +package armmsi diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/ci.yml b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/ci.yml new file mode 100644 index 0000000000000..c897a929e2db1 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/ci.yml @@ -0,0 +1,28 @@ +# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. +trigger: + branches: + include: + - main + - feature/* + - hotfix/* + - release/* + paths: + include: + - sdk/resourcemanager/msi/armmsi/ + +pr: + branches: + include: + - main + - feature/* + - hotfix/* + - release/* + paths: + include: + - sdk/resourcemanager/msi/armmsi/ + +extends: + template: /eng/pipelines/templates/jobs/archetype-sdk-client.yml + parameters: + ServiceDirectory: 'resourcemanager/msi/armmsi' + UsePipelineProxy: false diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/client_factory.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/client_factory.go new file mode 100644 index 0000000000000..fd7d09d85fa64 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/client_factory.go @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import ( + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" +) + +// ClientFactory is a client factory used to create any client in this module. +// Don't use this type directly, use NewClientFactory instead. +type ClientFactory struct { + subscriptionID string + internal *arm.Client +} + +// NewClientFactory creates a new instance of ClientFactory with the specified values. +// The parameter values will be propagated to any client created from this factory. +// - subscriptionID - The Id of the Subscription to which the identity belongs. +// - credential - used to authorize requests. Usually a credential from azidentity. +// - options - pass nil to accept the default values. +func NewClientFactory(subscriptionID string, credential azcore.TokenCredential, options *arm.ClientOptions) (*ClientFactory, error) { + internal, err := arm.NewClient(moduleName, moduleVersion, credential, options) + if err != nil { + return nil, err + } + return &ClientFactory{ + subscriptionID: subscriptionID, + internal: internal, + }, nil +} + +// NewFederatedIdentityCredentialsClient creates a new instance of FederatedIdentityCredentialsClient. +func (c *ClientFactory) NewFederatedIdentityCredentialsClient() *FederatedIdentityCredentialsClient { + return &FederatedIdentityCredentialsClient{ + subscriptionID: c.subscriptionID, + internal: c.internal, + } +} + +// NewOperationsClient creates a new instance of OperationsClient. +func (c *ClientFactory) NewOperationsClient() *OperationsClient { + return &OperationsClient{ + internal: c.internal, + } +} + +// NewSystemAssignedIdentitiesClient creates a new instance of SystemAssignedIdentitiesClient. +func (c *ClientFactory) NewSystemAssignedIdentitiesClient() *SystemAssignedIdentitiesClient { + return &SystemAssignedIdentitiesClient{ + internal: c.internal, + } +} + +// NewUserAssignedIdentitiesClient creates a new instance of UserAssignedIdentitiesClient. +func (c *ClientFactory) NewUserAssignedIdentitiesClient() *UserAssignedIdentitiesClient { + return &UserAssignedIdentitiesClient{ + subscriptionID: c.subscriptionID, + internal: c.internal, + } +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/constants.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/constants.go new file mode 100644 index 0000000000000..d33c5e1e4cc17 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/constants.go @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +const ( + moduleName = "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi" + moduleVersion = "v1.3.0" +) + +// CreatedByType - The type of identity that created the resource. +type CreatedByType string + +const ( + CreatedByTypeApplication CreatedByType = "Application" + CreatedByTypeKey CreatedByType = "Key" + CreatedByTypeManagedIdentity CreatedByType = "ManagedIdentity" + CreatedByTypeUser CreatedByType = "User" +) + +// PossibleCreatedByTypeValues returns the possible values for the CreatedByType const type. +func PossibleCreatedByTypeValues() []CreatedByType { + return []CreatedByType{ + CreatedByTypeApplication, + CreatedByTypeKey, + CreatedByTypeManagedIdentity, + CreatedByTypeUser, + } +} + +// IsolationScope - Enum to configure regional restrictions on identity assignment, as necessary. +type IsolationScope string + +const ( + IsolationScopeNone IsolationScope = "None" + IsolationScopeRegional IsolationScope = "Regional" +) + +// PossibleIsolationScopeValues returns the possible values for the IsolationScope const type. +func PossibleIsolationScopeValues() []IsolationScope { + return []IsolationScope{ + IsolationScopeNone, + IsolationScopeRegional, + } +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/federatedidentitycredentials_client.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/federatedidentitycredentials_client.go new file mode 100644 index 0000000000000..05f06385c5e38 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/federatedidentitycredentials_client.go @@ -0,0 +1,317 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import ( + "context" + "errors" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "net/http" + "net/url" + "strconv" + "strings" +) + +// FederatedIdentityCredentialsClient contains the methods for the FederatedIdentityCredentials group. +// Don't use this type directly, use NewFederatedIdentityCredentialsClient() instead. +type FederatedIdentityCredentialsClient struct { + internal *arm.Client + subscriptionID string +} + +// NewFederatedIdentityCredentialsClient creates a new instance of FederatedIdentityCredentialsClient with the specified values. +// - subscriptionID - The Id of the Subscription to which the identity belongs. +// - credential - used to authorize requests. Usually a credential from azidentity. +// - options - pass nil to accept the default values. +func NewFederatedIdentityCredentialsClient(subscriptionID string, credential azcore.TokenCredential, options *arm.ClientOptions) (*FederatedIdentityCredentialsClient, error) { + cl, err := arm.NewClient(moduleName, moduleVersion, credential, options) + if err != nil { + return nil, err + } + client := &FederatedIdentityCredentialsClient{ + subscriptionID: subscriptionID, + internal: cl, + } + return client, nil +} + +// CreateOrUpdate - Create or update a federated identity credential under the specified user assigned identity. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the resource group. The name is case insensitive. +// - resourceName - The name of the identity resource. +// - federatedIdentityCredentialResourceName - The name of the federated identity credential resource. +// - parameters - Parameters to create or update the federated identity credential. +// - options - FederatedIdentityCredentialsClientCreateOrUpdateOptions contains the optional parameters for the FederatedIdentityCredentialsClient.CreateOrUpdate +// method. +func (client *FederatedIdentityCredentialsClient) CreateOrUpdate(ctx context.Context, resourceGroupName string, resourceName string, federatedIdentityCredentialResourceName string, parameters FederatedIdentityCredential, options *FederatedIdentityCredentialsClientCreateOrUpdateOptions) (FederatedIdentityCredentialsClientCreateOrUpdateResponse, error) { + var err error + const operationName = "FederatedIdentityCredentialsClient.CreateOrUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.createOrUpdateCreateRequest(ctx, resourceGroupName, resourceName, federatedIdentityCredentialResourceName, parameters, options) + if err != nil { + return FederatedIdentityCredentialsClientCreateOrUpdateResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return FederatedIdentityCredentialsClientCreateOrUpdateResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK, http.StatusCreated) { + err = runtime.NewResponseError(httpResp) + return FederatedIdentityCredentialsClientCreateOrUpdateResponse{}, err + } + resp, err := client.createOrUpdateHandleResponse(httpResp) + return resp, err +} + +// createOrUpdateCreateRequest creates the CreateOrUpdate request. +func (client *FederatedIdentityCredentialsClient) createOrUpdateCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, federatedIdentityCredentialResourceName string, parameters FederatedIdentityCredential, _ *FederatedIdentityCredentialsClientCreateOrUpdateOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}/federatedIdentityCredentials/{federatedIdentityCredentialResourceName}" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + if federatedIdentityCredentialResourceName == "" { + return nil, errors.New("parameter federatedIdentityCredentialResourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{federatedIdentityCredentialResourceName}", url.PathEscape(federatedIdentityCredentialResourceName)) + req, err := runtime.NewRequest(ctx, http.MethodPut, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + if err := runtime.MarshalAsJSON(req, parameters); err != nil { + return nil, err + } + return req, nil +} + +// createOrUpdateHandleResponse handles the CreateOrUpdate response. +func (client *FederatedIdentityCredentialsClient) createOrUpdateHandleResponse(resp *http.Response) (FederatedIdentityCredentialsClientCreateOrUpdateResponse, error) { + result := FederatedIdentityCredentialsClientCreateOrUpdateResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.FederatedIdentityCredential); err != nil { + return FederatedIdentityCredentialsClientCreateOrUpdateResponse{}, err + } + return result, nil +} + +// Delete - Deletes the federated identity credential. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the resource group. The name is case insensitive. +// - resourceName - The name of the identity resource. +// - federatedIdentityCredentialResourceName - The name of the federated identity credential resource. +// - options - FederatedIdentityCredentialsClientDeleteOptions contains the optional parameters for the FederatedIdentityCredentialsClient.Delete +// method. +func (client *FederatedIdentityCredentialsClient) Delete(ctx context.Context, resourceGroupName string, resourceName string, federatedIdentityCredentialResourceName string, options *FederatedIdentityCredentialsClientDeleteOptions) (FederatedIdentityCredentialsClientDeleteResponse, error) { + var err error + const operationName = "FederatedIdentityCredentialsClient.Delete" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.deleteCreateRequest(ctx, resourceGroupName, resourceName, federatedIdentityCredentialResourceName, options) + if err != nil { + return FederatedIdentityCredentialsClientDeleteResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return FederatedIdentityCredentialsClientDeleteResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK, http.StatusNoContent) { + err = runtime.NewResponseError(httpResp) + return FederatedIdentityCredentialsClientDeleteResponse{}, err + } + return FederatedIdentityCredentialsClientDeleteResponse{}, nil +} + +// deleteCreateRequest creates the Delete request. +func (client *FederatedIdentityCredentialsClient) deleteCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, federatedIdentityCredentialResourceName string, _ *FederatedIdentityCredentialsClientDeleteOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}/federatedIdentityCredentials/{federatedIdentityCredentialResourceName}" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + if federatedIdentityCredentialResourceName == "" { + return nil, errors.New("parameter federatedIdentityCredentialResourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{federatedIdentityCredentialResourceName}", url.PathEscape(federatedIdentityCredentialResourceName)) + req, err := runtime.NewRequest(ctx, http.MethodDelete, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// Get - Gets the federated identity credential. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the resource group. The name is case insensitive. +// - resourceName - The name of the identity resource. +// - federatedIdentityCredentialResourceName - The name of the federated identity credential resource. +// - options - FederatedIdentityCredentialsClientGetOptions contains the optional parameters for the FederatedIdentityCredentialsClient.Get +// method. +func (client *FederatedIdentityCredentialsClient) Get(ctx context.Context, resourceGroupName string, resourceName string, federatedIdentityCredentialResourceName string, options *FederatedIdentityCredentialsClientGetOptions) (FederatedIdentityCredentialsClientGetResponse, error) { + var err error + const operationName = "FederatedIdentityCredentialsClient.Get" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.getCreateRequest(ctx, resourceGroupName, resourceName, federatedIdentityCredentialResourceName, options) + if err != nil { + return FederatedIdentityCredentialsClientGetResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return FederatedIdentityCredentialsClientGetResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = runtime.NewResponseError(httpResp) + return FederatedIdentityCredentialsClientGetResponse{}, err + } + resp, err := client.getHandleResponse(httpResp) + return resp, err +} + +// getCreateRequest creates the Get request. +func (client *FederatedIdentityCredentialsClient) getCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, federatedIdentityCredentialResourceName string, _ *FederatedIdentityCredentialsClientGetOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}/federatedIdentityCredentials/{federatedIdentityCredentialResourceName}" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + if federatedIdentityCredentialResourceName == "" { + return nil, errors.New("parameter federatedIdentityCredentialResourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{federatedIdentityCredentialResourceName}", url.PathEscape(federatedIdentityCredentialResourceName)) + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// getHandleResponse handles the Get response. +func (client *FederatedIdentityCredentialsClient) getHandleResponse(resp *http.Response) (FederatedIdentityCredentialsClientGetResponse, error) { + result := FederatedIdentityCredentialsClientGetResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.FederatedIdentityCredential); err != nil { + return FederatedIdentityCredentialsClientGetResponse{}, err + } + return result, nil +} + +// NewListPager - Lists all the federated identity credentials under the specified user assigned identity. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the resource group. The name is case insensitive. +// - resourceName - The name of the identity resource. +// - options - FederatedIdentityCredentialsClientListOptions contains the optional parameters for the FederatedIdentityCredentialsClient.NewListPager +// method. +func (client *FederatedIdentityCredentialsClient) NewListPager(resourceGroupName string, resourceName string, options *FederatedIdentityCredentialsClientListOptions) *runtime.Pager[FederatedIdentityCredentialsClientListResponse] { + return runtime.NewPager(runtime.PagingHandler[FederatedIdentityCredentialsClientListResponse]{ + More: func(page FederatedIdentityCredentialsClientListResponse) bool { + return page.NextLink != nil && len(*page.NextLink) > 0 + }, + Fetcher: func(ctx context.Context, page *FederatedIdentityCredentialsClientListResponse) (FederatedIdentityCredentialsClientListResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "FederatedIdentityCredentialsClient.NewListPager") + nextLink := "" + if page != nil { + nextLink = *page.NextLink + } + resp, err := runtime.FetcherForNextLink(ctx, client.internal.Pipeline(), nextLink, func(ctx context.Context) (*policy.Request, error) { + return client.listCreateRequest(ctx, resourceGroupName, resourceName, options) + }, nil) + if err != nil { + return FederatedIdentityCredentialsClientListResponse{}, err + } + return client.listHandleResponse(resp) + }, + Tracer: client.internal.Tracer(), + }) +} + +// listCreateRequest creates the List request. +func (client *FederatedIdentityCredentialsClient) listCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, options *FederatedIdentityCredentialsClientListOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}/federatedIdentityCredentials" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + if options != nil && options.Skiptoken != nil { + reqQP.Set("$skiptoken", *options.Skiptoken) + } + if options != nil && options.Top != nil { + reqQP.Set("$top", strconv.FormatInt(int64(*options.Top), 10)) + } + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// listHandleResponse handles the List response. +func (client *FederatedIdentityCredentialsClient) listHandleResponse(resp *http.Response) (FederatedIdentityCredentialsClientListResponse, error) { + result := FederatedIdentityCredentialsClientListResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.FederatedIdentityCredentialsListResult); err != nil { + return FederatedIdentityCredentialsClientListResponse{}, err + } + return result, nil +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/models.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/models.go new file mode 100644 index 0000000000000..6bf4970953996 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/models.go @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import "time" + +// FederatedIdentityCredential - Describes a federated identity credential. +type FederatedIdentityCredential struct { + // The properties associated with the federated identity credential. + Properties *FederatedIdentityCredentialProperties + + // READ-ONLY; Fully qualified resource ID for the resource. E.g. "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}" + ID *string + + // READ-ONLY; The name of the resource + Name *string + + // READ-ONLY; Azure Resource Manager metadata containing createdBy and modifiedBy information. + SystemData *SystemData + + // READ-ONLY; The type of the resource. E.g. "Microsoft.Compute/virtualMachines" or "Microsoft.Storage/storageAccounts" + Type *string +} + +// FederatedIdentityCredentialProperties - The properties associated with a federated identity credential. +type FederatedIdentityCredentialProperties struct { + // REQUIRED; The list of audiences that can appear in the issued token. + Audiences []*string + + // REQUIRED; The URL of the issuer to be trusted. + Issuer *string + + // REQUIRED; The identifier of the external identity. + Subject *string +} + +// FederatedIdentityCredentialsListResult - Values returned by the List operation for federated identity credentials. +type FederatedIdentityCredentialsListResult struct { + // The url to get the next page of results, if any. + NextLink *string + + // The collection of federated identity credentials returned by the listing operation. + Value []*FederatedIdentityCredential +} + +// Identity - Describes an identity resource. +type Identity struct { + // REQUIRED; The geo-location where the resource lives + Location *string + + // Resource tags. + Tags map[string]*string + + // READ-ONLY; Fully qualified resource ID for the resource. E.g. "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}" + ID *string + + // READ-ONLY; The name of the resource + Name *string + + // READ-ONLY; The properties associated with the identity. + Properties *UserAssignedIdentityProperties + + // READ-ONLY; Azure Resource Manager metadata containing createdBy and modifiedBy information. + SystemData *SystemData + + // READ-ONLY; The type of the resource. E.g. "Microsoft.Compute/virtualMachines" or "Microsoft.Storage/storageAccounts" + Type *string +} + +// IdentityUpdate - Describes an identity resource. +type IdentityUpdate struct { + // The geo-location where the resource lives + Location *string + + // Resource tags + Tags map[string]*string + + // READ-ONLY; Fully qualified resource ID for the resource. E.g. "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}" + ID *string + + // READ-ONLY; The name of the resource + Name *string + + // READ-ONLY; The properties associated with the identity. + Properties *UserAssignedIdentityProperties + + // READ-ONLY; Azure Resource Manager metadata containing createdBy and modifiedBy information. + SystemData *SystemData + + // READ-ONLY; The type of the resource. E.g. "Microsoft.Compute/virtualMachines" or "Microsoft.Storage/storageAccounts" + Type *string +} + +// Operation supported by the Microsoft.ManagedIdentity REST API. +type Operation struct { + // The object that describes the operation. + Display *OperationDisplay + + // The name of the REST Operation. This is of the format {provider}/{resource}/{operation}. + Name *string +} + +// OperationDisplay - The object that describes the operation. +type OperationDisplay struct { + // A description of the operation. + Description *string + + // The type of operation. For example: read, write, delete. + Operation *string + + // Friendly name of the resource provider. + Provider *string + + // The resource type on which the operation is performed. + Resource *string +} + +// OperationListResult - A list of operations supported by Microsoft.ManagedIdentity Resource Provider. +type OperationListResult struct { + // The url to get the next page of results, if any. + NextLink *string + + // A list of operations supported by Microsoft.ManagedIdentity Resource Provider. + Value []*Operation +} + +// SystemAssignedIdentity - Describes a system assigned identity resource. +type SystemAssignedIdentity struct { + // REQUIRED; The geo-location where the resource lives + Location *string + + // Resource tags + Tags map[string]*string + + // READ-ONLY; Fully qualified resource ID for the resource. E.g. "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}" + ID *string + + // READ-ONLY; The name of the resource + Name *string + + // READ-ONLY; The properties associated with the identity. + Properties *SystemAssignedIdentityProperties + + // READ-ONLY; Azure Resource Manager metadata containing createdBy and modifiedBy information. + SystemData *SystemData + + // READ-ONLY; The type of the resource. E.g. "Microsoft.Compute/virtualMachines" or "Microsoft.Storage/storageAccounts" + Type *string +} + +// SystemAssignedIdentityProperties - The properties associated with the system assigned identity. +type SystemAssignedIdentityProperties struct { + // READ-ONLY; The id of the app associated with the identity. This is a random generated UUID by MSI. + ClientID *string + + // READ-ONLY; The ManagedServiceIdentity DataPlane URL that can be queried to obtain the identity credentials. + ClientSecretURL *string + + // READ-ONLY; The id of the service principal object associated with the created identity. + PrincipalID *string + + // READ-ONLY; The id of the tenant which the identity belongs to. + TenantID *string +} + +// SystemData - Metadata pertaining to creation and last modification of the resource. +type SystemData struct { + // The timestamp of resource creation (UTC). + CreatedAt *time.Time + + // The identity that created the resource. + CreatedBy *string + + // The type of identity that created the resource. + CreatedByType *CreatedByType + + // The timestamp of resource last modification (UTC) + LastModifiedAt *time.Time + + // The identity that last modified the resource. + LastModifiedBy *string + + // The type of identity that last modified the resource. + LastModifiedByType *CreatedByType +} + +// UserAssignedIdentitiesListResult - Values returned by the List operation. +type UserAssignedIdentitiesListResult struct { + // The url to get the next page of results, if any. + NextLink *string + + // The collection of userAssignedIdentities returned by the listing operation. + Value []*Identity +} + +// UserAssignedIdentityProperties - The properties associated with the user assigned identity. +type UserAssignedIdentityProperties struct { + // Enum to configure regional restrictions on identity assignment, as necessary. + IsolationScope *IsolationScope + + // READ-ONLY; The id of the app associated with the identity. This is a random generated UUID by MSI. + ClientID *string + + // READ-ONLY; The id of the service principal object associated with the created identity. + PrincipalID *string + + // READ-ONLY; The id of the tenant which the identity belongs to. + TenantID *string +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/models_serde.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/models_serde.go new file mode 100644 index 0000000000000..323bfb1031d5e --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/models_serde.go @@ -0,0 +1,552 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import ( + "encoding/json" + "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "reflect" +) + +// MarshalJSON implements the json.Marshaller interface for type FederatedIdentityCredential. +func (f FederatedIdentityCredential) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "id", f.ID) + populate(objectMap, "name", f.Name) + populate(objectMap, "properties", f.Properties) + populate(objectMap, "systemData", f.SystemData) + populate(objectMap, "type", f.Type) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type FederatedIdentityCredential. +func (f *FederatedIdentityCredential) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", f, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "id": + err = unpopulate(val, "ID", &f.ID) + delete(rawMsg, key) + case "name": + err = unpopulate(val, "Name", &f.Name) + delete(rawMsg, key) + case "properties": + err = unpopulate(val, "Properties", &f.Properties) + delete(rawMsg, key) + case "systemData": + err = unpopulate(val, "SystemData", &f.SystemData) + delete(rawMsg, key) + case "type": + err = unpopulate(val, "Type", &f.Type) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", f, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type FederatedIdentityCredentialProperties. +func (f FederatedIdentityCredentialProperties) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "audiences", f.Audiences) + populate(objectMap, "issuer", f.Issuer) + populate(objectMap, "subject", f.Subject) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type FederatedIdentityCredentialProperties. +func (f *FederatedIdentityCredentialProperties) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", f, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "audiences": + err = unpopulate(val, "Audiences", &f.Audiences) + delete(rawMsg, key) + case "issuer": + err = unpopulate(val, "Issuer", &f.Issuer) + delete(rawMsg, key) + case "subject": + err = unpopulate(val, "Subject", &f.Subject) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", f, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type FederatedIdentityCredentialsListResult. +func (f FederatedIdentityCredentialsListResult) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "nextLink", f.NextLink) + populate(objectMap, "value", f.Value) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type FederatedIdentityCredentialsListResult. +func (f *FederatedIdentityCredentialsListResult) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", f, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "nextLink": + err = unpopulate(val, "NextLink", &f.NextLink) + delete(rawMsg, key) + case "value": + err = unpopulate(val, "Value", &f.Value) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", f, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type Identity. +func (i Identity) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "id", i.ID) + populate(objectMap, "location", i.Location) + populate(objectMap, "name", i.Name) + populate(objectMap, "properties", i.Properties) + populate(objectMap, "systemData", i.SystemData) + populate(objectMap, "tags", i.Tags) + populate(objectMap, "type", i.Type) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type Identity. +func (i *Identity) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", i, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "id": + err = unpopulate(val, "ID", &i.ID) + delete(rawMsg, key) + case "location": + err = unpopulate(val, "Location", &i.Location) + delete(rawMsg, key) + case "name": + err = unpopulate(val, "Name", &i.Name) + delete(rawMsg, key) + case "properties": + err = unpopulate(val, "Properties", &i.Properties) + delete(rawMsg, key) + case "systemData": + err = unpopulate(val, "SystemData", &i.SystemData) + delete(rawMsg, key) + case "tags": + err = unpopulate(val, "Tags", &i.Tags) + delete(rawMsg, key) + case "type": + err = unpopulate(val, "Type", &i.Type) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", i, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type IdentityUpdate. +func (i IdentityUpdate) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "id", i.ID) + populate(objectMap, "location", i.Location) + populate(objectMap, "name", i.Name) + populate(objectMap, "properties", i.Properties) + populate(objectMap, "systemData", i.SystemData) + populate(objectMap, "tags", i.Tags) + populate(objectMap, "type", i.Type) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type IdentityUpdate. +func (i *IdentityUpdate) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", i, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "id": + err = unpopulate(val, "ID", &i.ID) + delete(rawMsg, key) + case "location": + err = unpopulate(val, "Location", &i.Location) + delete(rawMsg, key) + case "name": + err = unpopulate(val, "Name", &i.Name) + delete(rawMsg, key) + case "properties": + err = unpopulate(val, "Properties", &i.Properties) + delete(rawMsg, key) + case "systemData": + err = unpopulate(val, "SystemData", &i.SystemData) + delete(rawMsg, key) + case "tags": + err = unpopulate(val, "Tags", &i.Tags) + delete(rawMsg, key) + case "type": + err = unpopulate(val, "Type", &i.Type) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", i, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type Operation. +func (o Operation) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "display", o.Display) + populate(objectMap, "name", o.Name) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type Operation. +func (o *Operation) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", o, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "display": + err = unpopulate(val, "Display", &o.Display) + delete(rawMsg, key) + case "name": + err = unpopulate(val, "Name", &o.Name) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", o, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type OperationDisplay. +func (o OperationDisplay) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "description", o.Description) + populate(objectMap, "operation", o.Operation) + populate(objectMap, "provider", o.Provider) + populate(objectMap, "resource", o.Resource) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type OperationDisplay. +func (o *OperationDisplay) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", o, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "description": + err = unpopulate(val, "Description", &o.Description) + delete(rawMsg, key) + case "operation": + err = unpopulate(val, "Operation", &o.Operation) + delete(rawMsg, key) + case "provider": + err = unpopulate(val, "Provider", &o.Provider) + delete(rawMsg, key) + case "resource": + err = unpopulate(val, "Resource", &o.Resource) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", o, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type OperationListResult. +func (o OperationListResult) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "nextLink", o.NextLink) + populate(objectMap, "value", o.Value) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type OperationListResult. +func (o *OperationListResult) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", o, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "nextLink": + err = unpopulate(val, "NextLink", &o.NextLink) + delete(rawMsg, key) + case "value": + err = unpopulate(val, "Value", &o.Value) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", o, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type SystemAssignedIdentity. +func (s SystemAssignedIdentity) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "id", s.ID) + populate(objectMap, "location", s.Location) + populate(objectMap, "name", s.Name) + populate(objectMap, "properties", s.Properties) + populate(objectMap, "systemData", s.SystemData) + populate(objectMap, "tags", s.Tags) + populate(objectMap, "type", s.Type) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type SystemAssignedIdentity. +func (s *SystemAssignedIdentity) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", s, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "id": + err = unpopulate(val, "ID", &s.ID) + delete(rawMsg, key) + case "location": + err = unpopulate(val, "Location", &s.Location) + delete(rawMsg, key) + case "name": + err = unpopulate(val, "Name", &s.Name) + delete(rawMsg, key) + case "properties": + err = unpopulate(val, "Properties", &s.Properties) + delete(rawMsg, key) + case "systemData": + err = unpopulate(val, "SystemData", &s.SystemData) + delete(rawMsg, key) + case "tags": + err = unpopulate(val, "Tags", &s.Tags) + delete(rawMsg, key) + case "type": + err = unpopulate(val, "Type", &s.Type) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", s, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type SystemAssignedIdentityProperties. +func (s SystemAssignedIdentityProperties) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "clientId", s.ClientID) + populate(objectMap, "clientSecretUrl", s.ClientSecretURL) + populate(objectMap, "principalId", s.PrincipalID) + populate(objectMap, "tenantId", s.TenantID) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type SystemAssignedIdentityProperties. +func (s *SystemAssignedIdentityProperties) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", s, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "clientId": + err = unpopulate(val, "ClientID", &s.ClientID) + delete(rawMsg, key) + case "clientSecretUrl": + err = unpopulate(val, "ClientSecretURL", &s.ClientSecretURL) + delete(rawMsg, key) + case "principalId": + err = unpopulate(val, "PrincipalID", &s.PrincipalID) + delete(rawMsg, key) + case "tenantId": + err = unpopulate(val, "TenantID", &s.TenantID) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", s, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type SystemData. +func (s SystemData) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populateDateTimeRFC3339(objectMap, "createdAt", s.CreatedAt) + populate(objectMap, "createdBy", s.CreatedBy) + populate(objectMap, "createdByType", s.CreatedByType) + populateDateTimeRFC3339(objectMap, "lastModifiedAt", s.LastModifiedAt) + populate(objectMap, "lastModifiedBy", s.LastModifiedBy) + populate(objectMap, "lastModifiedByType", s.LastModifiedByType) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type SystemData. +func (s *SystemData) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", s, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "createdAt": + err = unpopulateDateTimeRFC3339(val, "CreatedAt", &s.CreatedAt) + delete(rawMsg, key) + case "createdBy": + err = unpopulate(val, "CreatedBy", &s.CreatedBy) + delete(rawMsg, key) + case "createdByType": + err = unpopulate(val, "CreatedByType", &s.CreatedByType) + delete(rawMsg, key) + case "lastModifiedAt": + err = unpopulateDateTimeRFC3339(val, "LastModifiedAt", &s.LastModifiedAt) + delete(rawMsg, key) + case "lastModifiedBy": + err = unpopulate(val, "LastModifiedBy", &s.LastModifiedBy) + delete(rawMsg, key) + case "lastModifiedByType": + err = unpopulate(val, "LastModifiedByType", &s.LastModifiedByType) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", s, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type UserAssignedIdentitiesListResult. +func (u UserAssignedIdentitiesListResult) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "nextLink", u.NextLink) + populate(objectMap, "value", u.Value) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type UserAssignedIdentitiesListResult. +func (u *UserAssignedIdentitiesListResult) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", u, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "nextLink": + err = unpopulate(val, "NextLink", &u.NextLink) + delete(rawMsg, key) + case "value": + err = unpopulate(val, "Value", &u.Value) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", u, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type UserAssignedIdentityProperties. +func (u UserAssignedIdentityProperties) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "clientId", u.ClientID) + populate(objectMap, "isolationScope", u.IsolationScope) + populate(objectMap, "principalId", u.PrincipalID) + populate(objectMap, "tenantId", u.TenantID) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type UserAssignedIdentityProperties. +func (u *UserAssignedIdentityProperties) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", u, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "clientId": + err = unpopulate(val, "ClientID", &u.ClientID) + delete(rawMsg, key) + case "isolationScope": + err = unpopulate(val, "IsolationScope", &u.IsolationScope) + delete(rawMsg, key) + case "principalId": + err = unpopulate(val, "PrincipalID", &u.PrincipalID) + delete(rawMsg, key) + case "tenantId": + err = unpopulate(val, "TenantID", &u.TenantID) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", u, err) + } + } + return nil +} + +func populate(m map[string]any, k string, v any) { + if v == nil { + return + } else if azcore.IsNullValue(v) { + m[k] = nil + } else if !reflect.ValueOf(v).IsNil() { + m[k] = v + } +} + +func unpopulate(data json.RawMessage, fn string, v any) error { + if data == nil || string(data) == "null" { + return nil + } + if err := json.Unmarshal(data, v); err != nil { + return fmt.Errorf("struct field %s: %v", fn, err) + } + return nil +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/operations_client.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/operations_client.go new file mode 100644 index 0000000000000..c16b57ca2fad8 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/operations_client.go @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import ( + "context" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "net/http" +) + +// OperationsClient contains the methods for the Operations group. +// Don't use this type directly, use NewOperationsClient() instead. +type OperationsClient struct { + internal *arm.Client +} + +// NewOperationsClient creates a new instance of OperationsClient with the specified values. +// - credential - used to authorize requests. Usually a credential from azidentity. +// - options - pass nil to accept the default values. +func NewOperationsClient(credential azcore.TokenCredential, options *arm.ClientOptions) (*OperationsClient, error) { + cl, err := arm.NewClient(moduleName, moduleVersion, credential, options) + if err != nil { + return nil, err + } + client := &OperationsClient{ + internal: cl, + } + return client, nil +} + +// NewListPager - Lists available operations for the Microsoft.ManagedIdentity provider +// +// Generated from API version 2024-11-30 +// - options - OperationsClientListOptions contains the optional parameters for the OperationsClient.NewListPager method. +func (client *OperationsClient) NewListPager(options *OperationsClientListOptions) *runtime.Pager[OperationsClientListResponse] { + return runtime.NewPager(runtime.PagingHandler[OperationsClientListResponse]{ + More: func(page OperationsClientListResponse) bool { + return page.NextLink != nil && len(*page.NextLink) > 0 + }, + Fetcher: func(ctx context.Context, page *OperationsClientListResponse) (OperationsClientListResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "OperationsClient.NewListPager") + nextLink := "" + if page != nil { + nextLink = *page.NextLink + } + resp, err := runtime.FetcherForNextLink(ctx, client.internal.Pipeline(), nextLink, func(ctx context.Context) (*policy.Request, error) { + return client.listCreateRequest(ctx, options) + }, nil) + if err != nil { + return OperationsClientListResponse{}, err + } + return client.listHandleResponse(resp) + }, + Tracer: client.internal.Tracer(), + }) +} + +// listCreateRequest creates the List request. +func (client *OperationsClient) listCreateRequest(ctx context.Context, _ *OperationsClientListOptions) (*policy.Request, error) { + urlPath := "/providers/Microsoft.ManagedIdentity/operations" + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// listHandleResponse handles the List response. +func (client *OperationsClient) listHandleResponse(resp *http.Response) (OperationsClientListResponse, error) { + result := OperationsClientListResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.OperationListResult); err != nil { + return OperationsClientListResponse{}, err + } + return result, nil +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/options.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/options.go new file mode 100644 index 0000000000000..6b67faa9a8e10 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/options.go @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +// FederatedIdentityCredentialsClientCreateOrUpdateOptions contains the optional parameters for the FederatedIdentityCredentialsClient.CreateOrUpdate +// method. +type FederatedIdentityCredentialsClientCreateOrUpdateOptions struct { + // placeholder for future optional parameters +} + +// FederatedIdentityCredentialsClientDeleteOptions contains the optional parameters for the FederatedIdentityCredentialsClient.Delete +// method. +type FederatedIdentityCredentialsClientDeleteOptions struct { + // placeholder for future optional parameters +} + +// FederatedIdentityCredentialsClientGetOptions contains the optional parameters for the FederatedIdentityCredentialsClient.Get +// method. +type FederatedIdentityCredentialsClientGetOptions struct { + // placeholder for future optional parameters +} + +// FederatedIdentityCredentialsClientListOptions contains the optional parameters for the FederatedIdentityCredentialsClient.NewListPager +// method. +type FederatedIdentityCredentialsClientListOptions struct { + // A skip token is used to continue retrieving items after an operation returns a partial result. If a previous response contains + // a nextLink element, the value of the nextLink element will include a + // skipToken parameter that specifies a starting point to use for subsequent calls. + Skiptoken *string + + // Number of records to return. + Top *int32 +} + +// OperationsClientListOptions contains the optional parameters for the OperationsClient.NewListPager method. +type OperationsClientListOptions struct { + // placeholder for future optional parameters +} + +// SystemAssignedIdentitiesClientGetByScopeOptions contains the optional parameters for the SystemAssignedIdentitiesClient.GetByScope +// method. +type SystemAssignedIdentitiesClientGetByScopeOptions struct { + // placeholder for future optional parameters +} + +// UserAssignedIdentitiesClientCreateOrUpdateOptions contains the optional parameters for the UserAssignedIdentitiesClient.CreateOrUpdate +// method. +type UserAssignedIdentitiesClientCreateOrUpdateOptions struct { + // placeholder for future optional parameters +} + +// UserAssignedIdentitiesClientDeleteOptions contains the optional parameters for the UserAssignedIdentitiesClient.Delete +// method. +type UserAssignedIdentitiesClientDeleteOptions struct { + // placeholder for future optional parameters +} + +// UserAssignedIdentitiesClientGetOptions contains the optional parameters for the UserAssignedIdentitiesClient.Get method. +type UserAssignedIdentitiesClientGetOptions struct { + // placeholder for future optional parameters +} + +// UserAssignedIdentitiesClientListByResourceGroupOptions contains the optional parameters for the UserAssignedIdentitiesClient.NewListByResourceGroupPager +// method. +type UserAssignedIdentitiesClientListByResourceGroupOptions struct { + // placeholder for future optional parameters +} + +// UserAssignedIdentitiesClientListBySubscriptionOptions contains the optional parameters for the UserAssignedIdentitiesClient.NewListBySubscriptionPager +// method. +type UserAssignedIdentitiesClientListBySubscriptionOptions struct { + // placeholder for future optional parameters +} + +// UserAssignedIdentitiesClientUpdateOptions contains the optional parameters for the UserAssignedIdentitiesClient.Update +// method. +type UserAssignedIdentitiesClientUpdateOptions struct { + // placeholder for future optional parameters +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/responses.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/responses.go new file mode 100644 index 0000000000000..420661fd25fec --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/responses.go @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +// FederatedIdentityCredentialsClientCreateOrUpdateResponse contains the response from method FederatedIdentityCredentialsClient.CreateOrUpdate. +type FederatedIdentityCredentialsClientCreateOrUpdateResponse struct { + // Describes a federated identity credential. + FederatedIdentityCredential +} + +// FederatedIdentityCredentialsClientDeleteResponse contains the response from method FederatedIdentityCredentialsClient.Delete. +type FederatedIdentityCredentialsClientDeleteResponse struct { + // placeholder for future response values +} + +// FederatedIdentityCredentialsClientGetResponse contains the response from method FederatedIdentityCredentialsClient.Get. +type FederatedIdentityCredentialsClientGetResponse struct { + // Describes a federated identity credential. + FederatedIdentityCredential +} + +// FederatedIdentityCredentialsClientListResponse contains the response from method FederatedIdentityCredentialsClient.NewListPager. +type FederatedIdentityCredentialsClientListResponse struct { + // Values returned by the List operation for federated identity credentials. + FederatedIdentityCredentialsListResult +} + +// OperationsClientListResponse contains the response from method OperationsClient.NewListPager. +type OperationsClientListResponse struct { + // A list of operations supported by Microsoft.ManagedIdentity Resource Provider. + OperationListResult +} + +// SystemAssignedIdentitiesClientGetByScopeResponse contains the response from method SystemAssignedIdentitiesClient.GetByScope. +type SystemAssignedIdentitiesClientGetByScopeResponse struct { + // Describes a system assigned identity resource. + SystemAssignedIdentity +} + +// UserAssignedIdentitiesClientCreateOrUpdateResponse contains the response from method UserAssignedIdentitiesClient.CreateOrUpdate. +type UserAssignedIdentitiesClientCreateOrUpdateResponse struct { + // Describes an identity resource. + Identity +} + +// UserAssignedIdentitiesClientDeleteResponse contains the response from method UserAssignedIdentitiesClient.Delete. +type UserAssignedIdentitiesClientDeleteResponse struct { + // placeholder for future response values +} + +// UserAssignedIdentitiesClientGetResponse contains the response from method UserAssignedIdentitiesClient.Get. +type UserAssignedIdentitiesClientGetResponse struct { + // Describes an identity resource. + Identity +} + +// UserAssignedIdentitiesClientListByResourceGroupResponse contains the response from method UserAssignedIdentitiesClient.NewListByResourceGroupPager. +type UserAssignedIdentitiesClientListByResourceGroupResponse struct { + // Values returned by the List operation. + UserAssignedIdentitiesListResult +} + +// UserAssignedIdentitiesClientListBySubscriptionResponse contains the response from method UserAssignedIdentitiesClient.NewListBySubscriptionPager. +type UserAssignedIdentitiesClientListBySubscriptionResponse struct { + // Values returned by the List operation. + UserAssignedIdentitiesListResult +} + +// UserAssignedIdentitiesClientUpdateResponse contains the response from method UserAssignedIdentitiesClient.Update. +type UserAssignedIdentitiesClientUpdateResponse struct { + // Describes an identity resource. + Identity +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/systemassignedidentities_client.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/systemassignedidentities_client.go new file mode 100644 index 0000000000000..f87357240f5f9 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/systemassignedidentities_client.go @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import ( + "context" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "net/http" + "strings" +) + +// SystemAssignedIdentitiesClient contains the methods for the SystemAssignedIdentities group. +// Don't use this type directly, use NewSystemAssignedIdentitiesClient() instead. +type SystemAssignedIdentitiesClient struct { + internal *arm.Client +} + +// NewSystemAssignedIdentitiesClient creates a new instance of SystemAssignedIdentitiesClient with the specified values. +// - credential - used to authorize requests. Usually a credential from azidentity. +// - options - pass nil to accept the default values. +func NewSystemAssignedIdentitiesClient(credential azcore.TokenCredential, options *arm.ClientOptions) (*SystemAssignedIdentitiesClient, error) { + cl, err := arm.NewClient(moduleName, moduleVersion, credential, options) + if err != nil { + return nil, err + } + client := &SystemAssignedIdentitiesClient{ + internal: cl, + } + return client, nil +} + +// GetByScope - Gets the systemAssignedIdentity available under the specified RP scope. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - scope - The resource provider scope of the resource. Parent resource being extended by Managed Identities. +// - options - SystemAssignedIdentitiesClientGetByScopeOptions contains the optional parameters for the SystemAssignedIdentitiesClient.GetByScope +// method. +func (client *SystemAssignedIdentitiesClient) GetByScope(ctx context.Context, scope string, options *SystemAssignedIdentitiesClientGetByScopeOptions) (SystemAssignedIdentitiesClientGetByScopeResponse, error) { + var err error + const operationName = "SystemAssignedIdentitiesClient.GetByScope" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.getByScopeCreateRequest(ctx, scope, options) + if err != nil { + return SystemAssignedIdentitiesClientGetByScopeResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return SystemAssignedIdentitiesClientGetByScopeResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = runtime.NewResponseError(httpResp) + return SystemAssignedIdentitiesClientGetByScopeResponse{}, err + } + resp, err := client.getByScopeHandleResponse(httpResp) + return resp, err +} + +// getByScopeCreateRequest creates the GetByScope request. +func (client *SystemAssignedIdentitiesClient) getByScopeCreateRequest(ctx context.Context, scope string, _ *SystemAssignedIdentitiesClientGetByScopeOptions) (*policy.Request, error) { + urlPath := "/{scope}/providers/Microsoft.ManagedIdentity/identities/default" + urlPath = strings.ReplaceAll(urlPath, "{scope}", scope) + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// getByScopeHandleResponse handles the GetByScope response. +func (client *SystemAssignedIdentitiesClient) getByScopeHandleResponse(resp *http.Response) (SystemAssignedIdentitiesClientGetByScopeResponse, error) { + result := SystemAssignedIdentitiesClientGetByScopeResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.SystemAssignedIdentity); err != nil { + return SystemAssignedIdentitiesClientGetByScopeResponse{}, err + } + return result, nil +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/time_rfc3339.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/time_rfc3339.go new file mode 100644 index 0000000000000..141269078a878 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/time_rfc3339.go @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import ( + "encoding/json" + "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "reflect" + "regexp" + "strings" + "time" +) + +// Azure reports time in UTC but it doesn't include the 'Z' time zone suffix in some cases. +var tzOffsetRegex = regexp.MustCompile(`(?:Z|z|\+|-)(?:\d+:\d+)*"*$`) + +const ( + utcDateTime = "2006-01-02T15:04:05.999999999" + utcDateTimeJSON = `"` + utcDateTime + `"` + utcDateTimeNoT = "2006-01-02 15:04:05.999999999" + utcDateTimeJSONNoT = `"` + utcDateTimeNoT + `"` + dateTimeNoT = `2006-01-02 15:04:05.999999999Z07:00` + dateTimeJSON = `"` + time.RFC3339Nano + `"` + dateTimeJSONNoT = `"` + dateTimeNoT + `"` +) + +type dateTimeRFC3339 time.Time + +func (t dateTimeRFC3339) MarshalJSON() ([]byte, error) { + tt := time.Time(t) + return tt.MarshalJSON() +} + +func (t dateTimeRFC3339) MarshalText() ([]byte, error) { + tt := time.Time(t) + return tt.MarshalText() +} + +func (t *dateTimeRFC3339) UnmarshalJSON(data []byte) error { + tzOffset := tzOffsetRegex.Match(data) + hasT := strings.Contains(string(data), "T") || strings.Contains(string(data), "t") + var layout string + if tzOffset && hasT { + layout = dateTimeJSON + } else if tzOffset { + layout = dateTimeJSONNoT + } else if hasT { + layout = utcDateTimeJSON + } else { + layout = utcDateTimeJSONNoT + } + return t.Parse(layout, string(data)) +} + +func (t *dateTimeRFC3339) UnmarshalText(data []byte) error { + if len(data) == 0 { + return nil + } + tzOffset := tzOffsetRegex.Match(data) + hasT := strings.Contains(string(data), "T") || strings.Contains(string(data), "t") + var layout string + if tzOffset && hasT { + layout = time.RFC3339Nano + } else if tzOffset { + layout = dateTimeNoT + } else if hasT { + layout = utcDateTime + } else { + layout = utcDateTimeNoT + } + return t.Parse(layout, string(data)) +} + +func (t *dateTimeRFC3339) Parse(layout, value string) error { + p, err := time.Parse(layout, strings.ToUpper(value)) + *t = dateTimeRFC3339(p) + return err +} + +func (t dateTimeRFC3339) String() string { + return time.Time(t).Format(time.RFC3339Nano) +} + +func populateDateTimeRFC3339(m map[string]any, k string, t *time.Time) { + if t == nil { + return + } else if azcore.IsNullValue(t) { + m[k] = nil + return + } else if reflect.ValueOf(t).IsNil() { + return + } + m[k] = (*dateTimeRFC3339)(t) +} + +func unpopulateDateTimeRFC3339(data json.RawMessage, fn string, t **time.Time) error { + if data == nil || string(data) == "null" { + return nil + } + var aux dateTimeRFC3339 + if err := json.Unmarshal(data, &aux); err != nil { + return fmt.Errorf("struct field %s: %v", fn, err) + } + *t = (*time.Time)(&aux) + return nil +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/userassignedidentities_client.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/userassignedidentities_client.go new file mode 100644 index 0000000000000..454e4c0c35f22 --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi/userassignedidentities_client.go @@ -0,0 +1,414 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package armmsi + +import ( + "context" + "errors" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "net/http" + "net/url" + "strings" +) + +// UserAssignedIdentitiesClient contains the methods for the UserAssignedIdentities group. +// Don't use this type directly, use NewUserAssignedIdentitiesClient() instead. +type UserAssignedIdentitiesClient struct { + internal *arm.Client + subscriptionID string +} + +// NewUserAssignedIdentitiesClient creates a new instance of UserAssignedIdentitiesClient with the specified values. +// - subscriptionID - The Id of the Subscription to which the identity belongs. +// - credential - used to authorize requests. Usually a credential from azidentity. +// - options - pass nil to accept the default values. +func NewUserAssignedIdentitiesClient(subscriptionID string, credential azcore.TokenCredential, options *arm.ClientOptions) (*UserAssignedIdentitiesClient, error) { + cl, err := arm.NewClient(moduleName, moduleVersion, credential, options) + if err != nil { + return nil, err + } + client := &UserAssignedIdentitiesClient{ + subscriptionID: subscriptionID, + internal: cl, + } + return client, nil +} + +// CreateOrUpdate - Create or update an identity in the specified subscription and resource group. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the Resource Group to which the identity belongs. +// - resourceName - The name of the identity resource. +// - parameters - Parameters to create or update the identity +// - options - UserAssignedIdentitiesClientCreateOrUpdateOptions contains the optional parameters for the UserAssignedIdentitiesClient.CreateOrUpdate +// method. +func (client *UserAssignedIdentitiesClient) CreateOrUpdate(ctx context.Context, resourceGroupName string, resourceName string, parameters Identity, options *UserAssignedIdentitiesClientCreateOrUpdateOptions) (UserAssignedIdentitiesClientCreateOrUpdateResponse, error) { + var err error + const operationName = "UserAssignedIdentitiesClient.CreateOrUpdate" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.createOrUpdateCreateRequest(ctx, resourceGroupName, resourceName, parameters, options) + if err != nil { + return UserAssignedIdentitiesClientCreateOrUpdateResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return UserAssignedIdentitiesClientCreateOrUpdateResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK, http.StatusCreated) { + err = runtime.NewResponseError(httpResp) + return UserAssignedIdentitiesClientCreateOrUpdateResponse{}, err + } + resp, err := client.createOrUpdateHandleResponse(httpResp) + return resp, err +} + +// createOrUpdateCreateRequest creates the CreateOrUpdate request. +func (client *UserAssignedIdentitiesClient) createOrUpdateCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, parameters Identity, _ *UserAssignedIdentitiesClientCreateOrUpdateOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + req, err := runtime.NewRequest(ctx, http.MethodPut, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + if err := runtime.MarshalAsJSON(req, parameters); err != nil { + return nil, err + } + return req, nil +} + +// createOrUpdateHandleResponse handles the CreateOrUpdate response. +func (client *UserAssignedIdentitiesClient) createOrUpdateHandleResponse(resp *http.Response) (UserAssignedIdentitiesClientCreateOrUpdateResponse, error) { + result := UserAssignedIdentitiesClientCreateOrUpdateResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.Identity); err != nil { + return UserAssignedIdentitiesClientCreateOrUpdateResponse{}, err + } + return result, nil +} + +// Delete - Deletes the identity. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the Resource Group to which the identity belongs. +// - resourceName - The name of the identity resource. +// - options - UserAssignedIdentitiesClientDeleteOptions contains the optional parameters for the UserAssignedIdentitiesClient.Delete +// method. +func (client *UserAssignedIdentitiesClient) Delete(ctx context.Context, resourceGroupName string, resourceName string, options *UserAssignedIdentitiesClientDeleteOptions) (UserAssignedIdentitiesClientDeleteResponse, error) { + var err error + const operationName = "UserAssignedIdentitiesClient.Delete" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.deleteCreateRequest(ctx, resourceGroupName, resourceName, options) + if err != nil { + return UserAssignedIdentitiesClientDeleteResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return UserAssignedIdentitiesClientDeleteResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK, http.StatusNoContent) { + err = runtime.NewResponseError(httpResp) + return UserAssignedIdentitiesClientDeleteResponse{}, err + } + return UserAssignedIdentitiesClientDeleteResponse{}, nil +} + +// deleteCreateRequest creates the Delete request. +func (client *UserAssignedIdentitiesClient) deleteCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, _ *UserAssignedIdentitiesClientDeleteOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + req, err := runtime.NewRequest(ctx, http.MethodDelete, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// Get - Gets the identity. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the Resource Group to which the identity belongs. +// - resourceName - The name of the identity resource. +// - options - UserAssignedIdentitiesClientGetOptions contains the optional parameters for the UserAssignedIdentitiesClient.Get +// method. +func (client *UserAssignedIdentitiesClient) Get(ctx context.Context, resourceGroupName string, resourceName string, options *UserAssignedIdentitiesClientGetOptions) (UserAssignedIdentitiesClientGetResponse, error) { + var err error + const operationName = "UserAssignedIdentitiesClient.Get" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.getCreateRequest(ctx, resourceGroupName, resourceName, options) + if err != nil { + return UserAssignedIdentitiesClientGetResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return UserAssignedIdentitiesClientGetResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = runtime.NewResponseError(httpResp) + return UserAssignedIdentitiesClientGetResponse{}, err + } + resp, err := client.getHandleResponse(httpResp) + return resp, err +} + +// getCreateRequest creates the Get request. +func (client *UserAssignedIdentitiesClient) getCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, _ *UserAssignedIdentitiesClientGetOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// getHandleResponse handles the Get response. +func (client *UserAssignedIdentitiesClient) getHandleResponse(resp *http.Response) (UserAssignedIdentitiesClientGetResponse, error) { + result := UserAssignedIdentitiesClientGetResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.Identity); err != nil { + return UserAssignedIdentitiesClientGetResponse{}, err + } + return result, nil +} + +// NewListByResourceGroupPager - Lists all the userAssignedIdentities available under the specified ResourceGroup. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the Resource Group to which the identity belongs. +// - options - UserAssignedIdentitiesClientListByResourceGroupOptions contains the optional parameters for the UserAssignedIdentitiesClient.NewListByResourceGroupPager +// method. +func (client *UserAssignedIdentitiesClient) NewListByResourceGroupPager(resourceGroupName string, options *UserAssignedIdentitiesClientListByResourceGroupOptions) *runtime.Pager[UserAssignedIdentitiesClientListByResourceGroupResponse] { + return runtime.NewPager(runtime.PagingHandler[UserAssignedIdentitiesClientListByResourceGroupResponse]{ + More: func(page UserAssignedIdentitiesClientListByResourceGroupResponse) bool { + return page.NextLink != nil && len(*page.NextLink) > 0 + }, + Fetcher: func(ctx context.Context, page *UserAssignedIdentitiesClientListByResourceGroupResponse) (UserAssignedIdentitiesClientListByResourceGroupResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "UserAssignedIdentitiesClient.NewListByResourceGroupPager") + nextLink := "" + if page != nil { + nextLink = *page.NextLink + } + resp, err := runtime.FetcherForNextLink(ctx, client.internal.Pipeline(), nextLink, func(ctx context.Context) (*policy.Request, error) { + return client.listByResourceGroupCreateRequest(ctx, resourceGroupName, options) + }, nil) + if err != nil { + return UserAssignedIdentitiesClientListByResourceGroupResponse{}, err + } + return client.listByResourceGroupHandleResponse(resp) + }, + Tracer: client.internal.Tracer(), + }) +} + +// listByResourceGroupCreateRequest creates the ListByResourceGroup request. +func (client *UserAssignedIdentitiesClient) listByResourceGroupCreateRequest(ctx context.Context, resourceGroupName string, _ *UserAssignedIdentitiesClientListByResourceGroupOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// listByResourceGroupHandleResponse handles the ListByResourceGroup response. +func (client *UserAssignedIdentitiesClient) listByResourceGroupHandleResponse(resp *http.Response) (UserAssignedIdentitiesClientListByResourceGroupResponse, error) { + result := UserAssignedIdentitiesClientListByResourceGroupResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.UserAssignedIdentitiesListResult); err != nil { + return UserAssignedIdentitiesClientListByResourceGroupResponse{}, err + } + return result, nil +} + +// NewListBySubscriptionPager - Lists all the userAssignedIdentities available under the specified subscription. +// +// Generated from API version 2024-11-30 +// - options - UserAssignedIdentitiesClientListBySubscriptionOptions contains the optional parameters for the UserAssignedIdentitiesClient.NewListBySubscriptionPager +// method. +func (client *UserAssignedIdentitiesClient) NewListBySubscriptionPager(options *UserAssignedIdentitiesClientListBySubscriptionOptions) *runtime.Pager[UserAssignedIdentitiesClientListBySubscriptionResponse] { + return runtime.NewPager(runtime.PagingHandler[UserAssignedIdentitiesClientListBySubscriptionResponse]{ + More: func(page UserAssignedIdentitiesClientListBySubscriptionResponse) bool { + return page.NextLink != nil && len(*page.NextLink) > 0 + }, + Fetcher: func(ctx context.Context, page *UserAssignedIdentitiesClientListBySubscriptionResponse) (UserAssignedIdentitiesClientListBySubscriptionResponse, error) { + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, "UserAssignedIdentitiesClient.NewListBySubscriptionPager") + nextLink := "" + if page != nil { + nextLink = *page.NextLink + } + resp, err := runtime.FetcherForNextLink(ctx, client.internal.Pipeline(), nextLink, func(ctx context.Context) (*policy.Request, error) { + return client.listBySubscriptionCreateRequest(ctx, options) + }, nil) + if err != nil { + return UserAssignedIdentitiesClientListBySubscriptionResponse{}, err + } + return client.listBySubscriptionHandleResponse(resp) + }, + Tracer: client.internal.Tracer(), + }) +} + +// listBySubscriptionCreateRequest creates the ListBySubscription request. +func (client *UserAssignedIdentitiesClient) listBySubscriptionCreateRequest(ctx context.Context, _ *UserAssignedIdentitiesClientListBySubscriptionOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/providers/Microsoft.ManagedIdentity/userAssignedIdentities" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + req, err := runtime.NewRequest(ctx, http.MethodGet, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + return req, nil +} + +// listBySubscriptionHandleResponse handles the ListBySubscription response. +func (client *UserAssignedIdentitiesClient) listBySubscriptionHandleResponse(resp *http.Response) (UserAssignedIdentitiesClientListBySubscriptionResponse, error) { + result := UserAssignedIdentitiesClientListBySubscriptionResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.UserAssignedIdentitiesListResult); err != nil { + return UserAssignedIdentitiesClientListBySubscriptionResponse{}, err + } + return result, nil +} + +// Update - Update an identity in the specified subscription and resource group. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2024-11-30 +// - resourceGroupName - The name of the Resource Group to which the identity belongs. +// - resourceName - The name of the identity resource. +// - parameters - Parameters to update the identity +// - options - UserAssignedIdentitiesClientUpdateOptions contains the optional parameters for the UserAssignedIdentitiesClient.Update +// method. +func (client *UserAssignedIdentitiesClient) Update(ctx context.Context, resourceGroupName string, resourceName string, parameters IdentityUpdate, options *UserAssignedIdentitiesClientUpdateOptions) (UserAssignedIdentitiesClientUpdateResponse, error) { + var err error + const operationName = "UserAssignedIdentitiesClient.Update" + ctx = context.WithValue(ctx, runtime.CtxAPINameKey{}, operationName) + ctx, endSpan := runtime.StartSpan(ctx, operationName, client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.updateCreateRequest(ctx, resourceGroupName, resourceName, parameters, options) + if err != nil { + return UserAssignedIdentitiesClientUpdateResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return UserAssignedIdentitiesClientUpdateResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = runtime.NewResponseError(httpResp) + return UserAssignedIdentitiesClientUpdateResponse{}, err + } + resp, err := client.updateHandleResponse(httpResp) + return resp, err +} + +// updateCreateRequest creates the Update request. +func (client *UserAssignedIdentitiesClient) updateCreateRequest(ctx context.Context, resourceGroupName string, resourceName string, parameters IdentityUpdate, _ *UserAssignedIdentitiesClientUpdateOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{resourceName}" + if client.subscriptionID == "" { + return nil, errors.New("parameter client.subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(client.subscriptionID)) + if resourceGroupName == "" { + return nil, errors.New("parameter resourceGroupName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceGroupName}", url.PathEscape(resourceGroupName)) + if resourceName == "" { + return nil, errors.New("parameter resourceName cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{resourceName}", url.PathEscape(resourceName)) + req, err := runtime.NewRequest(ctx, http.MethodPatch, runtime.JoinPaths(client.internal.Endpoint(), urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + reqQP.Set("api-version", "2024-11-30") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + if err := runtime.MarshalAsJSON(req, parameters); err != nil { + return nil, err + } + return req, nil +} + +// updateHandleResponse handles the Update response. +func (client *UserAssignedIdentitiesClient) updateHandleResponse(resp *http.Response) (UserAssignedIdentitiesClientUpdateResponse, error) { + result := UserAssignedIdentitiesClientUpdateResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.Identity); err != nil { + return UserAssignedIdentitiesClientUpdateResponse{}, err + } + return result, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 69520e25868c6..8337b3c950efc 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -67,6 +67,9 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/authorization/armauthoriza # github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 ## explicit; go 1.18 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute +# github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.3.0 +## explicit; go 1.23.0 +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi # github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 ## explicit; go 1.18 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork