diff --git a/pkg/model/gcemodel/BUILD.bazel b/pkg/model/gcemodel/BUILD.bazel index ed8a843b7a..24d7dc0c32 100644 --- a/pkg/model/gcemodel/BUILD.bazel +++ b/pkg/model/gcemodel/BUILD.bazel @@ -10,6 +10,7 @@ go_library( "external_access.go", "firewall.go", "network.go", + "service_accounts.go", "storageacl.go", ], importpath = "k8s.io/kops/pkg/model/gcemodel", diff --git a/pkg/model/gcemodel/autoscalinggroup.go b/pkg/model/gcemodel/autoscalinggroup.go index 71967850d6..7696ab8650 100644 --- a/pkg/model/gcemodel/autoscalinggroup.go +++ b/pkg/model/gcemodel/autoscalinggroup.go @@ -164,9 +164,8 @@ func (b *AutoscalingGroupModelBuilder) buildInstanceTemplate(c *fi.ModelBuilderC klog.Warning("Use a pre-created Service Account with the flag: --gce-service-account=account@projectname.iam.gserviceaccount.com") b.Cluster.Spec.CloudConfig.GCEServiceAccount = "default" } + t.ServiceAccounts = append(t.ServiceAccounts, b.LinkToServiceAccount(ig)) - klog.Infof("gsa: %v", b.Cluster.Spec.CloudConfig.GCEServiceAccount) - t.ServiceAccounts = []string{b.Cluster.Spec.CloudConfig.GCEServiceAccount} //labels, err := b.CloudTagsForInstanceGroup(ig) //if err != nil { // return fmt.Errorf("error building cloud tags: %v", err) diff --git a/pkg/model/gcemodel/context.go b/pkg/model/gcemodel/context.go index bc6f0123fd..d2f9b9374f 100644 --- a/pkg/model/gcemodel/context.go +++ b/pkg/model/gcemodel/context.go @@ -17,8 +17,10 @@ limitations under the License. package gcemodel import ( + "k8s.io/klog/v2" "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/model" + "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/gce" "k8s.io/kops/upup/pkg/fi/cloudup/gcetasks" ) @@ -98,3 +100,14 @@ func (c *GCEModelContext) NetworkingIsIPAlias() bool { func (c *GCEModelContext) NetworkingIsGCERoutes() bool { return c.Cluster.Spec.Networking != nil && c.Cluster.Spec.Networking.Kubenet != nil } + +// LinkToServiceAccount returns a link to the GCE ServiceAccount object for VMs in the given role +func (c *GCEModelContext) LinkToServiceAccount(ig *kops.InstanceGroup) *gcetasks.ServiceAccount { + // This is a legacy setting because the nodes & control-plane run under the same serviceaccount + klog.Warningf("using legacy spec.cloudConfig.gceServiceAccount=%q setting", c.Cluster.Spec.CloudConfig.GCEServiceAccount) + return &gcetasks.ServiceAccount{ + Name: s("shared"), + Email: &c.Cluster.Spec.CloudConfig.GCEServiceAccount, + Shared: fi.Bool(true), + } +} diff --git a/pkg/model/gcemodel/service_accounts.go b/pkg/model/gcemodel/service_accounts.go new file mode 100644 index 0000000000..29933c8e25 --- /dev/null +++ b/pkg/model/gcemodel/service_accounts.go @@ -0,0 +1,47 @@ +/* +Copyright 2021 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 gcemodel + +import ( + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/upup/pkg/fi/cloudup/gcetasks" +) + +// ServiceAccountsBuilder configures service accounts and grants project permissions +type ServiceAccountsBuilder struct { + *GCEModelContext + + Lifecycle fi.Lifecycle +} + +var _ fi.ModelBuilder = &ServiceAccountsBuilder{} + +func (b *ServiceAccountsBuilder) Build(c *fi.ModelBuilderContext) error { + if b.Cluster.Spec.CloudConfig.GCEServiceAccount != "" { + serviceAccount := &gcetasks.ServiceAccount{ + Name: s("shared"), + Email: &b.Cluster.Spec.CloudConfig.GCEServiceAccount, + Shared: fi.Bool(true), + Lifecycle: b.Lifecycle, + } + c.AddTask(serviceAccount) + + return nil + } + + return nil +} diff --git a/upup/pkg/fi/cloudup/apply_cluster.go b/upup/pkg/fi/cloudup/apply_cluster.go index 97ff57dd15..6cf580c971 100644 --- a/upup/pkg/fi/cloudup/apply_cluster.go +++ b/upup/pkg/fi/cloudup/apply_cluster.go @@ -606,6 +606,7 @@ func (c *ApplyClusterCmd) Run(ctx context.Context) error { &gcemodel.NetworkModelBuilder{GCEModelContext: gceModelContext, Lifecycle: networkLifecycle}, &gcemodel.StorageAclBuilder{GCEModelContext: gceModelContext, Cloud: cloud.(gce.GCECloud), Lifecycle: storageACLLifecycle}, &gcemodel.AutoscalingGroupModelBuilder{GCEModelContext: gceModelContext, BootstrapScriptBuilder: bootstrapScriptBuilder, Lifecycle: clusterLifecycle}, + &gcemodel.ServiceAccountsBuilder{GCEModelContext: gceModelContext, Lifecycle: clusterLifecycle}, ) case kops.CloudProviderAzure: azureModelContext := &azuremodel.AzureModelContext{ diff --git a/upup/pkg/fi/cloudup/gcetasks/instance.go b/upup/pkg/fi/cloudup/gcetasks/instance.go index a22846befa..49c5b30f03 100644 --- a/upup/pkg/fi/cloudup/gcetasks/instance.go +++ b/upup/pkg/fi/cloudup/gcetasks/instance.go @@ -41,7 +41,7 @@ type Instance struct { Preemptible *bool Image *string Disks map[string]*Disk - ServiceAccount *string + ServiceAccount *ServiceAccount CanIPForward *bool IPAddress *Address @@ -263,7 +263,7 @@ func (e *Instance) mapToGCE(project string, ipAddressResolver func(*Address) (*s scopes = append(scopes, s) } serviceAccounts = append(serviceAccounts, &compute.ServiceAccount{ - Email: fi.StringValue(e.ServiceAccount), + Email: fi.StringValue(e.ServiceAccount.Email), Scopes: scopes, }) } diff --git a/upup/pkg/fi/cloudup/gcetasks/instancetemplate.go b/upup/pkg/fi/cloudup/gcetasks/instancetemplate.go index 13acb46129..32e8572722 100644 --- a/upup/pkg/fi/cloudup/gcetasks/instancetemplate.go +++ b/upup/pkg/fi/cloudup/gcetasks/instancetemplate.go @@ -65,7 +65,7 @@ type InstanceTemplate struct { AliasIPRanges map[string]string Scopes []string - ServiceAccounts []string + ServiceAccounts []*ServiceAccount Metadata map[string]fi.Resource MachineType *string @@ -164,7 +164,9 @@ func (e *InstanceTemplate) Find(c *fi.Context) (*InstanceTemplate, error) { for _, scope := range serviceAccount.Scopes { actual.Scopes = append(actual.Scopes, scopeToShortForm(scope)) } - actual.ServiceAccounts = append(actual.ServiceAccounts, serviceAccount.Email) + actual.ServiceAccounts = append(actual.ServiceAccounts, &ServiceAccount{ + Email: &serviceAccount.Email, + }) } // When we deal with additional disks (local disks), we'll need to map them like this... @@ -306,25 +308,14 @@ func (e *InstanceTemplate) mapToGCE(project string, region string) (*compute.Ins scopes = append(scopes, s) } } - serviceAccounts := []*compute.ServiceAccount{ - { - Email: e.ServiceAccounts[0], + + var serviceAccounts []*compute.ServiceAccount + for _, sa := range e.ServiceAccounts { + serviceAccounts = append(serviceAccounts, &compute.ServiceAccount{ + Email: fi.StringValue(sa.Email), Scopes: scopes, - }, + }) } - // if e.ServiceAccounts != nil { - // for _, s := range e.ServiceAccounts { - // serviceAccounts = append(serviceAccounts, &compute.ServiceAccount{ - // Email: s, - // Scopes: scopes, - // }) - // } - // } else { - // serviceAccounts = append(serviceAccounts, &compute.ServiceAccount{ - // Email: "default", - // Scopes: scopes, - // }) - // } var metadataItems []*compute.MetadataItems for key, r := range e.Metadata {