mirror of https://github.com/kubernetes/kops.git
GCE: Project IAM Binding task
This allows us to grant a project-level permission to a service account.
This commit is contained in:
parent
0cecb07d90
commit
faeeb1fe80
|
|
@ -6,6 +6,7 @@ go_library(
|
|||
importpath = "k8s.io/kops/cloudmock/gce",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//cloudmock/gce/mockcloudresourcemanager:go_default_library",
|
||||
"//cloudmock/gce/mockcompute:go_default_library",
|
||||
"//cloudmock/gce/mockdns:go_default_library",
|
||||
"//cloudmock/gce/mockiam:go_default_library",
|
||||
|
|
@ -16,6 +17,7 @@ go_library(
|
|||
"//pkg/cloudinstances:go_default_library",
|
||||
"//upup/pkg/fi:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/gce:go_default_library",
|
||||
"//vendor/google.golang.org/api/cloudresourcemanager/v1:go_default_library",
|
||||
"//vendor/google.golang.org/api/compute/v1:go_default_library",
|
||||
"//vendor/google.golang.org/api/iam/v1:go_default_library",
|
||||
"//vendor/google.golang.org/api/storage/v1:go_default_library",
|
||||
|
|
|
|||
|
|
@ -19,11 +19,13 @@ package gce
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/api/cloudresourcemanager/v1"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
"google.golang.org/api/iam/v1"
|
||||
"google.golang.org/api/storage/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kops/cloudmock/gce/mockcloudresourcemanager"
|
||||
mockcompute "k8s.io/kops/cloudmock/gce/mockcompute"
|
||||
"k8s.io/kops/cloudmock/gce/mockdns"
|
||||
"k8s.io/kops/cloudmock/gce/mockiam"
|
||||
|
|
@ -42,10 +44,11 @@ type MockGCECloud struct {
|
|||
region string
|
||||
labels map[string]string
|
||||
|
||||
computeClient *mockcompute.MockClient
|
||||
dnsClient *mockdns.MockClient
|
||||
iamClient *iam.Service
|
||||
storageClient *storage.Service
|
||||
computeClient *mockcompute.MockClient
|
||||
dnsClient *mockdns.MockClient
|
||||
iamClient *iam.Service
|
||||
storageClient *storage.Service
|
||||
cloudResourceManagerClient *cloudresourcemanager.Service
|
||||
}
|
||||
|
||||
var _ gce.GCECloud = &MockGCECloud{}
|
||||
|
|
@ -53,12 +56,13 @@ var _ gce.GCECloud = &MockGCECloud{}
|
|||
// InstallMockGCECloud registers a MockGCECloud implementation for the specified region & project
|
||||
func InstallMockGCECloud(region string, project string) *MockGCECloud {
|
||||
c := &MockGCECloud{
|
||||
project: project,
|
||||
region: region,
|
||||
computeClient: mockcompute.NewMockClient(project),
|
||||
dnsClient: mockdns.NewMockClient(),
|
||||
iamClient: mockiam.New(project),
|
||||
storageClient: mockstorage.New(),
|
||||
project: project,
|
||||
region: region,
|
||||
computeClient: mockcompute.NewMockClient(project),
|
||||
dnsClient: mockdns.NewMockClient(),
|
||||
iamClient: mockiam.New(project),
|
||||
storageClient: mockstorage.New(),
|
||||
cloudResourceManagerClient: mockcloudresourcemanager.New(),
|
||||
}
|
||||
gce.CacheGCECloudInstance(region, project, c)
|
||||
return c
|
||||
|
|
@ -117,6 +121,11 @@ func (c *MockGCECloud) IAM() *iam.Service {
|
|||
return c.iamClient
|
||||
}
|
||||
|
||||
// CloudResourceManager returns the client for the cloudresourcemanager API
|
||||
func (c *MockGCECloud) CloudResourceManager() *cloudresourcemanager.Service {
|
||||
return c.cloudResourceManagerClient
|
||||
}
|
||||
|
||||
// CloudDNS returns the DNS client
|
||||
func (c *MockGCECloud) CloudDNS() gce.DNSClient {
|
||||
return c.dnsClient
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"api.go",
|
||||
"projects.go",
|
||||
],
|
||||
importpath = "k8s.io/kops/cloudmock/gce/mockcloudresourcemanager",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//cloudmock/gce/gcphttp:go_default_library",
|
||||
"//vendor/google.golang.org/api/cloudresourcemanager/v1:go_default_library",
|
||||
"//vendor/google.golang.org/api/option:go_default_library",
|
||||
"//vendor/k8s.io/klog/v2:go_default_library",
|
||||
],
|
||||
)
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
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 mockcloudresourcemanager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/api/cloudresourcemanager/v1"
|
||||
option "google.golang.org/api/option"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// mockCloudResourceManagerService represents a mocked cloudresourcemanager client.
|
||||
type mockCloudResourceManagerService struct {
|
||||
svc *cloudresourcemanager.Service
|
||||
|
||||
projects projects
|
||||
}
|
||||
|
||||
// New creates a new mock cloudresourcemanager client.
|
||||
func New() *cloudresourcemanager.Service {
|
||||
ctx := context.Background()
|
||||
|
||||
s := &mockCloudResourceManagerService{}
|
||||
|
||||
s.projects.Init()
|
||||
|
||||
httpClient := &http.Client{Transport: s}
|
||||
svc, err := cloudresourcemanager.NewService(ctx, option.WithHTTPClient(httpClient))
|
||||
if err != nil {
|
||||
klog.Fatalf("failed to build mock cloudresourcemanager service: %v", err)
|
||||
}
|
||||
s.svc = svc
|
||||
return svc
|
||||
}
|
||||
|
||||
func (s *mockCloudResourceManagerService) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||
url := request.URL
|
||||
if url.Host != "cloudresourcemanager.googleapis.com" {
|
||||
return nil, fmt.Errorf("unexpected host in request %#v", request)
|
||||
}
|
||||
|
||||
pathTokens := strings.Split(strings.TrimPrefix(url.Path, "/"), "/")
|
||||
if len(pathTokens) >= 1 && pathTokens[0] == "v1" {
|
||||
if len(pathTokens) >= 3 && pathTokens[1] == "projects" {
|
||||
projectTokens := strings.Split(pathTokens[2], ":")
|
||||
if len(projectTokens) == 2 {
|
||||
projectID := projectTokens[0]
|
||||
verb := projectTokens[1]
|
||||
|
||||
if request.Method == "POST" && verb == "getIamPolicy" {
|
||||
return s.projects.getIAMPolicy(projectID, request)
|
||||
}
|
||||
|
||||
if request.Method == "POST" && verb == "setIamPolicy" {
|
||||
return s.projects.setIAMPolicy(projectID, request)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// klog.Warningf("request: %s %s %#v", request.Method, request.URL, request)
|
||||
return nil, fmt.Errorf("unhandled request %#v", request)
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
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 mockcloudresourcemanager
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"google.golang.org/api/cloudresourcemanager/v1"
|
||||
"k8s.io/kops/cloudmock/gce/gcphttp"
|
||||
)
|
||||
|
||||
type projects struct {
|
||||
mutex sync.Mutex
|
||||
|
||||
projectBindings map[string]*cloudresourcemanager.Policy
|
||||
}
|
||||
|
||||
func (s *projects) Init() {
|
||||
s.projectBindings = make(map[string]*cloudresourcemanager.Policy)
|
||||
}
|
||||
|
||||
func (s *projects) getIAMPolicy(projectID string, request *http.Request) (*http.Response, error) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
bindings := s.projectBindings[projectID]
|
||||
if bindings == nil {
|
||||
bindings = &cloudresourcemanager.Policy{
|
||||
Etag: nextEtag(""),
|
||||
}
|
||||
}
|
||||
|
||||
return gcphttp.OKResponse(bindings)
|
||||
}
|
||||
|
||||
func nextEtag(etag string) string {
|
||||
hash := sha256.Sum256([]byte(etag))
|
||||
nextEtag := hex.EncodeToString(hash[:])
|
||||
return nextEtag
|
||||
}
|
||||
|
||||
func (s *projects) setIAMPolicy(projectID string, request *http.Request) (*http.Response, error) {
|
||||
b, err := io.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
return gcphttp.ErrorBadRequest("")
|
||||
}
|
||||
|
||||
req := &cloudresourcemanager.SetIamPolicyRequest{}
|
||||
if err := json.Unmarshal(b, &req); err != nil {
|
||||
return gcphttp.ErrorBadRequest("")
|
||||
}
|
||||
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
oldBindings := s.projectBindings[projectID]
|
||||
if oldBindings == nil {
|
||||
oldBindings = &cloudresourcemanager.Policy{
|
||||
Etag: nextEtag(""),
|
||||
}
|
||||
}
|
||||
|
||||
newBindings := req.Policy
|
||||
|
||||
if oldBindings.Etag != newBindings.Etag {
|
||||
// TODO: What is the actual error?
|
||||
return gcphttp.ErrorNotFound("etag")
|
||||
}
|
||||
|
||||
newBindings.Etag = nextEtag(oldBindings.Etag)
|
||||
s.projectBindings[projectID] = newBindings
|
||||
|
||||
return gcphttp.OKResponse(newBindings)
|
||||
}
|
||||
|
|
@ -28,6 +28,7 @@ go_library(
|
|||
"//upup/pkg/fi:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/gce/gcemetadata:go_default_library",
|
||||
"//vendor/golang.org/x/oauth2/google:go_default_library",
|
||||
"//vendor/google.golang.org/api/cloudresourcemanager/v1:go_default_library",
|
||||
"//vendor/google.golang.org/api/compute/v1:go_default_library",
|
||||
"//vendor/google.golang.org/api/dns/v1:go_default_library",
|
||||
"//vendor/google.golang.org/api/googleapi:go_default_library",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"golang.org/x/oauth2/google"
|
||||
"google.golang.org/api/cloudresourcemanager/v1"
|
||||
compute "google.golang.org/api/compute/v1"
|
||||
"google.golang.org/api/iam/v1"
|
||||
oauth2 "google.golang.org/api/oauth2/v2"
|
||||
|
|
@ -50,6 +51,9 @@ type GCECloud interface {
|
|||
|
||||
// ServiceAccount returns the email for the service account that the instances will run under
|
||||
ServiceAccount() (string, error)
|
||||
|
||||
// CloudResourceManager returns the client for the cloudresourcemanager API
|
||||
CloudResourceManager() *cloudresourcemanager.Service
|
||||
}
|
||||
|
||||
type gceCloudImplementation struct {
|
||||
|
|
@ -58,6 +62,9 @@ type gceCloudImplementation struct {
|
|||
iam *iam.Service
|
||||
dns *dnsClientImpl
|
||||
|
||||
// cloudResourceManager is the client for the cloudresourcemanager API
|
||||
cloudResourceManager *cloudresourcemanager.Service
|
||||
|
||||
region string
|
||||
project string
|
||||
|
||||
|
|
@ -144,6 +151,12 @@ func NewGCECloud(region string, project string, labels map[string]string) (GCECl
|
|||
}
|
||||
c.dns = dnsClient
|
||||
|
||||
cloudResourceManager, err := cloudresourcemanager.NewService(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error building cloudresourcemanager API client: %w", err)
|
||||
}
|
||||
c.cloudResourceManager = cloudResourceManager
|
||||
|
||||
CacheGCECloudInstance(region, project, c)
|
||||
|
||||
{
|
||||
|
|
@ -193,11 +206,16 @@ func (c *gceCloudImplementation) IAM() *iam.Service {
|
|||
return c.iam
|
||||
}
|
||||
|
||||
// NameService returns the DNS client
|
||||
// CloudDNS returns the DNS client
|
||||
func (c *gceCloudImplementation) CloudDNS() DNSClient {
|
||||
return c.dns
|
||||
}
|
||||
|
||||
// CloudResourceManager returns the client for the cloudresourcemanager API
|
||||
func (c *gceCloudImplementation) CloudResourceManager() *cloudresourcemanager.Service {
|
||||
return c.cloudResourceManager
|
||||
}
|
||||
|
||||
// Region returns private struct element region.
|
||||
func (c *gceCloudImplementation) Region() string {
|
||||
return c.region
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ go_library(
|
|||
"instancetemplate_fitask.go",
|
||||
"network.go",
|
||||
"network_fitask.go",
|
||||
"projectiambinding.go",
|
||||
"projectiambinding_fitask.go",
|
||||
"router.go",
|
||||
"router_fitask.go",
|
||||
"serviceaccount.go",
|
||||
|
|
@ -43,6 +45,7 @@ go_library(
|
|||
"//upup/pkg/fi/cloudup/gce:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/terraform:go_default_library",
|
||||
"//upup/pkg/fi/cloudup/terraformWriter:go_default_library",
|
||||
"//vendor/google.golang.org/api/cloudresourcemanager/v1:go_default_library",
|
||||
"//vendor/google.golang.org/api/compute/v1:go_default_library",
|
||||
"//vendor/google.golang.org/api/iam/v1:go_default_library",
|
||||
"//vendor/google.golang.org/api/storage/v1:go_default_library",
|
||||
|
|
@ -53,6 +56,7 @@ go_library(
|
|||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"projectiambinding_test.go",
|
||||
"serviceaccount_test.go",
|
||||
"storagebucketiam_test.go",
|
||||
],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
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 gcetasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/api/cloudresourcemanager/v1"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/gce"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
// ProjectIAMBinding represents an IAM rule on a project
|
||||
// +kops:fitask
|
||||
type ProjectIAMBinding struct {
|
||||
Name *string
|
||||
Lifecycle fi.Lifecycle
|
||||
|
||||
Project *string
|
||||
Member *string
|
||||
Role *string
|
||||
}
|
||||
|
||||
var _ fi.CompareWithID = &ProjectIAMBinding{}
|
||||
|
||||
func (e *ProjectIAMBinding) CompareWithID() *string {
|
||||
return e.Name
|
||||
}
|
||||
|
||||
func (e *ProjectIAMBinding) Find(c *fi.Context) (*ProjectIAMBinding, error) {
|
||||
ctx := context.TODO()
|
||||
|
||||
cloud := c.Cloud.(gce.GCECloud)
|
||||
|
||||
projectID := fi.StringValue(e.Project)
|
||||
member := fi.StringValue(e.Member)
|
||||
role := fi.StringValue(e.Role)
|
||||
|
||||
klog.V(2).Infof("Checking IAM for project %q", projectID)
|
||||
options := &cloudresourcemanager.GetIamPolicyRequest{Options: &cloudresourcemanager.GetPolicyOptions{RequestedPolicyVersion: 3}}
|
||||
policy, err := cloud.CloudResourceManager().Projects.GetIamPolicy(projectID, options).Context(ctx).Do()
|
||||
if err != nil {
|
||||
if gce.IsNotFound(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("error checking IAM for project %s: %w", projectID, err)
|
||||
}
|
||||
|
||||
changed := patchCRMPolicy(policy, member, role)
|
||||
if changed {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
actual := &ProjectIAMBinding{}
|
||||
actual.Project = e.Project
|
||||
actual.Member = e.Member
|
||||
actual.Role = e.Role
|
||||
|
||||
// Ignore "system" fields
|
||||
actual.Name = e.Name
|
||||
actual.Lifecycle = e.Lifecycle
|
||||
|
||||
return actual, nil
|
||||
}
|
||||
|
||||
func (e *ProjectIAMBinding) Run(c *fi.Context) error {
|
||||
return fi.DefaultDeltaRunMethod(e, c)
|
||||
}
|
||||
|
||||
func (_ *ProjectIAMBinding) CheckChanges(a, e, changes *ProjectIAMBinding) error {
|
||||
if fi.StringValue(e.Project) == "" {
|
||||
return fi.RequiredField("Project")
|
||||
}
|
||||
if fi.StringValue(e.Member) == "" {
|
||||
return fi.RequiredField("Member")
|
||||
}
|
||||
if fi.StringValue(e.Role) == "" {
|
||||
return fi.RequiredField("Role")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *ProjectIAMBinding) RenderGCE(t *gce.GCEAPITarget, a, e, changes *ProjectIAMBinding) error {
|
||||
ctx := context.TODO()
|
||||
|
||||
projectID := fi.StringValue(e.Project)
|
||||
member := fi.StringValue(e.Member)
|
||||
role := fi.StringValue(e.Role)
|
||||
|
||||
request := &cloudresourcemanager.GetIamPolicyRequest{}
|
||||
policy, err := t.Cloud.CloudResourceManager().Projects.GetIamPolicy(projectID, request).Context(ctx).Do()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting IAM policy for project %s: %w", projectID, err)
|
||||
}
|
||||
|
||||
changed := patchCRMPolicy(policy, member, role)
|
||||
|
||||
if !changed {
|
||||
klog.Warningf("did not need to change policy (concurrent change?)")
|
||||
return nil
|
||||
}
|
||||
|
||||
klog.V(2).Infof("updating IAM for project %s", projectID)
|
||||
if _, err := t.Cloud.CloudResourceManager().Projects.SetIamPolicy(projectID, &cloudresourcemanager.SetIamPolicyRequest{Policy: policy}).Context(ctx).Do(); err != nil {
|
||||
return fmt.Errorf("error updating IAM for project %s: %w", projectID, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// terraformProjectIAMBinding is the model for a terraform google_project_iam_binding rule
|
||||
type terraformProjectIAMBinding struct {
|
||||
Project string `json:"project,omitempty" cty:"project"`
|
||||
Role string `json:"role,omitempty" cty:"role"`
|
||||
Member string `json:"member,omitempty" cty:"member"`
|
||||
}
|
||||
|
||||
func (_ *ProjectIAMBinding) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *ProjectIAMBinding) error {
|
||||
tf := &terraformProjectIAMBinding{
|
||||
Project: fi.StringValue(e.Project),
|
||||
Role: fi.StringValue(e.Role),
|
||||
Member: fi.StringValue(e.Member),
|
||||
}
|
||||
|
||||
return t.RenderResource("google_project_iam_binding", *e.Name, tf)
|
||||
}
|
||||
|
||||
func patchCRMPolicy(policy *cloudresourcemanager.Policy, wantMember string, wantRole string) bool {
|
||||
for _, binding := range policy.Bindings {
|
||||
if binding.Condition != nil {
|
||||
continue
|
||||
}
|
||||
if binding.Role != wantRole {
|
||||
continue
|
||||
}
|
||||
exists := false
|
||||
for _, member := range binding.Members {
|
||||
if member == wantMember {
|
||||
exists = true
|
||||
}
|
||||
}
|
||||
if exists {
|
||||
return false
|
||||
}
|
||||
|
||||
if !exists {
|
||||
binding.Members = append(binding.Members, wantMember)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
policy.Bindings = append(policy.Bindings, &cloudresourcemanager.Binding{
|
||||
Members: []string{wantMember},
|
||||
Role: wantRole,
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
|
@ -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 gcetasks
|
||||
|
||||
import (
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
)
|
||||
|
||||
// ProjectIAMBinding
|
||||
|
||||
var _ fi.HasLifecycle = &ProjectIAMBinding{}
|
||||
|
||||
// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle
|
||||
func (o *ProjectIAMBinding) GetLifecycle() fi.Lifecycle {
|
||||
return o.Lifecycle
|
||||
}
|
||||
|
||||
// SetLifecycle sets the Lifecycle of the object, implementing fi.SetLifecycle
|
||||
func (o *ProjectIAMBinding) SetLifecycle(lifecycle fi.Lifecycle) {
|
||||
o.Lifecycle = lifecycle
|
||||
}
|
||||
|
||||
var _ fi.HasName = &ProjectIAMBinding{}
|
||||
|
||||
// GetName returns the Name of the object, implementing fi.HasName
|
||||
func (o *ProjectIAMBinding) GetName() *string {
|
||||
return o.Name
|
||||
}
|
||||
|
||||
// String is the stringer function for the task, producing readable output using fi.TaskAsString
|
||||
func (o *ProjectIAMBinding) String() string {
|
||||
return fi.TaskAsString(o)
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
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 gcetasks
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
gcemock "k8s.io/kops/cloudmock/gce"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
)
|
||||
|
||||
func TestProjectIAMBinding(t *testing.T) {
|
||||
project := "testproject"
|
||||
region := "us-test1"
|
||||
|
||||
cloud := gcemock.InstallMockGCECloud(region, project)
|
||||
|
||||
// We define a function so we can rebuild the tasks, because we modify in-place when running
|
||||
buildTasks := func() map[string]fi.Task {
|
||||
binding := &ProjectIAMBinding{
|
||||
Lifecycle: fi.LifecycleSync,
|
||||
|
||||
Project: fi.String("testproject"),
|
||||
Member: fi.String("serviceAccount:foo@testproject.iam.gserviceaccount.com"),
|
||||
Role: fi.String("roles/owner"),
|
||||
}
|
||||
|
||||
return map[string]fi.Task{
|
||||
"binding": binding,
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
allTasks := buildTasks()
|
||||
checkHasChanges(t, cloud, allTasks)
|
||||
}
|
||||
|
||||
{
|
||||
allTasks := buildTasks()
|
||||
runTasks(t, cloud, allTasks)
|
||||
}
|
||||
|
||||
{
|
||||
allTasks := buildTasks()
|
||||
checkNoChanges(t, cloud, allTasks)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue