diff --git a/Gopkg.lock b/Gopkg.lock index 64b6faccf2..649c68d106 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -41,7 +41,7 @@ version = "1.0.0" [[projects]] - digest = "1:1fe4e00b5f0ca95c699c92d097ccc2e96d6c2061cfa7ecc4c9248f1cfb8e217e" + digest = "1:c9f5a5d068a9b13bce0623860177de18d4f5a6cab3968c77fd524811f525dc0e" name = "github.com/aws/aws-sdk-go" packages = [ "aws", @@ -69,6 +69,8 @@ "internal/shareddefaults", "private/protocol", "private/protocol/ec2query", + "private/protocol/json/jsonutil", + "private/protocol/jsonrpc", "private/protocol/query", "private/protocol/query/queryutil", "private/protocol/rest", @@ -79,6 +81,7 @@ "service/ec2/ec2iface", "service/elb", "service/elb/elbiface", + "service/resourcegroupstaggingapi", "service/sts", "service/sts/stsiface", ] @@ -1268,6 +1271,7 @@ "github.com/aws/aws-sdk-go/service/ec2/ec2iface", "github.com/aws/aws-sdk-go/service/elb", "github.com/aws/aws-sdk-go/service/elb/elbiface", + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi", "github.com/aws/aws-sdk-go/service/sts", "github.com/aws/aws-sdk-go/service/sts/stsiface", "github.com/awslabs/goformation/cloudformation", diff --git a/Gopkg.toml b/Gopkg.toml index 10b6bf58e7..b9608ce5fb 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -65,3 +65,7 @@ version="v1.4.7" [[prune.project]] name = "sigs.k8s.io/cluster-api" unused-packages = false + +[[constraint]] + name = "github.com/aws/aws-sdk-go" + version = ">=1.17.5" diff --git a/cmd/clusterawsadm/cmd/alpha/BUILD.bazel b/cmd/clusterawsadm/cmd/alpha/BUILD.bazel index bd8b9c66e4..793ecadeca 100644 --- a/cmd/clusterawsadm/cmd/alpha/BUILD.bazel +++ b/cmd/clusterawsadm/cmd/alpha/BUILD.bazel @@ -7,6 +7,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//cmd/clusterawsadm/cmd/alpha/bootstrap:go_default_library", + "//cmd/clusterawsadm/cmd/alpha/migrate:go_default_library", "//vendor/github.com/spf13/cobra:go_default_library", ], ) diff --git a/cmd/clusterawsadm/cmd/alpha/alpha.go b/cmd/clusterawsadm/cmd/alpha/alpha.go index 8e843fe40d..6f4ddcdeeb 100644 --- a/cmd/clusterawsadm/cmd/alpha/alpha.go +++ b/cmd/clusterawsadm/cmd/alpha/alpha.go @@ -19,6 +19,7 @@ package alpha import ( "github.com/spf13/cobra" "sigs.k8s.io/cluster-api-provider-aws/cmd/clusterawsadm/cmd/alpha/bootstrap" + "sigs.k8s.io/cluster-api-provider-aws/cmd/clusterawsadm/cmd/alpha/migrate" ) // AlphaCmd is the top-level alpha set of commands @@ -32,5 +33,6 @@ func AlphaCmd() *cobra.Command { // nolint }, } newCmd.AddCommand(bootstrap.RootCmd()) + newCmd.AddCommand(migrate.MigrateCmd()) return newCmd } diff --git a/cmd/clusterawsadm/cmd/alpha/migrate/BUILD b/cmd/clusterawsadm/cmd/alpha/migrate/BUILD new file mode 100644 index 0000000000..c291841b8f --- /dev/null +++ b/cmd/clusterawsadm/cmd/alpha/migrate/BUILD @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["migrate.go"], + importpath = "sigs.k8s.io/cluster-api-provider-aws/cmd/clusterawsadm/cmd/alpha/migrate", + visibility = ["//visibility:public"], + deps = [ + "//pkg/cloud/aws/tags:go_default_library", + "//vendor/github.com/aws/aws-sdk-go/aws:go_default_library", + "//vendor/github.com/aws/aws-sdk-go/aws/session:go_default_library", + "//vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi:go_default_library", + "//vendor/github.com/spf13/cobra:go_default_library", + ], +) diff --git a/cmd/clusterawsadm/cmd/alpha/migrate/migrate.go b/cmd/clusterawsadm/cmd/alpha/migrate/migrate.go new file mode 100644 index 0000000000..7df3a08992 --- /dev/null +++ b/cmd/clusterawsadm/cmd/alpha/migrate/migrate.go @@ -0,0 +1,208 @@ +/* +Copyright 2018 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 migrate + +import ( + "fmt" + "os" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + awstags "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" + "github.com/spf13/cobra" + "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/aws/tags" +) + +var ( + // See: https://docs.aws.amazon.com/sdk-for-go/api/service/resourcegroupstaggingapi/#TagResourcesInput + maxARNs = 20 + supportedVersions = []string{"0.3.0"} + + clusterName string +) + +// MigrateCmd is the command for migrating AWS resources to be compatible +// with specific CAPA versions +func MigrateCmd() *cobra.Command { // nolint + newCmd := &cobra.Command{ + Use: "migrate [target version]", + Short: "migrate between CAPA versions", + Long: fmt.Sprintf(`Migrate AWS resources between incompatible versions of Cluster API Provider AWS. +Supported versions: %v`, supportedVersions), + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + fmt.Printf("Error: requires target version as an argument. Supported versions: %v\n\n", supportedVersions) + if err := cmd.Help(); err != nil { + return err + } + os.Exit(200) + } + if !isValidVersion(args[0]) { + fmt.Printf("Error: unsupported migration target. Supported versions: %v\n\n", supportedVersions) + cmd.Help() + os.Exit(201) + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + sess, err := session.NewSessionWithOptions(session.Options{ + SharedConfigState: session.SharedConfigEnable, + }) + if err != nil { + fmt.Printf("Error: %v", err) + return nil + } + + tagsSvc := awstags.New(sess) + + resources, err := getResourcesByCluster(tagsSvc, clusterName) + if err != nil { + return err + } + + fmt.Printf("Found %v resources owned by cluster %q.\n", len(resources), clusterName) + fmt.Printf("Applying new tags to cluster %q.\n", clusterName) + + err = applyNewTags(tagsSvc, resources, clusterName) + if err != nil { + return err + } + + fmt.Printf("Removing legacy tags from cluster %q\n", clusterName) + + return removeOldTags(tagsSvc, resources, clusterName) + }, + } + + newCmd.Flags().StringVarP(&clusterName, "clusterName", "n", "", "name of existing Cluster object") + newCmd.MarkFlagRequired("clusterName") + + return newCmd +} + +func getResourcesByCluster(svc *awstags.ResourceGroupsTaggingAPI, name string) ([]*string, error) { + input := &awstags.GetResourcesInput{ + TagFilters: []*awstags.TagFilter{ + { + Key: aws.String(fmt.Sprintf("kubernetes.io/cluster/%s", name)), + Values: []*string{aws.String("owned")}, + }, + { + Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/managed"), + Values: []*string{aws.String("true")}, + }, + }, + } + + out, err := svc.GetResources(input) + if err != nil { + return nil, err + } + + arns := make([]*string, 0, len(out.ResourceTagMappingList)) + for _, resource := range out.ResourceTagMappingList { + arns = append(arns, resource.ResourceARN) + } + + return arns, nil +} + +func applyNewTags(svc *awstags.ResourceGroupsTaggingAPI, arns []*string, name string) error { + + for i := 0; i <= (len(arns) / maxARNs); i++ { + end := (i + 1) * maxARNs + if end > len(arns) { + end = len(arns) + } + + input := &awstags.TagResourcesInput{ + ResourceARNList: arns[i*maxARNs : end], + Tags: map[string]*string{ + tags.ClusterKey(name): aws.String("owned"), + }, + } + + _, err := svc.TagResources(input) + if err != nil { + return err + } + } + + return nil +} + +func removeOldTags(svc *awstags.ResourceGroupsTaggingAPI, arns []*string, name string) error { + for i := 0; i <= (len(arns) / maxARNs); i++ { + end := (i + 1) * maxARNs + if end > len(arns) { + end = len(arns) + } + + managedInput := &awstags.UntagResourcesInput{ + ResourceARNList: arns[i*maxARNs : end], + TagKeys: []*string{ + aws.String("sigs.k8s.io/cluster-api-provider-aws/managed"), + }, + } + + _, err := svc.UntagResources(managedInput) + if err != nil { + return err + } + } + + var filteredARNs []*string + + for _, v := range arns { + // instances should have both ownership tags, so filter those out + // TODO(rudoi): is there a better way to filter? + if !strings.Contains(aws.StringValue(v), "instance") { + filteredARNs = append(filteredARNs, v) + } + } + + for i := 0; i <= (len(filteredARNs) / maxARNs); i++ { + end := (i + 1) * maxARNs + if i == (len(filteredARNs) / maxARNs) { + end = len(filteredARNs) + } + + ownedInput := &awstags.UntagResourcesInput{ + ResourceARNList: filteredARNs[i*maxARNs : end], + TagKeys: []*string{ + aws.String(fmt.Sprintf("kubernetes.io/cluster/%s", name)), + }, + } + + _, err := svc.UntagResources(ownedInput) + if err != nil { + return err + } + } + + return nil +} + +func isValidVersion(s string) bool { + for _, v := range supportedVersions { + if s == v { + return true + } + } + return false +} diff --git a/docs/proposal/aws-resource-handling.md b/docs/proposal/aws-resource-handling.md index 7410a1b0bc..1cc5500b26 100644 --- a/docs/proposal/aws-resource-handling.md +++ b/docs/proposal/aws-resource-handling.md @@ -38,7 +38,11 @@ Where possible use [client tokens](https://docs.aws.amazon.com/AWSEC2/latest/API ## Tagging of resources -Resources that are managed by the controllers/actuators should be tagged with: `kubernetes.io/cluster/=owned` and `sigs.k8s.io/cluster-api-provider-aws=true`. The latter tag being used to differentiate from resources managed by other tools/components that make use of the common tag. +Resources handled by these components fall into one of three categories: + +1. Fully-managed resources whose lifecycle is tied to the cluster. These resources should be tagged with `sigs.k8s.io/cluster-api-provider-aws/cluster/=owned`, and the actuator is expected to keep these resources as closely in sync with the spec as possible. +2. Resources whose management is shared with the in-cluster aws cloud provider, such as a security group for load balancer ingress rules. These resources should be tagged with `sigs.k8s.io/cluster-api-provider-aws/cluster/=owned` and `kubernetes.io/cluster/=owned`, with the latter being the tag defined by the cloud provider. These resources are create/delete only: that is to say their ongoing management is "handed off" to the cloud provider. +3. Unmanaged resources that are provided by config (such as a common VPC). The provider will avoid changing these resources as much as is possible. TODO: Define additional tags that can be used to provide additional metadata about the resource configuration/usage by the actuator. This is would allow us to rebuild status without relying on polluting the object config. diff --git a/docs/upgrade-to-0.3.0.md b/docs/upgrade-to-0.3.0.md new file mode 100644 index 0000000000..741ae9a95e --- /dev/null +++ b/docs/upgrade-to-0.3.0.md @@ -0,0 +1,13 @@ +# Upgrading to 0.3.0 + +In 0.3.0, the tagging scheme changed for identifying AWS resources. In order not to lose track, there is a partial migration tool included in 0.3.0's `clusterawsadm`. + +The migration path is as follows: + +1. `kubectl scale statefulset -n aws-provider-system aws-provider-controller-manager --replicas=0` +2. `clusterawsadm migrate -n CLUSTER_NAME 0.3.0` +3. Update the image for the aws-provider-controller-manager +4. `kubectl scale statefulset -n aws-provider-system aws-provider-controller-manager --replicas=1` +5. Wait ~2 minutes for the security group changes to all settle + - All of the nodes and control plane machines should have exactly one security group tagged with `kubernetes.io/cluster/=owned`: the new `CLUSTER_NAME-lb` group. +6. Find the names of your controller-manager pods, and run `kubectl exec -n kube-system -it CONTROLLER_MANAGER_POD_NAME -- sh -c 'kill 1' as a workaround for kubernetes/kubernetes#77019` diff --git a/pkg/apis/awsprovider/v1alpha1/awsclusterproviderconfig_types.go b/pkg/apis/awsprovider/v1alpha1/awsclusterproviderconfig_types.go index 7ffcaf1918..b7527035e0 100644 --- a/pkg/apis/awsprovider/v1alpha1/awsclusterproviderconfig_types.go +++ b/pkg/apis/awsprovider/v1alpha1/awsclusterproviderconfig_types.go @@ -111,9 +111,9 @@ func (v *VPCSpec) String() string { return fmt.Sprintf("id=%s", v.ID) } -// IsProvided returns true if the VPC is unmanaged. -func (v *VPCSpec) IsProvided() bool { - return v.ID != "" && !v.Tags.HasManaged() +// IsUnmanaged returns true if the VPC is unmanaged. +func (v *VPCSpec) IsUnmanaged(clusterName string) bool { + return v.ID != "" && !v.Tags.HasOwned(clusterName) } // SubnetSpec configures an AWS Subnet. diff --git a/pkg/apis/awsprovider/v1alpha1/types.go b/pkg/apis/awsprovider/v1alpha1/types.go index c7f4dfc10f..41a2b85079 100644 --- a/pkg/apis/awsprovider/v1alpha1/types.go +++ b/pkg/apis/awsprovider/v1alpha1/types.go @@ -23,6 +23,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/aws/tags" ) // AWSResourceReference is a reference to a specific AWS resource by ID, ARN, or filters. @@ -237,6 +238,9 @@ var ( // SecurityGroupControlPlane defines a Kubernetes control plane node role SecurityGroupControlPlane = SecurityGroupRole("controlplane") + + // SecurityGroupLB defines a container for the cloud provider to inject its load balancer ingress rules + SecurityGroupLB = SecurityGroupRole("lb") ) // SecurityGroup defines an AWS security group. @@ -251,7 +255,7 @@ type SecurityGroup struct { IngressRules IngressRules `json:"ingressRule"` // Tags is a map of tags associated with the security group. - Tags map[string]string `json:"tags,omitempty"` + Tags tags.Map `json:"tags,omitempty"` } // String returns a string representation of the security group. diff --git a/pkg/apis/awsprovider/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/awsprovider/v1alpha1/zz_generated.deepcopy.go index 3f770147d0..42961e2b77 100644 --- a/pkg/apis/awsprovider/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/awsprovider/v1alpha1/zz_generated.deepcopy.go @@ -596,7 +596,7 @@ func (in *SecurityGroup) DeepCopyInto(out *SecurityGroup) { } if in.Tags != nil { in, out := &in.Tags, &out.Tags - *out = make(map[string]string, len(*in)) + *out = make(tags.Map, len(*in)) for key, val := range *in { (*out)[key] = val } diff --git a/pkg/cloud/aws/actuators/machine/actuator.go b/pkg/cloud/aws/actuators/machine/actuator.go index 9f67fb19b7..a2930cf00e 100644 --- a/pkg/cloud/aws/actuators/machine/actuator.go +++ b/pkg/cloud/aws/actuators/machine/actuator.go @@ -374,13 +374,18 @@ func (a *Actuator) Update(ctx context.Context, cluster *clusterv1.Cluster, machi return errors.Errorf("found attempt to change immutable state for machine %q: %+q", machine.Name, errs) } + existingSecurityGroups, err := ec2svc.GetInstanceSecurityGroups(*scope.MachineStatus.InstanceID) + if err != nil { + return err + } + // Ensure that the security groups are correct. _, err = a.ensureSecurityGroups( ec2svc, - machine, + scope, *scope.MachineStatus.InstanceID, scope.MachineConfig.AdditionalSecurityGroups, - instanceDescription.SecurityGroupIDs, + existingSecurityGroups, ) if err != nil { return errors.Errorf("failed to apply security groups: %+v", err) diff --git a/pkg/cloud/aws/actuators/machine/security_groups.go b/pkg/cloud/aws/actuators/machine/security_groups.go index c99400e4de..43b11760f3 100644 --- a/pkg/cloud/aws/actuators/machine/security_groups.go +++ b/pkg/cloud/aws/actuators/machine/security_groups.go @@ -21,9 +21,8 @@ import ( "sort" "sigs.k8s.io/cluster-api-provider-aws/pkg/apis/awsprovider/v1alpha1" + "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/aws/actuators" service "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/aws/services" - - clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" ) const ( @@ -40,13 +39,17 @@ const ( // Returns bool, error // Bool indicates if changes were made or not, allowing the caller to decide // if the machine should be updated. -func (a *Actuator) ensureSecurityGroups(ec2svc service.EC2MachineInterface, machine *clusterv1.Machine, instanceID string, additional []v1alpha1.AWSResourceReference, existing []string) (bool, error) { - annotation, err := a.machineAnnotationJSON(machine, SecurityGroupsLastAppliedAnnotation) +func (a *Actuator) ensureSecurityGroups(ec2svc service.EC2MachineInterface, scope *actuators.MachineScope, instanceID string, additional []v1alpha1.AWSResourceReference, existing map[string][]string) (bool, error) { + annotation, err := a.machineAnnotationJSON(scope.Machine, SecurityGroupsLastAppliedAnnotation) if err != nil { return false, err } - changed, ids := a.securityGroupsChanged(annotation, additional, existing) + core, err := ec2svc.GetCoreSecurityGroups(scope) + if err != nil { + return false, err + } + changed, ids := a.securityGroupsChanged(annotation, core, additional, existing) if !changed { return false, nil } @@ -61,7 +64,7 @@ func (a *Actuator) ensureSecurityGroups(ec2svc service.EC2MachineInterface, mach newAnnotation[*id.ID] = struct{}{} } - if err := a.updateMachineAnnotationJSON(machine, SecurityGroupsLastAppliedAnnotation, newAnnotation); err != nil { + if err := a.updateMachineAnnotationJSON(scope.Machine, SecurityGroupsLastAppliedAnnotation, newAnnotation); err != nil { return false, err } @@ -69,7 +72,7 @@ func (a *Actuator) ensureSecurityGroups(ec2svc service.EC2MachineInterface, mach } // securityGroupsChanged determines which security groups to delete and which to add. -func (a *Actuator) securityGroupsChanged(annotation map[string]interface{}, additional []v1alpha1.AWSResourceReference, existing []string) (bool, []string) { +func (a *Actuator) securityGroupsChanged(annotation map[string]interface{}, core []string, additional []v1alpha1.AWSResourceReference, existing map[string][]string) (bool, []string) { state := map[string]bool{} for _, s := range additional { state[*s.ID] = true @@ -83,6 +86,11 @@ func (a *Actuator) securityGroupsChanged(annotation map[string]interface{}, addi } } + // add (or add back) the core security groups + for _, s := range core { + state[s] = true + } + // Build the security group list. res := []string{} for id, keep := range state { @@ -91,26 +99,20 @@ func (a *Actuator) securityGroupsChanged(annotation map[string]interface{}, addi } } - // Add groups managed externally (i.e. not in the state). - for _, id := range existing { - if _, ok := state[id]; !ok { - res = append(res, id) + for _, actual := range existing { + if len(actual) != len(res) { + return true, res } - } - changed := len(existing) != len(res) - - if !changed { // Length is the same, check if the ids are the same too. - sort.Strings(existing) + sort.Strings(actual) sort.Strings(res) for i, id := range res { - if existing[i] != id { - changed = true - break + if actual[i] != id { + return true, res } } } - return changed, res + return false, res } diff --git a/pkg/cloud/aws/filter/ec2.go b/pkg/cloud/aws/filter/ec2.go index 9b7dd8e254..a8653c6e63 100644 --- a/pkg/cloud/aws/filter/ec2.go +++ b/pkg/cloud/aws/filter/ec2.go @@ -72,18 +72,10 @@ func (ec2Filters) ClusterShared(clusterName string) *ec2.Filter { } } -// ProviderManaged returns a filter using cluster-api-provider-aws managed tag. -func (ec2Filters) ProviderManaged() *ec2.Filter { - return &ec2.Filter{ - Name: aws.String(filterNameTagKey), - Values: aws.StringSlice([]string{TagNameAWSProviderManaged}), - } -} - // ProviderRole returns a filter using cluster-api-provider-aws role tag. func (ec2Filters) ProviderRole(role string) *ec2.Filter { return &ec2.Filter{ - Name: aws.String(fmt.Sprintf("tag:%s", TagNameAWSClusterAPIRole)), + Name: aws.String(fmt.Sprintf("tag:%s", tags.NameAWSClusterAPIRole)), Values: aws.StringSlice([]string{role}), } } diff --git a/pkg/cloud/aws/filter/types.go b/pkg/cloud/aws/filter/types.go index ea0429a9b6..60c7948001 100644 --- a/pkg/cloud/aws/filter/types.go +++ b/pkg/cloud/aws/filter/types.go @@ -15,29 +15,3 @@ limitations under the License. */ package filter - -const ( - // TagNameKubernetesClusterPrefix is the tag name we use to differentiate multiple - // logically independent clusters running in the same AZ. - // The tag key = TagNameKubernetesClusterPrefix + clusterID - // The tag value is an ownership value - TagNameKubernetesClusterPrefix = "kubernetes.io/cluster/" - - // TagNameAWSProviderManaged is the tag name we use to differentiate - // cluster-api-provider-aws owned components from other tooling that - // uses TagNameKubernetesClusterPrefix - TagNameAWSProviderManaged = "sigs.k8s.io/cluster-api-provider-aws/managed" - - // TagNameAWSClusterAPIRole is the tag name we use to mark roles for resources - // dedicated to this cluster api provider implementation. - TagNameAWSClusterAPIRole = "sigs.k8s.io/cluster-api-provider-aws/role" - - // TagValueAPIServerRole describes the value for the apiserver role - TagValueAPIServerRole = "apiserver" - - // TagValueBastionRole describes the value for the bastion role - TagValueBastionRole = "bastion" - - // TagValueCommonRole describes the value for the common role - TagValueCommonRole = "common" -) diff --git a/pkg/cloud/aws/services/ec2/bastion.go b/pkg/cloud/aws/services/ec2/bastion.go index b788f98bb7..b99ca7b4e6 100644 --- a/pkg/cloud/aws/services/ec2/bastion.go +++ b/pkg/cloud/aws/services/ec2/bastion.go @@ -37,7 +37,7 @@ const ( // ReconcileBastion ensures a bastion is created for the cluster func (s *Service) ReconcileBastion() error { - if s.scope.VPC().IsProvided() { + if s.scope.VPC().IsUnmanaged(s.scope.Name()) { s.scope.V(4).Info("Skipping bastion reconcile in unmanaged mode") return nil } @@ -77,7 +77,7 @@ func (s *Service) ReconcileBastion() error { // DeleteBastion deletes the Bastion instance func (s *Service) DeleteBastion() error { - if s.scope.VPC().IsProvided() { + if s.scope.VPC().IsUnmanaged(s.scope.Name()) { s.scope.V(4).Info("Skipping bastion deletion in unmanaged mode") return nil } diff --git a/pkg/cloud/aws/services/ec2/gateways.go b/pkg/cloud/aws/services/ec2/gateways.go index a6bdae62c0..b1080fe587 100644 --- a/pkg/cloud/aws/services/ec2/gateways.go +++ b/pkg/cloud/aws/services/ec2/gateways.go @@ -30,7 +30,7 @@ import ( ) func (s *Service) reconcileInternetGateways() error { - if s.scope.VPC().IsProvided() { + if s.scope.VPC().IsUnmanaged(s.scope.Name()) { s.scope.V(4).Info("Skipping internet gateways reconcile in unmanaged mode") return nil } @@ -39,7 +39,7 @@ func (s *Service) reconcileInternetGateways() error { igs, err := s.describeVpcInternetGateways() if awserrors.IsNotFound(err) { - if s.scope.VPC().IsProvided() { + if s.scope.VPC().IsUnmanaged(s.scope.Name()) { return errors.Errorf("failed to validate network: no internet gateways found in VPC %q", s.scope.VPC().ID) } @@ -69,7 +69,7 @@ func (s *Service) reconcileInternetGateways() error { } func (s *Service) deleteInternetGateways() error { - if s.scope.VPC().IsProvided() { + if s.scope.VPC().IsUnmanaged(s.scope.Name()) { s.scope.V(4).Info("Skipping internet gateway deletion in unmanaged mode") return nil } diff --git a/pkg/cloud/aws/services/ec2/gateways_test.go b/pkg/cloud/aws/services/ec2/gateways_test.go index efe8d5c3a5..7a2730b95b 100644 --- a/pkg/cloud/aws/services/ec2/gateways_test.go +++ b/pkg/cloud/aws/services/ec2/gateways_test.go @@ -47,7 +47,7 @@ func TestReconcileInternetGateways(t *testing.T) { VPC: v1alpha1.VPCSpec{ ID: "vpc-gateways", Tags: tags.Map{ - tags.NameAWSProviderManaged: "true", + tags.ClusterKey("test-cluster"): "owned", }, }, }, @@ -77,7 +77,7 @@ func TestReconcileInternetGateways(t *testing.T) { VPC: v1alpha1.VPCSpec{ ID: "vpc-gateways", Tags: tags.Map{ - tags.NameAWSProviderManaged: "true", + tags.ClusterKey("test-cluster"): "owned", }, }, }, diff --git a/pkg/cloud/aws/services/ec2/instances.go b/pkg/cloud/aws/services/ec2/instances.go index aaa2a926ee..cacd0dd639 100644 --- a/pkg/cloud/aws/services/ec2/instances.go +++ b/pkg/cloud/aws/services/ec2/instances.go @@ -119,6 +119,9 @@ func (s *Service) createInstance(machine *actuators.MachineScope, bootstrapToken Lifecycle: tags.ResourceLifecycleOwned, Name: aws.String(machine.Name()), Role: aws.String(machine.Role()), + Additional: tags.Map{ + tags.ClusterAWSCloudProviderKey(s.scope.Name()): string(tags.ResourceLifecycleOwned), + }, }) var err error @@ -166,12 +169,6 @@ func (s *Service) createInstance(machine *actuators.MachineScope, bootstrapToken // apply values based on the role of the machine switch machine.Role() { case "controlplane": - if s.scope.SecurityGroups()[v1alpha1.SecurityGroupControlPlane] == nil { - return nil, awserrors.NewFailedDependency( - errors.New("failed to run controlplane, security group not available"), - ) - } - var userData string if bootstrapToken != "" { @@ -237,10 +234,8 @@ func (s *Service) createInstance(machine *actuators.MachineScope, bootstrapToken } input.UserData = aws.String(userData) - input.SecurityGroupIDs = append(input.SecurityGroupIDs, s.scope.SecurityGroups()[v1alpha1.SecurityGroupControlPlane].ID, s.scope.SecurityGroups()[v1alpha1.SecurityGroupNode].ID) case "node": s.scope.V(2).Info("Joining a worker node to the cluster") - input.SecurityGroupIDs = append(input.SecurityGroupIDs, s.scope.SecurityGroups()[v1alpha1.SecurityGroupNode].ID) joinConfiguration := kubeadm.SetJoinNodeConfigurationOverrides(caCertHash, bootstrapToken, machine, &machine.MachineConfig.KubeadmConfiguration.Join) joinConfigurationYAML, err := kubeadm.ConfigurationToYAML(joinConfiguration) @@ -262,6 +257,14 @@ func (s *Service) createInstance(machine *actuators.MachineScope, bootstrapToken return nil, errors.Errorf("Unknown node role %q", machine.Role()) } + ids, err := s.GetCoreSecurityGroups(machine) + if err != nil { + return nil, err + } + input.SecurityGroupIDs = append(input.SecurityGroupIDs, + ids..., + ) + // Pick SSH key, if any. if machine.MachineConfig.KeyName != "" { input.KeyName = aws.String(machine.MachineConfig.KeyName) @@ -279,6 +282,32 @@ func (s *Service) createInstance(machine *actuators.MachineScope, bootstrapToken return out, nil } +func (s *Service) GetCoreSecurityGroups(machine *actuators.MachineScope) ([]string, error) { + // These are common across both controlplane and node machines + sgRoles := []v1alpha1.SecurityGroupRole{ + v1alpha1.SecurityGroupNode, + v1alpha1.SecurityGroupLB, + } + switch machine.Role() { + case "node": + // Just the common security groups above + case "controlplane": + sgRoles = append(sgRoles, v1alpha1.SecurityGroupControlPlane) + default: + return nil, errors.Errorf("Unknown node role %q", machine.Role()) + } + ids := make([]string, 0, len(sgRoles)) + for _, sg := range sgRoles { + if s.scope.SecurityGroups()[sg] == nil { + return nil, awserrors.NewFailedDependency( + errors.Errorf("%s security group not available", sg), + ) + } + ids = append(ids, s.scope.SecurityGroups()[sg].ID) + } + return ids, nil +} + // TerminateInstance terminates an EC2 instance. // Returns nil on success, error in all other cases. func (s *Service) TerminateInstance(instanceID string) error { @@ -455,6 +484,25 @@ func (a *awslog) Log(args ...interface{}) { a.WithName("aws-logger").Info("AWS context", args...) } +// GetInstanceSecurityGroups returns a map from ENI id to the security groups applied to that ENI +// While some security group operations take place at the "instance" level, these are in fact an API convenience for manipulating the first ("primary") ENI's properties. +func (s *Service) GetInstanceSecurityGroups(instanceID string) (map[string][]string, error) { + enis, err := s.getInstanceENIs(instanceID) + if err != nil { + return nil, errors.Wrapf(err, "failed to get ENIs for instance %q", instanceID) + } + + out := make(map[string][]string) + for _, eni := range enis { + var groups []string + for _, group := range eni.Groups { + groups = append(groups, aws.StringValue(group.GroupId)) + } + out[aws.StringValue(eni.NetworkInterfaceId)] = groups + } + return out, nil +} + // UpdateInstanceSecurityGroups modifies the security groups of the given // EC2 instance. func (s *Service) UpdateInstanceSecurityGroups(instanceID string, ids []string) error { diff --git a/pkg/cloud/aws/services/ec2/instances_test.go b/pkg/cloud/aws/services/ec2/instances_test.go index 8a41ad77ab..9a762d2f1c 100644 --- a/pkg/cloud/aws/services/ec2/instances_test.go +++ b/pkg/cloud/aws/services/ec2/instances_test.go @@ -314,6 +314,9 @@ vuO9LYxDXLVY9F7W4ccyCqe27Cj1xyAvdZxwhITrib8Wg5CMqoRpqTw5V3+TpA== v1alpha1.SecurityGroupNode: { ID: "2", }, + v1alpha1.SecurityGroupLB: { + ID: "3", + }, }, APIServerELB: v1alpha1.ClassicELB{ DNSName: "test-apiserver.us-east-1.aws", diff --git a/pkg/cloud/aws/services/ec2/natgateways.go b/pkg/cloud/aws/services/ec2/natgateways.go index a6c8da14b4..465bac0da1 100644 --- a/pkg/cloud/aws/services/ec2/natgateways.go +++ b/pkg/cloud/aws/services/ec2/natgateways.go @@ -31,7 +31,7 @@ import ( ) func (s *Service) reconcileNatGateways() error { - if s.scope.VPC().IsProvided() { + if s.scope.VPC().IsUnmanaged(s.scope.Name()) { s.scope.V(4).Info("Skipping NAT gateway reconcile in unmanaged mode") return nil } @@ -82,7 +82,7 @@ func (s *Service) reconcileNatGateways() error { } func (s *Service) deleteNatGateways() error { - if s.scope.VPC().IsProvided() { + if s.scope.VPC().IsUnmanaged(s.scope.Name()) { s.scope.V(4).Info("Skipping NAT gateway deletion in unmanaged mode") return nil } diff --git a/pkg/cloud/aws/services/ec2/natgateways_test.go b/pkg/cloud/aws/services/ec2/natgateways_test.go index a840ac220d..b2b541a565 100644 --- a/pkg/cloud/aws/services/ec2/natgateways_test.go +++ b/pkg/cloud/aws/services/ec2/natgateways_test.go @@ -240,10 +240,6 @@ func TestReconcileNatGateways(t *testing.T) { NatGatewayId: aws.String("gateway"), SubnetId: aws.String("subnet-1"), Tags: []*ec2.Tag{ - { - Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/managed"), - Value: aws.String("true"), - }, { Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/role"), Value: aws.String("common"), @@ -253,7 +249,7 @@ func TestReconcileNatGateways(t *testing.T) { Value: aws.String("test-cluster-nat"), }, { - Key: aws.String("kubernetes.io/cluster/test-cluster"), + Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"), Value: aws.String("owned"), }, }, @@ -309,7 +305,7 @@ func TestReconcileNatGateways(t *testing.T) { VPC: v1alpha1.VPCSpec{ ID: subnetsVPCID, Tags: tags.Map{ - tags.NameAWSProviderManaged: "true", + tags.ClusterKey("test-cluster"): "owned", }, }, Subnets: tc.input, diff --git a/pkg/cloud/aws/services/ec2/routetables.go b/pkg/cloud/aws/services/ec2/routetables.go index 1e7795fbb6..b43f782032 100644 --- a/pkg/cloud/aws/services/ec2/routetables.go +++ b/pkg/cloud/aws/services/ec2/routetables.go @@ -33,7 +33,7 @@ const ( ) func (s *Service) reconcileRouteTables() error { - if s.scope.VPC().IsProvided() { + if s.scope.VPC().IsUnmanaged(s.scope.Name()) { s.scope.V(4).Info("Skipping routing tables reconcile in unmanaged mode") return nil } @@ -121,7 +121,7 @@ func (s *Service) describeVpcRouteTablesBySubnet() (map[string]*ec2.RouteTable, } func (s *Service) deleteRouteTables() error { - if s.scope.VPC().IsProvided() { + if s.scope.VPC().IsUnmanaged(s.scope.Name()) { s.scope.V(4).Info("Skipping routing tables deletion in unmanaged mode") return nil } @@ -158,7 +158,7 @@ func (s *Service) describeVpcRouteTables() ([]*ec2.RouteTable, error) { filter.EC2.VPC(s.scope.VPC().ID), } - if !s.scope.VPC().IsProvided() { + if !s.scope.VPC().IsUnmanaged(s.scope.Name()) { filters = append(filters, filter.EC2.Cluster(s.scope.Name())) } diff --git a/pkg/cloud/aws/services/ec2/routetables_test.go b/pkg/cloud/aws/services/ec2/routetables_test.go index 16cbfcf7a0..cc8ddd284d 100644 --- a/pkg/cloud/aws/services/ec2/routetables_test.go +++ b/pkg/cloud/aws/services/ec2/routetables_test.go @@ -50,7 +50,7 @@ func TestReconcileRouteTables(t *testing.T) { ID: "vpc-routetables", InternetGatewayID: aws.String("igw-01"), Tags: tags.Map{ - tags.NameAWSProviderManaged: "true", + tags.ClusterKey("test-cluster"): "owned", }, }, Subnets: v1alpha1.Subnets{ @@ -119,7 +119,7 @@ func TestReconcileRouteTables(t *testing.T) { InternetGatewayID: aws.String("igw-01"), ID: "vpc-routetables", Tags: tags.Map{ - tags.NameAWSProviderManaged: "true", + tags.ClusterKey("test-cluster"): "owned", }, }, Subnets: v1alpha1.Subnets{ diff --git a/pkg/cloud/aws/services/ec2/securitygroups.go b/pkg/cloud/aws/services/ec2/securitygroups.go index 004a92a479..031460dc4f 100644 --- a/pkg/cloud/aws/services/ec2/securitygroups.go +++ b/pkg/cloud/aws/services/ec2/securitygroups.go @@ -61,6 +61,7 @@ func (s *Service) reconcileSecurityGroups() error { v1alpha1.SecurityGroupBastion, v1alpha1.SecurityGroupControlPlane, v1alpha1.SecurityGroupNode, + v1alpha1.SecurityGroupLB, } // First iteration makes sure that the security group are valid and fully created. @@ -98,6 +99,10 @@ func (s *Service) reconcileSecurityGroups() error { // Second iteration creates or updates all permissions on the security group to match // the specified ingress rules. for role, sg := range s.scope.SecurityGroups() { + if sg.Tags.HasAWSCloudProviderOwned(s.scope.Name()) { + // skip rule reconciliation, as we expect the in-cluster cloud integration to manage them + continue + } current := sg.IngressRules want, err := s.getSecurityGroupIngressRules(role) @@ -338,6 +343,9 @@ func (s *Service) getSecurityGroupIngressRules(role v1alpha1.SecurityGroupRole) }, }, }, nil + case v1alpha1.SecurityGroupLB: + // We hand this group off to the in-cluster cloud provider, so these rules aren't used + return v1alpha1.IngressRules{}, nil } return nil, errors.Errorf("Cannot determine ingress rules for unknown security group role %q", role) @@ -358,11 +366,17 @@ func (s *Service) getDefaultSecurityGroup(role v1alpha1.SecurityGroupRole) *ec2. } func (s *Service) getSecurityGroupTagParams(name string, role v1alpha1.SecurityGroupRole) tags.BuildParams { + + additional := tags.Map{} + if role == v1alpha1.SecurityGroupLB { + additional[tags.ClusterAWSCloudProviderKey(s.scope.Name())] = string(tags.ResourceLifecycleOwned) + } return tags.BuildParams{ ClusterName: s.scope.Name(), Lifecycle: tags.ResourceLifecycleOwned, Name: aws.String(name), Role: aws.String(string(role)), + Additional: additional, } } diff --git a/pkg/cloud/aws/services/ec2/subnets.go b/pkg/cloud/aws/services/ec2/subnets.go index 05400b64aa..51622639b4 100644 --- a/pkg/cloud/aws/services/ec2/subnets.go +++ b/pkg/cloud/aws/services/ec2/subnets.go @@ -58,7 +58,7 @@ func (s *Service) reconcileSubnets() error { } if len(subnets.FilterPrivate()) == 0 { - if s.scope.VPC().IsProvided() { + if s.scope.VPC().IsUnmanaged(s.scope.Name()) { return errors.New("expected at least one private subnet available for use, got 0") } @@ -70,7 +70,7 @@ func (s *Service) reconcileSubnets() error { } if len(subnets.FilterPublic()) == 0 { - if s.scope.VPC().IsProvided() { + if s.scope.VPC().IsUnmanaged(s.scope.Name()) { return errors.New("expected at least one public subnet available for use, got 0") } @@ -89,7 +89,7 @@ LoopExisting: // Two subnets are defined equal to each other if their id is equal // or if they are in the same vpc and the cidr block is the same. if (sn.ID != "" && exsn.ID == sn.ID) || (sn.CidrBlock == exsn.CidrBlock) { - if s.scope.VPC().IsProvided() { + if s.scope.VPC().IsUnmanaged(s.scope.Name()) { // TODO(vincepri): Validate provided subnet passes some basic checks. exsn.DeepCopyInto(sn) continue LoopExisting @@ -116,7 +116,7 @@ LoopExisting: } // Proceed to create the rest of the subnets that don't have an ID. - if !s.scope.VPC().IsProvided() { + if !s.scope.VPC().IsUnmanaged(s.scope.Name()) { for _, subnet := range subnets { if subnet.ID != "" { continue @@ -136,7 +136,7 @@ LoopExisting: } func (s *Service) deleteSubnets() error { - if s.scope.VPC().IsProvided() { + if s.scope.VPC().IsUnmanaged(s.scope.Name()) { s.scope.V(4).Info("Skipping subnets deletion in unmanaged mode") return nil } diff --git a/pkg/cloud/aws/services/ec2/subnets_test.go b/pkg/cloud/aws/services/ec2/subnets_test.go index ecdf9932a4..6350406f4f 100644 --- a/pkg/cloud/aws/services/ec2/subnets_test.go +++ b/pkg/cloud/aws/services/ec2/subnets_test.go @@ -52,7 +52,7 @@ func TestReconcileSubnets(t *testing.T) { VPC: v1alpha1.VPCSpec{ ID: subnetsVPCID, Tags: tags.Map{ - tags.NameAWSProviderManaged: "true", + tags.ClusterKey("test-cluster"): "owned", }, }, Subnets: []*v1alpha1.SubnetSpec{ @@ -97,13 +97,9 @@ func TestReconcileSubnets(t *testing.T) { MapPublicIpOnLaunch: aws.Bool(false), Tags: []*ec2.Tag{ { - Key: aws.String("kubernetes.io/cluster/test-cluster"), + Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"), Value: aws.String("owned"), }, - { - Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/managed"), - Value: aws.String("true"), - }, { Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/role"), Value: aws.String("private"), @@ -171,7 +167,7 @@ func TestReconcileSubnets(t *testing.T) { VPC: v1alpha1.VPCSpec{ ID: subnetsVPCID, Tags: tags.Map{ - tags.NameAWSProviderManaged: "true", + tags.ClusterKey("test-cluster"): "owned", }, }, Subnets: []*v1alpha1.SubnetSpec{ @@ -280,7 +276,7 @@ func TestReconcileSubnets(t *testing.T) { VPC: v1alpha1.VPCSpec{ ID: subnetsVPCID, Tags: tags.Map{ - tags.NameAWSProviderManaged: "true", + tags.ClusterKey("test-cluster"): "owned", }, }, Subnets: []*v1alpha1.SubnetSpec{}, @@ -387,7 +383,7 @@ func TestReconcileSubnets(t *testing.T) { VPC: v1alpha1.VPCSpec{ ID: subnetsVPCID, Tags: tags.Map{ - tags.NameAWSProviderManaged: "true", + tags.ClusterKey("test-cluster"): "owned", }, }, Subnets: []*v1alpha1.SubnetSpec{ @@ -431,13 +427,9 @@ func TestReconcileSubnets(t *testing.T) { CidrBlock: aws.String("10.0.10.0/24"), Tags: []*ec2.Tag{ { - Key: aws.String("kubernetes.io/cluster/test-cluster"), + Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"), Value: aws.String("owned"), }, - { - Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/managed"), - Value: aws.String("true"), - }, { Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/role"), Value: aws.String("public"), diff --git a/pkg/cloud/aws/services/ec2/vpc.go b/pkg/cloud/aws/services/ec2/vpc.go index da3054e4d9..7007711b40 100644 --- a/pkg/cloud/aws/services/ec2/vpc.go +++ b/pkg/cloud/aws/services/ec2/vpc.go @@ -50,7 +50,7 @@ func (s *Service) reconcileVPC() error { return errors.Wrap(err, "failed to describe VPCs") } - if vpc.IsProvided() { + if vpc.IsUnmanaged(s.scope.Name()) { vpc.DeepCopyInto(s.scope.VPC()) s.scope.V(2).Info("Working on unmanaged VPC", "vpc-id", vpc.ID) return nil @@ -72,7 +72,7 @@ func (s *Service) reconcileVPC() error { } func (s *Service) createVPC() (*v1alpha1.VPCSpec, error) { - if s.scope.VPC().IsProvided() { + if s.scope.VPC().IsUnmanaged(s.scope.Name()) { return nil, errors.Errorf("cannot create a managed vpc in unmanaged mode") } @@ -117,7 +117,7 @@ func (s *Service) createVPC() (*v1alpha1.VPCSpec, error) { func (s *Service) deleteVPC() error { vpc := s.scope.VPC() - if vpc.IsProvided() { + if vpc.IsUnmanaged(s.scope.Name()) { s.scope.V(4).Info("Skipping VPC deletion in unmanaged mode") return nil } diff --git a/pkg/cloud/aws/services/ec2/vpc_test.go b/pkg/cloud/aws/services/ec2/vpc_test.go index 6f584f1698..d7a291642f 100644 --- a/pkg/cloud/aws/services/ec2/vpc_test.go +++ b/pkg/cloud/aws/services/ec2/vpc_test.go @@ -73,13 +73,9 @@ func TestReconcileVPC(t *testing.T) { Value: aws.String("test-cluster-vpc"), }, { - Key: aws.String("kubernetes.io/cluster/test-cluster"), + Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"), Value: aws.String("owned"), }, - { - Key: aws.String("sigs.k8s.io/cluster-api-provider-aws/managed"), - Value: aws.String("true"), - }, }, }, }, @@ -101,7 +97,7 @@ func TestReconcileVPC(t *testing.T) { }, { Name: aws.String("tag-key"), - Values: aws.StringSlice([]string{"kubernetes.io/cluster/test-cluster"}), + Values: aws.StringSlice([]string{"sigs.k8s.io/cluster-api-provider-aws/cluster/test-cluster"}), }, }, })). diff --git a/pkg/cloud/aws/services/interfaces.go b/pkg/cloud/aws/services/interfaces.go index b2a590844b..30f7cccce7 100644 --- a/pkg/cloud/aws/services/interfaces.go +++ b/pkg/cloud/aws/services/interfaces.go @@ -64,6 +64,8 @@ type EC2ClusterInterface interface { type EC2MachineInterface interface { InstanceIfExists(id *string) (*providerv1.Instance, error) TerminateInstance(id string) error + GetCoreSecurityGroups(machine *actuators.MachineScope) ([]string, error) + GetInstanceSecurityGroups(id string) (map[string][]string, error) CreateOrGetMachine(machine *actuators.MachineScope, token, kubeConfig string) (*providerv1.Instance, error) UpdateInstanceSecurityGroups(id string, securityGroups []string) error UpdateResourceTags(resourceID *string, create map[string]string, remove map[string]string) error diff --git a/pkg/cloud/aws/services/mocks/services_mock.go b/pkg/cloud/aws/services/mocks/services_mock.go index 2a41b98cd8..ca789def2c 100755 --- a/pkg/cloud/aws/services/mocks/services_mock.go +++ b/pkg/cloud/aws/services/mocks/services_mock.go @@ -93,6 +93,36 @@ func (mr *MockEC2InterfaceMockRecorder) DeleteNetwork() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNetwork", reflect.TypeOf((*MockEC2Interface)(nil).DeleteNetwork)) } +// GetCoreSecurityGroups mocks base method +func (m *MockEC2Interface) GetCoreSecurityGroups(arg0 *actuators.MachineScope) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCoreSecurityGroups", arg0) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCoreSecurityGroups indicates an expected call of GetCoreSecurityGroups +func (mr *MockEC2InterfaceMockRecorder) GetCoreSecurityGroups(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCoreSecurityGroups", reflect.TypeOf((*MockEC2Interface)(nil).GetCoreSecurityGroups), arg0) +} + +// GetInstanceSecurityGroups mocks base method +func (m *MockEC2Interface) GetInstanceSecurityGroups(arg0 string) (map[string][]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetInstanceSecurityGroups", arg0) + ret0, _ := ret[0].(map[string][]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetInstanceSecurityGroups indicates an expected call of GetInstanceSecurityGroups +func (mr *MockEC2InterfaceMockRecorder) GetInstanceSecurityGroups(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInstanceSecurityGroups", reflect.TypeOf((*MockEC2Interface)(nil).GetInstanceSecurityGroups), arg0) +} + // InstanceIfExists mocks base method func (m *MockEC2Interface) InstanceIfExists(arg0 *string) (*v1alpha1.Instance, error) { m.ctrl.T.Helper() diff --git a/pkg/cloud/aws/tags/cluster.go b/pkg/cloud/aws/tags/cluster.go index 2aba49c67a..a485243fb8 100644 --- a/pkg/cloud/aws/tags/cluster.go +++ b/pkg/cloud/aws/tags/cluster.go @@ -20,5 +20,10 @@ import "fmt" // ClusterKey generates the key for resources associated with a cluster. func ClusterKey(name string) string { - return fmt.Sprintf("%s%s", NameKubernetesClusterPrefix, name) + return fmt.Sprintf("%s%s", NameAWSProviderOwned, name) +} + +// ClusterAWSCloudProviderKey generates the key for resources associated a cluster's AWS cloud provider. +func ClusterAWSCloudProviderKey(name string) string { + return fmt.Sprintf("%s%s", NameKubernetesAWSCloudProviderPrefix, name) } diff --git a/pkg/cloud/aws/tags/tags.go b/pkg/cloud/aws/tags/tags.go index 9415348795..66d7f57df7 100644 --- a/pkg/cloud/aws/tags/tags.go +++ b/pkg/cloud/aws/tags/tags.go @@ -92,10 +92,6 @@ func Build(params BuildParams) Map { } tags[ClusterKey(params.ClusterName)] = string(params.Lifecycle) - if params.Lifecycle == ResourceLifecycleOwned { - tags[NameAWSProviderManaged] = "true" - } - if params.Role != nil { tags[NameAWSClusterAPIRole] = *params.Role } diff --git a/pkg/cloud/aws/tags/types.go b/pkg/cloud/aws/tags/types.go index 9d1c79468d..4d3af869fb 100644 --- a/pkg/cloud/aws/tags/types.go +++ b/pkg/cloud/aws/tags/types.go @@ -17,7 +17,6 @@ limitations under the License. package tags import ( - "path" "reflect" ) @@ -29,16 +28,16 @@ func (m Map) Equals(other Map) bool { return reflect.DeepEqual(m, other) } -// HasOwned returns true if the tags contains a tag that marks the resource as owned by the cluster. +// HasOwned returns true if the tags contains a tag that marks the resource as owned by the cluster from the perspective of this management tooling. func (m Map) HasOwned(cluster string) bool { - value, ok := m[path.Join(NameKubernetesClusterPrefix, cluster)] + value, ok := m[ClusterKey(cluster)] return ok && ResourceLifecycle(value) == ResourceLifecycleOwned } -// HasManaged returns true if the map contains NameAWSProviderManaged key set to true. -func (m Map) HasManaged() bool { - value, ok := m[NameAWSProviderManaged] - return ok && value == "true" +// HasOwned returns true if the tags contains a tag that marks the resource as owned by the cluster from the perspective of the in-tree cloud provider. +func (m Map) HasAWSCloudProviderOwned(cluster string) bool { + value, ok := m[ClusterAWSCloudProviderKey(cluster)] + return ok && ResourceLifecycle(value) == ResourceLifecycleOwned } // GetRole returns the Cluster API role for the tagged resource @@ -75,21 +74,23 @@ const ( // if the cluster is destroyed. ResourceLifecycleShared = ResourceLifecycle("shared") - // NameKubernetesClusterPrefix is the tag name we use to differentiate multiple + // NameKubernetesClusterPrefix is the tag name used by the cloud provider to logically + // separate independent cluster resources. We use it to identify which resources we expect + // to be permissive about state changes. // logically independent clusters running in the same AZ. - // The tag key = NameKubernetesClusterPrefix + clusterID + // The tag key = NameKubernetesAWSCloudProviderPrefix + clusterID // The tag value is an ownership value - NameKubernetesClusterPrefix = "kubernetes.io/cluster/" + NameKubernetesAWSCloudProviderPrefix = "kubernetes.io/cluster/" // NameAWSProviderPrefix is the tag prefix we use to differentiate // cluster-api-provider-aws owned components from other tooling that // uses NameKubernetesClusterPrefix NameAWSProviderPrefix = "sigs.k8s.io/cluster-api-provider-aws/" - // NameAWSProviderManaged is the tag name we use to differentiate + // NameAWSProviderOwned is the tag name we use to differentiate // cluster-api-provider-aws owned components from other tooling that // uses NameKubernetesClusterPrefix - NameAWSProviderManaged = NameAWSProviderPrefix + "managed" + NameAWSProviderOwned = NameAWSProviderPrefix + "cluster/" // NameAWSClusterAPIRole is the tag name we use to mark roles for resources // dedicated to this cluster api provider implementation. diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/BUILD.bazel b/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/BUILD.bazel new file mode 100644 index 0000000000..d911365971 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/BUILD.bazel @@ -0,0 +1,16 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "build.go", + "unmarshal.go", + ], + importmap = "sigs.k8s.io/cluster-api-provider-aws/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil", + importpath = "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/aws/aws-sdk-go/aws:go_default_library", + "//vendor/github.com/aws/aws-sdk-go/private/protocol:go_default_library", + ], +) diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go new file mode 100644 index 0000000000..864fb6704b --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/build.go @@ -0,0 +1,296 @@ +// Package jsonutil provides JSON serialization of AWS requests and responses. +package jsonutil + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "math" + "reflect" + "sort" + "strconv" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/private/protocol" +) + +var timeType = reflect.ValueOf(time.Time{}).Type() +var byteSliceType = reflect.ValueOf([]byte{}).Type() + +// BuildJSON builds a JSON string for a given object v. +func BuildJSON(v interface{}) ([]byte, error) { + var buf bytes.Buffer + + err := buildAny(reflect.ValueOf(v), &buf, "") + return buf.Bytes(), err +} + +func buildAny(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error { + origVal := value + value = reflect.Indirect(value) + if !value.IsValid() { + return nil + } + + vtype := value.Type() + + t := tag.Get("type") + if t == "" { + switch vtype.Kind() { + case reflect.Struct: + // also it can't be a time object + if value.Type() != timeType { + t = "structure" + } + case reflect.Slice: + // also it can't be a byte slice + if _, ok := value.Interface().([]byte); !ok { + t = "list" + } + case reflect.Map: + // cannot be a JSONValue map + if _, ok := value.Interface().(aws.JSONValue); !ok { + t = "map" + } + } + } + + switch t { + case "structure": + if field, ok := vtype.FieldByName("_"); ok { + tag = field.Tag + } + return buildStruct(value, buf, tag) + case "list": + return buildList(value, buf, tag) + case "map": + return buildMap(value, buf, tag) + default: + return buildScalar(origVal, buf, tag) + } +} + +func buildStruct(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error { + if !value.IsValid() { + return nil + } + + // unwrap payloads + if payload := tag.Get("payload"); payload != "" { + field, _ := value.Type().FieldByName(payload) + tag = field.Tag + value = elemOf(value.FieldByName(payload)) + + if !value.IsValid() { + return nil + } + } + + buf.WriteByte('{') + + t := value.Type() + first := true + for i := 0; i < t.NumField(); i++ { + member := value.Field(i) + + // This allocates the most memory. + // Additionally, we cannot skip nil fields due to + // idempotency auto filling. + field := t.Field(i) + + if field.PkgPath != "" { + continue // ignore unexported fields + } + if field.Tag.Get("json") == "-" { + continue + } + if field.Tag.Get("location") != "" { + continue // ignore non-body elements + } + if field.Tag.Get("ignore") != "" { + continue + } + + if protocol.CanSetIdempotencyToken(member, field) { + token := protocol.GetIdempotencyToken() + member = reflect.ValueOf(&token) + } + + if (member.Kind() == reflect.Ptr || member.Kind() == reflect.Slice || member.Kind() == reflect.Map) && member.IsNil() { + continue // ignore unset fields + } + + if first { + first = false + } else { + buf.WriteByte(',') + } + + // figure out what this field is called + name := field.Name + if locName := field.Tag.Get("locationName"); locName != "" { + name = locName + } + + writeString(name, buf) + buf.WriteString(`:`) + + err := buildAny(member, buf, field.Tag) + if err != nil { + return err + } + + } + + buf.WriteString("}") + + return nil +} + +func buildList(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error { + buf.WriteString("[") + + for i := 0; i < value.Len(); i++ { + buildAny(value.Index(i), buf, "") + + if i < value.Len()-1 { + buf.WriteString(",") + } + } + + buf.WriteString("]") + + return nil +} + +type sortedValues []reflect.Value + +func (sv sortedValues) Len() int { return len(sv) } +func (sv sortedValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } +func (sv sortedValues) Less(i, j int) bool { return sv[i].String() < sv[j].String() } + +func buildMap(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error { + buf.WriteString("{") + + sv := sortedValues(value.MapKeys()) + sort.Sort(sv) + + for i, k := range sv { + if i > 0 { + buf.WriteByte(',') + } + + writeString(k.String(), buf) + buf.WriteString(`:`) + + buildAny(value.MapIndex(k), buf, "") + } + + buf.WriteString("}") + + return nil +} + +func buildScalar(v reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error { + // prevents allocation on the heap. + scratch := [64]byte{} + switch value := reflect.Indirect(v); value.Kind() { + case reflect.String: + writeString(value.String(), buf) + case reflect.Bool: + if value.Bool() { + buf.WriteString("true") + } else { + buf.WriteString("false") + } + case reflect.Int64: + buf.Write(strconv.AppendInt(scratch[:0], value.Int(), 10)) + case reflect.Float64: + f := value.Float() + if math.IsInf(f, 0) || math.IsNaN(f) { + return &json.UnsupportedValueError{Value: v, Str: strconv.FormatFloat(f, 'f', -1, 64)} + } + buf.Write(strconv.AppendFloat(scratch[:0], f, 'f', -1, 64)) + default: + switch converted := value.Interface().(type) { + case time.Time: + format := tag.Get("timestampFormat") + if len(format) == 0 { + format = protocol.UnixTimeFormatName + } + + ts := protocol.FormatTime(format, converted) + if format != protocol.UnixTimeFormatName { + ts = `"` + ts + `"` + } + + buf.WriteString(ts) + case []byte: + if !value.IsNil() { + buf.WriteByte('"') + if len(converted) < 1024 { + // for small buffers, using Encode directly is much faster. + dst := make([]byte, base64.StdEncoding.EncodedLen(len(converted))) + base64.StdEncoding.Encode(dst, converted) + buf.Write(dst) + } else { + // for large buffers, avoid unnecessary extra temporary + // buffer space. + enc := base64.NewEncoder(base64.StdEncoding, buf) + enc.Write(converted) + enc.Close() + } + buf.WriteByte('"') + } + case aws.JSONValue: + str, err := protocol.EncodeJSONValue(converted, protocol.QuotedEscape) + if err != nil { + return fmt.Errorf("unable to encode JSONValue, %v", err) + } + buf.WriteString(str) + default: + return fmt.Errorf("unsupported JSON value %v (%s)", value.Interface(), value.Type()) + } + } + return nil +} + +var hex = "0123456789abcdef" + +func writeString(s string, buf *bytes.Buffer) { + buf.WriteByte('"') + for i := 0; i < len(s); i++ { + if s[i] == '"' { + buf.WriteString(`\"`) + } else if s[i] == '\\' { + buf.WriteString(`\\`) + } else if s[i] == '\b' { + buf.WriteString(`\b`) + } else if s[i] == '\f' { + buf.WriteString(`\f`) + } else if s[i] == '\r' { + buf.WriteString(`\r`) + } else if s[i] == '\t' { + buf.WriteString(`\t`) + } else if s[i] == '\n' { + buf.WriteString(`\n`) + } else if s[i] < 32 { + buf.WriteString("\\u00") + buf.WriteByte(hex[s[i]>>4]) + buf.WriteByte(hex[s[i]&0xF]) + } else { + buf.WriteByte(s[i]) + } + } + buf.WriteByte('"') +} + +// Returns the reflection element of a value, if it is a pointer. +func elemOf(value reflect.Value) reflect.Value { + for value.Kind() == reflect.Ptr { + value = value.Elem() + } + return value +} diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/unmarshal.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/unmarshal.go new file mode 100644 index 0000000000..b11f3ee45b --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil/unmarshal.go @@ -0,0 +1,228 @@ +package jsonutil + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "io" + "reflect" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/private/protocol" +) + +// UnmarshalJSON reads a stream and unmarshals the results in object v. +func UnmarshalJSON(v interface{}, stream io.Reader) error { + var out interface{} + + err := json.NewDecoder(stream).Decode(&out) + if err == io.EOF { + return nil + } else if err != nil { + return err + } + + return unmarshalAny(reflect.ValueOf(v), out, "") +} + +func unmarshalAny(value reflect.Value, data interface{}, tag reflect.StructTag) error { + vtype := value.Type() + if vtype.Kind() == reflect.Ptr { + vtype = vtype.Elem() // check kind of actual element type + } + + t := tag.Get("type") + if t == "" { + switch vtype.Kind() { + case reflect.Struct: + // also it can't be a time object + if _, ok := value.Interface().(*time.Time); !ok { + t = "structure" + } + case reflect.Slice: + // also it can't be a byte slice + if _, ok := value.Interface().([]byte); !ok { + t = "list" + } + case reflect.Map: + // cannot be a JSONValue map + if _, ok := value.Interface().(aws.JSONValue); !ok { + t = "map" + } + } + } + + switch t { + case "structure": + if field, ok := vtype.FieldByName("_"); ok { + tag = field.Tag + } + return unmarshalStruct(value, data, tag) + case "list": + return unmarshalList(value, data, tag) + case "map": + return unmarshalMap(value, data, tag) + default: + return unmarshalScalar(value, data, tag) + } +} + +func unmarshalStruct(value reflect.Value, data interface{}, tag reflect.StructTag) error { + if data == nil { + return nil + } + mapData, ok := data.(map[string]interface{}) + if !ok { + return fmt.Errorf("JSON value is not a structure (%#v)", data) + } + + t := value.Type() + if value.Kind() == reflect.Ptr { + if value.IsNil() { // create the structure if it's nil + s := reflect.New(value.Type().Elem()) + value.Set(s) + value = s + } + + value = value.Elem() + t = t.Elem() + } + + // unwrap any payloads + if payload := tag.Get("payload"); payload != "" { + field, _ := t.FieldByName(payload) + return unmarshalAny(value.FieldByName(payload), data, field.Tag) + } + + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + if field.PkgPath != "" { + continue // ignore unexported fields + } + + // figure out what this field is called + name := field.Name + if locName := field.Tag.Get("locationName"); locName != "" { + name = locName + } + + member := value.FieldByIndex(field.Index) + err := unmarshalAny(member, mapData[name], field.Tag) + if err != nil { + return err + } + } + return nil +} + +func unmarshalList(value reflect.Value, data interface{}, tag reflect.StructTag) error { + if data == nil { + return nil + } + listData, ok := data.([]interface{}) + if !ok { + return fmt.Errorf("JSON value is not a list (%#v)", data) + } + + if value.IsNil() { + l := len(listData) + value.Set(reflect.MakeSlice(value.Type(), l, l)) + } + + for i, c := range listData { + err := unmarshalAny(value.Index(i), c, "") + if err != nil { + return err + } + } + + return nil +} + +func unmarshalMap(value reflect.Value, data interface{}, tag reflect.StructTag) error { + if data == nil { + return nil + } + mapData, ok := data.(map[string]interface{}) + if !ok { + return fmt.Errorf("JSON value is not a map (%#v)", data) + } + + if value.IsNil() { + value.Set(reflect.MakeMap(value.Type())) + } + + for k, v := range mapData { + kvalue := reflect.ValueOf(k) + vvalue := reflect.New(value.Type().Elem()).Elem() + + unmarshalAny(vvalue, v, "") + value.SetMapIndex(kvalue, vvalue) + } + + return nil +} + +func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTag) error { + + switch d := data.(type) { + case nil: + return nil // nothing to do here + case string: + switch value.Interface().(type) { + case *string: + value.Set(reflect.ValueOf(&d)) + case []byte: + b, err := base64.StdEncoding.DecodeString(d) + if err != nil { + return err + } + value.Set(reflect.ValueOf(b)) + case *time.Time: + format := tag.Get("timestampFormat") + if len(format) == 0 { + format = protocol.ISO8601TimeFormatName + } + + t, err := protocol.ParseTime(format, d) + if err != nil { + return err + } + value.Set(reflect.ValueOf(&t)) + case aws.JSONValue: + // No need to use escaping as the value is a non-quoted string. + v, err := protocol.DecodeJSONValue(d, protocol.NoEscape) + if err != nil { + return err + } + value.Set(reflect.ValueOf(v)) + default: + return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type()) + } + case float64: + switch value.Interface().(type) { + case *int64: + di := int64(d) + value.Set(reflect.ValueOf(&di)) + case *float64: + value.Set(reflect.ValueOf(&d)) + case *time.Time: + // Time unmarshaled from a float64 can only be epoch seconds + t := time.Unix(int64(d), 0).UTC() + value.Set(reflect.ValueOf(&t)) + default: + return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type()) + } + case bool: + switch value.Interface().(type) { + case *bool: + value.Set(reflect.ValueOf(&d)) + default: + return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type()) + } + default: + return fmt.Errorf("unsupported JSON value (%v)", data) + } + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/BUILD.bazel b/vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/BUILD.bazel new file mode 100644 index 0000000000..100428c016 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/BUILD.bazel @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["jsonrpc.go"], + importmap = "sigs.k8s.io/cluster-api-provider-aws/vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc", + importpath = "github.com/aws/aws-sdk-go/private/protocol/jsonrpc", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/aws/aws-sdk-go/aws/awserr:go_default_library", + "//vendor/github.com/aws/aws-sdk-go/aws/request:go_default_library", + "//vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil:go_default_library", + "//vendor/github.com/aws/aws-sdk-go/private/protocol/rest:go_default_library", + ], +) diff --git a/vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/jsonrpc.go b/vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/jsonrpc.go new file mode 100644 index 0000000000..36ceab088c --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc/jsonrpc.go @@ -0,0 +1,118 @@ +// Package jsonrpc provides JSON RPC utilities for serialization of AWS +// requests and responses. +package jsonrpc + +//go:generate go run -tags codegen ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/input/json.json build_test.go +//go:generate go run -tags codegen ../../../models/protocol_tests/generate.go ../../../models/protocol_tests/output/json.json unmarshal_test.go + +import ( + "encoding/json" + "io" + "strings" + + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" + "github.com/aws/aws-sdk-go/private/protocol/rest" +) + +var emptyJSON = []byte("{}") + +// BuildHandler is a named request handler for building jsonrpc protocol requests +var BuildHandler = request.NamedHandler{Name: "awssdk.jsonrpc.Build", Fn: Build} + +// UnmarshalHandler is a named request handler for unmarshaling jsonrpc protocol requests +var UnmarshalHandler = request.NamedHandler{Name: "awssdk.jsonrpc.Unmarshal", Fn: Unmarshal} + +// UnmarshalMetaHandler is a named request handler for unmarshaling jsonrpc protocol request metadata +var UnmarshalMetaHandler = request.NamedHandler{Name: "awssdk.jsonrpc.UnmarshalMeta", Fn: UnmarshalMeta} + +// UnmarshalErrorHandler is a named request handler for unmarshaling jsonrpc protocol request errors +var UnmarshalErrorHandler = request.NamedHandler{Name: "awssdk.jsonrpc.UnmarshalError", Fn: UnmarshalError} + +// Build builds a JSON payload for a JSON RPC request. +func Build(req *request.Request) { + var buf []byte + var err error + if req.ParamsFilled() { + buf, err = jsonutil.BuildJSON(req.Params) + if err != nil { + req.Error = awserr.New("SerializationError", "failed encoding JSON RPC request", err) + return + } + } else { + buf = emptyJSON + } + + if req.ClientInfo.TargetPrefix != "" || string(buf) != "{}" { + req.SetBufferBody(buf) + } + + if req.ClientInfo.TargetPrefix != "" { + target := req.ClientInfo.TargetPrefix + "." + req.Operation.Name + req.HTTPRequest.Header.Add("X-Amz-Target", target) + } + + // Only set the content type if one is not already specified and an + // JSONVersion is specified. + if ct, v := req.HTTPRequest.Header.Get("Content-Type"), req.ClientInfo.JSONVersion; len(ct) == 0 && len(v) != 0 { + jsonVersion := req.ClientInfo.JSONVersion + req.HTTPRequest.Header.Set("Content-Type", "application/x-amz-json-"+jsonVersion) + } +} + +// Unmarshal unmarshals a response for a JSON RPC service. +func Unmarshal(req *request.Request) { + defer req.HTTPResponse.Body.Close() + if req.DataFilled() { + err := jsonutil.UnmarshalJSON(req.Data, req.HTTPResponse.Body) + if err != nil { + req.Error = awserr.NewRequestFailure( + awserr.New("SerializationError", "failed decoding JSON RPC response", err), + req.HTTPResponse.StatusCode, + req.RequestID, + ) + } + } + return +} + +// UnmarshalMeta unmarshals headers from a response for a JSON RPC service. +func UnmarshalMeta(req *request.Request) { + rest.UnmarshalMeta(req) +} + +// UnmarshalError unmarshals an error response for a JSON RPC service. +func UnmarshalError(req *request.Request) { + defer req.HTTPResponse.Body.Close() + + var jsonErr jsonErrorResponse + err := json.NewDecoder(req.HTTPResponse.Body).Decode(&jsonErr) + if err == io.EOF { + req.Error = awserr.NewRequestFailure( + awserr.New("SerializationError", req.HTTPResponse.Status, nil), + req.HTTPResponse.StatusCode, + req.RequestID, + ) + return + } else if err != nil { + req.Error = awserr.NewRequestFailure( + awserr.New("SerializationError", "failed decoding JSON RPC error response", err), + req.HTTPResponse.StatusCode, + req.RequestID, + ) + return + } + + codes := strings.SplitN(jsonErr.Code, "#", 2) + req.Error = awserr.NewRequestFailure( + awserr.New(codes[len(codes)-1], jsonErr.Message, nil), + req.HTTPResponse.StatusCode, + req.RequestID, + ) +} + +type jsonErrorResponse struct { + Code string `json:"__type"` + Message string `json:"message"` +} diff --git a/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/BUILD.bazel b/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/BUILD.bazel new file mode 100644 index 0000000000..1c6b2ac07a --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "api.go", + "doc.go", + "errors.go", + "service.go", + ], + importmap = "sigs.k8s.io/cluster-api-provider-aws/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi", + importpath = "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/aws/aws-sdk-go/aws:go_default_library", + "//vendor/github.com/aws/aws-sdk-go/aws/awsutil:go_default_library", + "//vendor/github.com/aws/aws-sdk-go/aws/client:go_default_library", + "//vendor/github.com/aws/aws-sdk-go/aws/client/metadata:go_default_library", + "//vendor/github.com/aws/aws-sdk-go/aws/request:go_default_library", + "//vendor/github.com/aws/aws-sdk-go/aws/signer/v4:go_default_library", + "//vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc:go_default_library", + ], +) diff --git a/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/api.go b/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/api.go new file mode 100644 index 0000000000..351a636300 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/api.go @@ -0,0 +1,1311 @@ +// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. + +package resourcegroupstaggingapi + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awsutil" + "github.com/aws/aws-sdk-go/aws/request" +) + +const opGetResources = "GetResources" + +// GetResourcesRequest generates a "aws/request.Request" representing the +// client's request for the GetResources operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See GetResources for more information on using the GetResources +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the GetResourcesRequest method. +// req, resp := client.GetResourcesRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/resourcegroupstaggingapi-2017-01-26/GetResources +func (c *ResourceGroupsTaggingAPI) GetResourcesRequest(input *GetResourcesInput) (req *request.Request, output *GetResourcesOutput) { + op := &request.Operation{ + Name: opGetResources, + HTTPMethod: "POST", + HTTPPath: "/", + Paginator: &request.Paginator{ + InputTokens: []string{"PaginationToken"}, + OutputTokens: []string{"PaginationToken"}, + LimitToken: "ResourcesPerPage", + TruncationToken: "", + }, + } + + if input == nil { + input = &GetResourcesInput{} + } + + output = &GetResourcesOutput{} + req = c.newRequest(op, input, output) + return +} + +// GetResources API operation for AWS Resource Groups Tagging API. +// +// Returns all the tagged resources that are associated with the specified tags +// (keys and values) located in the specified region for the AWS account. The +// tags and the resource types that you specify in the request are known as +// filters. The response includes all tags that are associated with the requested +// resources. If no filter is provided, this action returns a paginated resource +// list with the associated tags. +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for AWS Resource Groups Tagging API's +// API operation GetResources for usage and error information. +// +// Returned Error Codes: +// * ErrCodeInvalidParameterException "InvalidParameterException" +// A parameter is missing or a malformed string or invalid or out-of-range value +// was supplied for the request parameter. +// +// * ErrCodeThrottledException "ThrottledException" +// The request was denied to limit the frequency of submitted requests. +// +// * ErrCodeInternalServiceException "InternalServiceException" +// The request processing failed because of an unknown error, exception, or +// failure. You can retry the request. +// +// * ErrCodePaginationTokenExpiredException "PaginationTokenExpiredException" +// A PaginationToken is valid for a maximum of 15 minutes. Your request was +// denied because the specified PaginationToken has expired. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/resourcegroupstaggingapi-2017-01-26/GetResources +func (c *ResourceGroupsTaggingAPI) GetResources(input *GetResourcesInput) (*GetResourcesOutput, error) { + req, out := c.GetResourcesRequest(input) + return out, req.Send() +} + +// GetResourcesWithContext is the same as GetResources with the addition of +// the ability to pass a context and additional request options. +// +// See GetResources for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *ResourceGroupsTaggingAPI) GetResourcesWithContext(ctx aws.Context, input *GetResourcesInput, opts ...request.Option) (*GetResourcesOutput, error) { + req, out := c.GetResourcesRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() +} + +// GetResourcesPages iterates over the pages of a GetResources operation, +// calling the "fn" function with the response data for each page. To stop +// iterating, return false from the fn function. +// +// See GetResources method for more information on how to use this operation. +// +// Note: This operation can generate multiple requests to a service. +// +// // Example iterating over at most 3 pages of a GetResources operation. +// pageNum := 0 +// err := client.GetResourcesPages(params, +// func(page *GetResourcesOutput, lastPage bool) bool { +// pageNum++ +// fmt.Println(page) +// return pageNum <= 3 +// }) +// +func (c *ResourceGroupsTaggingAPI) GetResourcesPages(input *GetResourcesInput, fn func(*GetResourcesOutput, bool) bool) error { + return c.GetResourcesPagesWithContext(aws.BackgroundContext(), input, fn) +} + +// GetResourcesPagesWithContext same as GetResourcesPages except +// it takes a Context and allows setting request options on the pages. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *ResourceGroupsTaggingAPI) GetResourcesPagesWithContext(ctx aws.Context, input *GetResourcesInput, fn func(*GetResourcesOutput, bool) bool, opts ...request.Option) error { + p := request.Pagination{ + NewRequest: func() (*request.Request, error) { + var inCpy *GetResourcesInput + if input != nil { + tmp := *input + inCpy = &tmp + } + req, _ := c.GetResourcesRequest(inCpy) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return req, nil + }, + } + + cont := true + for p.Next() && cont { + cont = fn(p.Page().(*GetResourcesOutput), !p.HasNextPage()) + } + return p.Err() +} + +const opGetTagKeys = "GetTagKeys" + +// GetTagKeysRequest generates a "aws/request.Request" representing the +// client's request for the GetTagKeys operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See GetTagKeys for more information on using the GetTagKeys +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the GetTagKeysRequest method. +// req, resp := client.GetTagKeysRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/resourcegroupstaggingapi-2017-01-26/GetTagKeys +func (c *ResourceGroupsTaggingAPI) GetTagKeysRequest(input *GetTagKeysInput) (req *request.Request, output *GetTagKeysOutput) { + op := &request.Operation{ + Name: opGetTagKeys, + HTTPMethod: "POST", + HTTPPath: "/", + Paginator: &request.Paginator{ + InputTokens: []string{"PaginationToken"}, + OutputTokens: []string{"PaginationToken"}, + LimitToken: "", + TruncationToken: "", + }, + } + + if input == nil { + input = &GetTagKeysInput{} + } + + output = &GetTagKeysOutput{} + req = c.newRequest(op, input, output) + return +} + +// GetTagKeys API operation for AWS Resource Groups Tagging API. +// +// Returns all tag keys in the specified region for the AWS account. +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for AWS Resource Groups Tagging API's +// API operation GetTagKeys for usage and error information. +// +// Returned Error Codes: +// * ErrCodeInvalidParameterException "InvalidParameterException" +// A parameter is missing or a malformed string or invalid or out-of-range value +// was supplied for the request parameter. +// +// * ErrCodeThrottledException "ThrottledException" +// The request was denied to limit the frequency of submitted requests. +// +// * ErrCodeInternalServiceException "InternalServiceException" +// The request processing failed because of an unknown error, exception, or +// failure. You can retry the request. +// +// * ErrCodePaginationTokenExpiredException "PaginationTokenExpiredException" +// A PaginationToken is valid for a maximum of 15 minutes. Your request was +// denied because the specified PaginationToken has expired. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/resourcegroupstaggingapi-2017-01-26/GetTagKeys +func (c *ResourceGroupsTaggingAPI) GetTagKeys(input *GetTagKeysInput) (*GetTagKeysOutput, error) { + req, out := c.GetTagKeysRequest(input) + return out, req.Send() +} + +// GetTagKeysWithContext is the same as GetTagKeys with the addition of +// the ability to pass a context and additional request options. +// +// See GetTagKeys for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *ResourceGroupsTaggingAPI) GetTagKeysWithContext(ctx aws.Context, input *GetTagKeysInput, opts ...request.Option) (*GetTagKeysOutput, error) { + req, out := c.GetTagKeysRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() +} + +// GetTagKeysPages iterates over the pages of a GetTagKeys operation, +// calling the "fn" function with the response data for each page. To stop +// iterating, return false from the fn function. +// +// See GetTagKeys method for more information on how to use this operation. +// +// Note: This operation can generate multiple requests to a service. +// +// // Example iterating over at most 3 pages of a GetTagKeys operation. +// pageNum := 0 +// err := client.GetTagKeysPages(params, +// func(page *GetTagKeysOutput, lastPage bool) bool { +// pageNum++ +// fmt.Println(page) +// return pageNum <= 3 +// }) +// +func (c *ResourceGroupsTaggingAPI) GetTagKeysPages(input *GetTagKeysInput, fn func(*GetTagKeysOutput, bool) bool) error { + return c.GetTagKeysPagesWithContext(aws.BackgroundContext(), input, fn) +} + +// GetTagKeysPagesWithContext same as GetTagKeysPages except +// it takes a Context and allows setting request options on the pages. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *ResourceGroupsTaggingAPI) GetTagKeysPagesWithContext(ctx aws.Context, input *GetTagKeysInput, fn func(*GetTagKeysOutput, bool) bool, opts ...request.Option) error { + p := request.Pagination{ + NewRequest: func() (*request.Request, error) { + var inCpy *GetTagKeysInput + if input != nil { + tmp := *input + inCpy = &tmp + } + req, _ := c.GetTagKeysRequest(inCpy) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return req, nil + }, + } + + cont := true + for p.Next() && cont { + cont = fn(p.Page().(*GetTagKeysOutput), !p.HasNextPage()) + } + return p.Err() +} + +const opGetTagValues = "GetTagValues" + +// GetTagValuesRequest generates a "aws/request.Request" representing the +// client's request for the GetTagValues operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See GetTagValues for more information on using the GetTagValues +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the GetTagValuesRequest method. +// req, resp := client.GetTagValuesRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/resourcegroupstaggingapi-2017-01-26/GetTagValues +func (c *ResourceGroupsTaggingAPI) GetTagValuesRequest(input *GetTagValuesInput) (req *request.Request, output *GetTagValuesOutput) { + op := &request.Operation{ + Name: opGetTagValues, + HTTPMethod: "POST", + HTTPPath: "/", + Paginator: &request.Paginator{ + InputTokens: []string{"PaginationToken"}, + OutputTokens: []string{"PaginationToken"}, + LimitToken: "", + TruncationToken: "", + }, + } + + if input == nil { + input = &GetTagValuesInput{} + } + + output = &GetTagValuesOutput{} + req = c.newRequest(op, input, output) + return +} + +// GetTagValues API operation for AWS Resource Groups Tagging API. +// +// Returns all tag values for the specified key in the specified region for +// the AWS account. +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for AWS Resource Groups Tagging API's +// API operation GetTagValues for usage and error information. +// +// Returned Error Codes: +// * ErrCodeInvalidParameterException "InvalidParameterException" +// A parameter is missing or a malformed string or invalid or out-of-range value +// was supplied for the request parameter. +// +// * ErrCodeThrottledException "ThrottledException" +// The request was denied to limit the frequency of submitted requests. +// +// * ErrCodeInternalServiceException "InternalServiceException" +// The request processing failed because of an unknown error, exception, or +// failure. You can retry the request. +// +// * ErrCodePaginationTokenExpiredException "PaginationTokenExpiredException" +// A PaginationToken is valid for a maximum of 15 minutes. Your request was +// denied because the specified PaginationToken has expired. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/resourcegroupstaggingapi-2017-01-26/GetTagValues +func (c *ResourceGroupsTaggingAPI) GetTagValues(input *GetTagValuesInput) (*GetTagValuesOutput, error) { + req, out := c.GetTagValuesRequest(input) + return out, req.Send() +} + +// GetTagValuesWithContext is the same as GetTagValues with the addition of +// the ability to pass a context and additional request options. +// +// See GetTagValues for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *ResourceGroupsTaggingAPI) GetTagValuesWithContext(ctx aws.Context, input *GetTagValuesInput, opts ...request.Option) (*GetTagValuesOutput, error) { + req, out := c.GetTagValuesRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() +} + +// GetTagValuesPages iterates over the pages of a GetTagValues operation, +// calling the "fn" function with the response data for each page. To stop +// iterating, return false from the fn function. +// +// See GetTagValues method for more information on how to use this operation. +// +// Note: This operation can generate multiple requests to a service. +// +// // Example iterating over at most 3 pages of a GetTagValues operation. +// pageNum := 0 +// err := client.GetTagValuesPages(params, +// func(page *GetTagValuesOutput, lastPage bool) bool { +// pageNum++ +// fmt.Println(page) +// return pageNum <= 3 +// }) +// +func (c *ResourceGroupsTaggingAPI) GetTagValuesPages(input *GetTagValuesInput, fn func(*GetTagValuesOutput, bool) bool) error { + return c.GetTagValuesPagesWithContext(aws.BackgroundContext(), input, fn) +} + +// GetTagValuesPagesWithContext same as GetTagValuesPages except +// it takes a Context and allows setting request options on the pages. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *ResourceGroupsTaggingAPI) GetTagValuesPagesWithContext(ctx aws.Context, input *GetTagValuesInput, fn func(*GetTagValuesOutput, bool) bool, opts ...request.Option) error { + p := request.Pagination{ + NewRequest: func() (*request.Request, error) { + var inCpy *GetTagValuesInput + if input != nil { + tmp := *input + inCpy = &tmp + } + req, _ := c.GetTagValuesRequest(inCpy) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return req, nil + }, + } + + cont := true + for p.Next() && cont { + cont = fn(p.Page().(*GetTagValuesOutput), !p.HasNextPage()) + } + return p.Err() +} + +const opTagResources = "TagResources" + +// TagResourcesRequest generates a "aws/request.Request" representing the +// client's request for the TagResources operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See TagResources for more information on using the TagResources +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the TagResourcesRequest method. +// req, resp := client.TagResourcesRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/resourcegroupstaggingapi-2017-01-26/TagResources +func (c *ResourceGroupsTaggingAPI) TagResourcesRequest(input *TagResourcesInput) (req *request.Request, output *TagResourcesOutput) { + op := &request.Operation{ + Name: opTagResources, + HTTPMethod: "POST", + HTTPPath: "/", + } + + if input == nil { + input = &TagResourcesInput{} + } + + output = &TagResourcesOutput{} + req = c.newRequest(op, input, output) + return +} + +// TagResources API operation for AWS Resource Groups Tagging API. +// +// Applies one or more tags to the specified resources. Note the following: +// +// * Not all resources can have tags. For a list of resources that support +// tagging, see Supported Resources (http://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/supported-resources.html) +// in the AWS Resource Groups and Tag Editor User Guide. +// +// * Each resource can have up to 50 tags. For other limits, see Tag Restrictions +// (http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#tag-restrictions) +// in the Amazon EC2 User Guide for Linux Instances. +// +// * You can only tag resources that are located in the specified region +// for the AWS account. +// +// * To add tags to a resource, you need the necessary permissions for the +// service that the resource belongs to as well as permissions for adding +// tags. For more information, see Obtaining Permissions for Tagging (http://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/obtaining-permissions-for-tagging.html) +// in the AWS Resource Groups and Tag Editor User Guide. +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for AWS Resource Groups Tagging API's +// API operation TagResources for usage and error information. +// +// Returned Error Codes: +// * ErrCodeInvalidParameterException "InvalidParameterException" +// A parameter is missing or a malformed string or invalid or out-of-range value +// was supplied for the request parameter. +// +// * ErrCodeThrottledException "ThrottledException" +// The request was denied to limit the frequency of submitted requests. +// +// * ErrCodeInternalServiceException "InternalServiceException" +// The request processing failed because of an unknown error, exception, or +// failure. You can retry the request. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/resourcegroupstaggingapi-2017-01-26/TagResources +func (c *ResourceGroupsTaggingAPI) TagResources(input *TagResourcesInput) (*TagResourcesOutput, error) { + req, out := c.TagResourcesRequest(input) + return out, req.Send() +} + +// TagResourcesWithContext is the same as TagResources with the addition of +// the ability to pass a context and additional request options. +// +// See TagResources for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *ResourceGroupsTaggingAPI) TagResourcesWithContext(ctx aws.Context, input *TagResourcesInput, opts ...request.Option) (*TagResourcesOutput, error) { + req, out := c.TagResourcesRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() +} + +const opUntagResources = "UntagResources" + +// UntagResourcesRequest generates a "aws/request.Request" representing the +// client's request for the UntagResources operation. The "output" return +// value will be populated with the request's response once the request completes +// successfully. +// +// Use "Send" method on the returned Request to send the API call to the service. +// the "output" return value is not valid until after Send returns without error. +// +// See UntagResources for more information on using the UntagResources +// API call, and error handling. +// +// This method is useful when you want to inject custom logic or configuration +// into the SDK's request lifecycle. Such as custom headers, or retry logic. +// +// +// // Example sending a request using the UntagResourcesRequest method. +// req, resp := client.UntagResourcesRequest(params) +// +// err := req.Send() +// if err == nil { // resp is now filled +// fmt.Println(resp) +// } +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/resourcegroupstaggingapi-2017-01-26/UntagResources +func (c *ResourceGroupsTaggingAPI) UntagResourcesRequest(input *UntagResourcesInput) (req *request.Request, output *UntagResourcesOutput) { + op := &request.Operation{ + Name: opUntagResources, + HTTPMethod: "POST", + HTTPPath: "/", + } + + if input == nil { + input = &UntagResourcesInput{} + } + + output = &UntagResourcesOutput{} + req = c.newRequest(op, input, output) + return +} + +// UntagResources API operation for AWS Resource Groups Tagging API. +// +// Removes the specified tags from the specified resources. When you specify +// a tag key, the action removes both that key and its associated value. The +// operation succeeds even if you attempt to remove tags from a resource that +// were already removed. Note the following: +// +// * To remove tags from a resource, you need the necessary permissions for +// the service that the resource belongs to as well as permissions for removing +// tags. For more information, see Obtaining Permissions for Tagging (http://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/obtaining-permissions-for-tagging.html) +// in the AWS Resource Groups and Tag Editor User Guide. +// +// * You can only tag resources that are located in the specified region +// for the AWS account. +// +// Returns awserr.Error for service API and SDK errors. Use runtime type assertions +// with awserr.Error's Code and Message methods to get detailed information about +// the error. +// +// See the AWS API reference guide for AWS Resource Groups Tagging API's +// API operation UntagResources for usage and error information. +// +// Returned Error Codes: +// * ErrCodeInvalidParameterException "InvalidParameterException" +// A parameter is missing or a malformed string or invalid or out-of-range value +// was supplied for the request parameter. +// +// * ErrCodeThrottledException "ThrottledException" +// The request was denied to limit the frequency of submitted requests. +// +// * ErrCodeInternalServiceException "InternalServiceException" +// The request processing failed because of an unknown error, exception, or +// failure. You can retry the request. +// +// See also, https://docs.aws.amazon.com/goto/WebAPI/resourcegroupstaggingapi-2017-01-26/UntagResources +func (c *ResourceGroupsTaggingAPI) UntagResources(input *UntagResourcesInput) (*UntagResourcesOutput, error) { + req, out := c.UntagResourcesRequest(input) + return out, req.Send() +} + +// UntagResourcesWithContext is the same as UntagResources with the addition of +// the ability to pass a context and additional request options. +// +// See UntagResources for details on how to use this API operation. +// +// The context must be non-nil and will be used for request cancellation. If +// the context is nil a panic will occur. In the future the SDK may create +// sub-contexts for http.Requests. See https://golang.org/pkg/context/ +// for more information on using Contexts. +func (c *ResourceGroupsTaggingAPI) UntagResourcesWithContext(ctx aws.Context, input *UntagResourcesInput, opts ...request.Option) (*UntagResourcesOutput, error) { + req, out := c.UntagResourcesRequest(input) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return out, req.Send() +} + +// Details of the common errors that all actions return. +type FailureInfo struct { + _ struct{} `type:"structure"` + + // The code of the common error. Valid values include InternalServiceException, + // InvalidParameterException, and any valid error code returned by the AWS service + // that hosts the resource that you want to tag. + ErrorCode *string `type:"string" enum:"ErrorCode"` + + // The message of the common error. + ErrorMessage *string `type:"string"` + + // The HTTP status code of the common error. + StatusCode *int64 `type:"integer"` +} + +// String returns the string representation +func (s FailureInfo) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s FailureInfo) GoString() string { + return s.String() +} + +// SetErrorCode sets the ErrorCode field's value. +func (s *FailureInfo) SetErrorCode(v string) *FailureInfo { + s.ErrorCode = &v + return s +} + +// SetErrorMessage sets the ErrorMessage field's value. +func (s *FailureInfo) SetErrorMessage(v string) *FailureInfo { + s.ErrorMessage = &v + return s +} + +// SetStatusCode sets the StatusCode field's value. +func (s *FailureInfo) SetStatusCode(v int64) *FailureInfo { + s.StatusCode = &v + return s +} + +type GetResourcesInput struct { + _ struct{} `type:"structure"` + + // A string that indicates that additional data is available. Leave this value + // empty for your initial request. If the response includes a PaginationToken, + // use that string for this value to request an additional page of data. + PaginationToken *string `type:"string"` + + // The constraints on the resources that you want returned. The format of each + // resource type is service[:resourceType]. For example, specifying a resource + // type of ec2 returns all tagged Amazon EC2 resources (which includes tagged + // EC2 instances). Specifying a resource type of ec2:instance returns only EC2 + // instances. + // + // The string for each service name and resource type is the same as that embedded + // in a resource's Amazon Resource Name (ARN). Consult the AWS General Reference + // for the following: + // + // * For a list of service name strings, see AWS Service Namespaces (http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html#genref-aws-service-namespaces). + // + // * For resource type strings, see Example ARNs (http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html#arns-syntax). + // + // * For more information about ARNs, see Amazon Resource Names (ARNs) and + // AWS Service Namespaces (http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html). + ResourceTypeFilters []*string `type:"list"` + + // A limit that restricts the number of resources returned by GetResources in + // paginated output. You can set ResourcesPerPage to a minimum of 1 item and + // the maximum of 50 items. + ResourcesPerPage *int64 `type:"integer"` + + // A list of tags (keys and values). A request can include up to 50 keys, and + // each key can include up to 20 values. + // + // If you specify multiple filters connected by an AND operator in a single + // request, the response returns only those resources that are associated with + // every specified filter. + // + // If you specify multiple filters connected by an OR operator in a single request, + // the response returns all resources that are associated with at least one + // or possibly more of the specified filters. + TagFilters []*TagFilter `type:"list"` + + // A limit that restricts the number of tags (key and value pairs) returned + // by GetResources in paginated output. A resource with no tags is counted as + // having one tag (one key and value pair). + // + // GetResources does not split a resource and its associated tags across pages. + // If the specified TagsPerPage would cause such a break, a PaginationToken + // is returned in place of the affected resource and its tags. Use that token + // in another request to get the remaining data. For example, if you specify + // a TagsPerPage of 100 and the account has 22 resources with 10 tags each (meaning + // that each resource has 10 key and value pairs), the output will consist of + // 3 pages, with the first page displaying the first 10 resources, each with + // its 10 tags, the second page displaying the next 10 resources each with its + // 10 tags, and the third page displaying the remaining 2 resources, each with + // its 10 tags. + // + // You can set TagsPerPage + TagsPerPage *int64 `type:"integer"` +} + +// String returns the string representation +func (s GetResourcesInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s GetResourcesInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetResourcesInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetResourcesInput"} + if s.TagFilters != nil { + for i, v := range s.TagFilters { + if v == nil { + continue + } + if err := v.Validate(); err != nil { + invalidParams.AddNested(fmt.Sprintf("%s[%v]", "TagFilters", i), err.(request.ErrInvalidParams)) + } + } + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetPaginationToken sets the PaginationToken field's value. +func (s *GetResourcesInput) SetPaginationToken(v string) *GetResourcesInput { + s.PaginationToken = &v + return s +} + +// SetResourceTypeFilters sets the ResourceTypeFilters field's value. +func (s *GetResourcesInput) SetResourceTypeFilters(v []*string) *GetResourcesInput { + s.ResourceTypeFilters = v + return s +} + +// SetResourcesPerPage sets the ResourcesPerPage field's value. +func (s *GetResourcesInput) SetResourcesPerPage(v int64) *GetResourcesInput { + s.ResourcesPerPage = &v + return s +} + +// SetTagFilters sets the TagFilters field's value. +func (s *GetResourcesInput) SetTagFilters(v []*TagFilter) *GetResourcesInput { + s.TagFilters = v + return s +} + +// SetTagsPerPage sets the TagsPerPage field's value. +func (s *GetResourcesInput) SetTagsPerPage(v int64) *GetResourcesInput { + s.TagsPerPage = &v + return s +} + +type GetResourcesOutput struct { + _ struct{} `type:"structure"` + + // A string that indicates that the response contains more data than can be + // returned in a single response. To receive additional data, specify this string + // for the PaginationToken value in a subsequent request. + PaginationToken *string `type:"string"` + + // A list of resource ARNs and the tags (keys and values) associated with each. + ResourceTagMappingList []*ResourceTagMapping `type:"list"` +} + +// String returns the string representation +func (s GetResourcesOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s GetResourcesOutput) GoString() string { + return s.String() +} + +// SetPaginationToken sets the PaginationToken field's value. +func (s *GetResourcesOutput) SetPaginationToken(v string) *GetResourcesOutput { + s.PaginationToken = &v + return s +} + +// SetResourceTagMappingList sets the ResourceTagMappingList field's value. +func (s *GetResourcesOutput) SetResourceTagMappingList(v []*ResourceTagMapping) *GetResourcesOutput { + s.ResourceTagMappingList = v + return s +} + +type GetTagKeysInput struct { + _ struct{} `type:"structure"` + + // A string that indicates that additional data is available. Leave this value + // empty for your initial request. If the response includes a PaginationToken, + // use that string for this value to request an additional page of data. + PaginationToken *string `type:"string"` +} + +// String returns the string representation +func (s GetTagKeysInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s GetTagKeysInput) GoString() string { + return s.String() +} + +// SetPaginationToken sets the PaginationToken field's value. +func (s *GetTagKeysInput) SetPaginationToken(v string) *GetTagKeysInput { + s.PaginationToken = &v + return s +} + +type GetTagKeysOutput struct { + _ struct{} `type:"structure"` + + // A string that indicates that the response contains more data than can be + // returned in a single response. To receive additional data, specify this string + // for the PaginationToken value in a subsequent request. + PaginationToken *string `type:"string"` + + // A list of all tag keys in the AWS account. + TagKeys []*string `type:"list"` +} + +// String returns the string representation +func (s GetTagKeysOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s GetTagKeysOutput) GoString() string { + return s.String() +} + +// SetPaginationToken sets the PaginationToken field's value. +func (s *GetTagKeysOutput) SetPaginationToken(v string) *GetTagKeysOutput { + s.PaginationToken = &v + return s +} + +// SetTagKeys sets the TagKeys field's value. +func (s *GetTagKeysOutput) SetTagKeys(v []*string) *GetTagKeysOutput { + s.TagKeys = v + return s +} + +type GetTagValuesInput struct { + _ struct{} `type:"structure"` + + // The key for which you want to list all existing values in the specified region + // for the AWS account. + // + // Key is a required field + Key *string `min:"1" type:"string" required:"true"` + + // A string that indicates that additional data is available. Leave this value + // empty for your initial request. If the response includes a PaginationToken, + // use that string for this value to request an additional page of data. + PaginationToken *string `type:"string"` +} + +// String returns the string representation +func (s GetTagValuesInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s GetTagValuesInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetTagValuesInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetTagValuesInput"} + if s.Key == nil { + invalidParams.Add(request.NewErrParamRequired("Key")) + } + if s.Key != nil && len(*s.Key) < 1 { + invalidParams.Add(request.NewErrParamMinLen("Key", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetKey sets the Key field's value. +func (s *GetTagValuesInput) SetKey(v string) *GetTagValuesInput { + s.Key = &v + return s +} + +// SetPaginationToken sets the PaginationToken field's value. +func (s *GetTagValuesInput) SetPaginationToken(v string) *GetTagValuesInput { + s.PaginationToken = &v + return s +} + +type GetTagValuesOutput struct { + _ struct{} `type:"structure"` + + // A string that indicates that the response contains more data than can be + // returned in a single response. To receive additional data, specify this string + // for the PaginationToken value in a subsequent request. + PaginationToken *string `type:"string"` + + // A list of all tag values for the specified key in the AWS account. + TagValues []*string `type:"list"` +} + +// String returns the string representation +func (s GetTagValuesOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s GetTagValuesOutput) GoString() string { + return s.String() +} + +// SetPaginationToken sets the PaginationToken field's value. +func (s *GetTagValuesOutput) SetPaginationToken(v string) *GetTagValuesOutput { + s.PaginationToken = &v + return s +} + +// SetTagValues sets the TagValues field's value. +func (s *GetTagValuesOutput) SetTagValues(v []*string) *GetTagValuesOutput { + s.TagValues = v + return s +} + +// A list of resource ARNs and the tags (keys and values) that are associated +// with each. +type ResourceTagMapping struct { + _ struct{} `type:"structure"` + + // An array of resource ARN(s). + ResourceARN *string `min:"1" type:"string"` + + // The tags that have been applied to one or more AWS resources. + Tags []*Tag `type:"list"` +} + +// String returns the string representation +func (s ResourceTagMapping) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s ResourceTagMapping) GoString() string { + return s.String() +} + +// SetResourceARN sets the ResourceARN field's value. +func (s *ResourceTagMapping) SetResourceARN(v string) *ResourceTagMapping { + s.ResourceARN = &v + return s +} + +// SetTags sets the Tags field's value. +func (s *ResourceTagMapping) SetTags(v []*Tag) *ResourceTagMapping { + s.Tags = v + return s +} + +// The metadata that you apply to AWS resources to help you categorize and organize +// them. Each tag consists of a key and an optional value, both of which you +// define. For more information, see Tag Basics (http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html#tag-basics) +// in the Amazon EC2 User Guide for Linux Instances. +type Tag struct { + _ struct{} `type:"structure"` + + // One part of a key-value pair that make up a tag. A key is a general label + // that acts like a category for more specific tag values. + // + // Key is a required field + Key *string `min:"1" type:"string" required:"true"` + + // The optional part of a key-value pair that make up a tag. A value acts as + // a descriptor within a tag category (key). + // + // Value is a required field + Value *string `type:"string" required:"true"` +} + +// String returns the string representation +func (s Tag) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s Tag) GoString() string { + return s.String() +} + +// SetKey sets the Key field's value. +func (s *Tag) SetKey(v string) *Tag { + s.Key = &v + return s +} + +// SetValue sets the Value field's value. +func (s *Tag) SetValue(v string) *Tag { + s.Value = &v + return s +} + +// A list of tags (keys and values) that are used to specify the associated +// resources. +type TagFilter struct { + _ struct{} `type:"structure"` + + // One part of a key-value pair that make up a tag. A key is a general label + // that acts like a category for more specific tag values. + Key *string `min:"1" type:"string"` + + // The optional part of a key-value pair that make up a tag. A value acts as + // a descriptor within a tag category (key). + Values []*string `type:"list"` +} + +// String returns the string representation +func (s TagFilter) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s TagFilter) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *TagFilter) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "TagFilter"} + if s.Key != nil && len(*s.Key) < 1 { + invalidParams.Add(request.NewErrParamMinLen("Key", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetKey sets the Key field's value. +func (s *TagFilter) SetKey(v string) *TagFilter { + s.Key = &v + return s +} + +// SetValues sets the Values field's value. +func (s *TagFilter) SetValues(v []*string) *TagFilter { + s.Values = v + return s +} + +type TagResourcesInput struct { + _ struct{} `type:"structure"` + + // A list of ARNs. An ARN (Amazon Resource Name) uniquely identifies a resource. + // You can specify a minimum of 1 and a maximum of 20 ARNs (resources) to tag. + // An ARN can be set to a maximum of 1600 characters. For more information, + // see Amazon Resource Names (ARNs) and AWS Service Namespaces (http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) + // in the AWS General Reference. + // + // ResourceARNList is a required field + ResourceARNList []*string `min:"1" type:"list" required:"true"` + + // The tags that you want to add to the specified resources. A tag consists + // of a key and a value that you define. + // + // Tags is a required field + Tags map[string]*string `min:"1" type:"map" required:"true"` +} + +// String returns the string representation +func (s TagResourcesInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s TagResourcesInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *TagResourcesInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "TagResourcesInput"} + if s.ResourceARNList == nil { + invalidParams.Add(request.NewErrParamRequired("ResourceARNList")) + } + if s.ResourceARNList != nil && len(s.ResourceARNList) < 1 { + invalidParams.Add(request.NewErrParamMinLen("ResourceARNList", 1)) + } + if s.Tags == nil { + invalidParams.Add(request.NewErrParamRequired("Tags")) + } + if s.Tags != nil && len(s.Tags) < 1 { + invalidParams.Add(request.NewErrParamMinLen("Tags", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetResourceARNList sets the ResourceARNList field's value. +func (s *TagResourcesInput) SetResourceARNList(v []*string) *TagResourcesInput { + s.ResourceARNList = v + return s +} + +// SetTags sets the Tags field's value. +func (s *TagResourcesInput) SetTags(v map[string]*string) *TagResourcesInput { + s.Tags = v + return s +} + +type TagResourcesOutput struct { + _ struct{} `type:"structure"` + + // Details of resources that could not be tagged. An error code, status code, + // and error message are returned for each failed item. + FailedResourcesMap map[string]*FailureInfo `type:"map"` +} + +// String returns the string representation +func (s TagResourcesOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s TagResourcesOutput) GoString() string { + return s.String() +} + +// SetFailedResourcesMap sets the FailedResourcesMap field's value. +func (s *TagResourcesOutput) SetFailedResourcesMap(v map[string]*FailureInfo) *TagResourcesOutput { + s.FailedResourcesMap = v + return s +} + +type UntagResourcesInput struct { + _ struct{} `type:"structure"` + + // A list of ARNs. An ARN (Amazon Resource Name) uniquely identifies a resource. + // You can specify a minimum of 1 and a maximum of 20 ARNs (resources) to untag. + // An ARN can be set to a maximum of 1600 characters. For more information, + // see Amazon Resource Names (ARNs) and AWS Service Namespaces (http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) + // in the AWS General Reference. + // + // ResourceARNList is a required field + ResourceARNList []*string `min:"1" type:"list" required:"true"` + + // A list of the tag keys that you want to remove from the specified resources. + // + // TagKeys is a required field + TagKeys []*string `min:"1" type:"list" required:"true"` +} + +// String returns the string representation +func (s UntagResourcesInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s UntagResourcesInput) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *UntagResourcesInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "UntagResourcesInput"} + if s.ResourceARNList == nil { + invalidParams.Add(request.NewErrParamRequired("ResourceARNList")) + } + if s.ResourceARNList != nil && len(s.ResourceARNList) < 1 { + invalidParams.Add(request.NewErrParamMinLen("ResourceARNList", 1)) + } + if s.TagKeys == nil { + invalidParams.Add(request.NewErrParamRequired("TagKeys")) + } + if s.TagKeys != nil && len(s.TagKeys) < 1 { + invalidParams.Add(request.NewErrParamMinLen("TagKeys", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetResourceARNList sets the ResourceARNList field's value. +func (s *UntagResourcesInput) SetResourceARNList(v []*string) *UntagResourcesInput { + s.ResourceARNList = v + return s +} + +// SetTagKeys sets the TagKeys field's value. +func (s *UntagResourcesInput) SetTagKeys(v []*string) *UntagResourcesInput { + s.TagKeys = v + return s +} + +type UntagResourcesOutput struct { + _ struct{} `type:"structure"` + + // Details of resources that could not be untagged. An error code, status code, + // and error message are returned for each failed item. + FailedResourcesMap map[string]*FailureInfo `type:"map"` +} + +// String returns the string representation +func (s UntagResourcesOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s UntagResourcesOutput) GoString() string { + return s.String() +} + +// SetFailedResourcesMap sets the FailedResourcesMap field's value. +func (s *UntagResourcesOutput) SetFailedResourcesMap(v map[string]*FailureInfo) *UntagResourcesOutput { + s.FailedResourcesMap = v + return s +} + +const ( + // ErrorCodeInternalServiceException is a ErrorCode enum value + ErrorCodeInternalServiceException = "InternalServiceException" + + // ErrorCodeInvalidParameterException is a ErrorCode enum value + ErrorCodeInvalidParameterException = "InvalidParameterException" +) diff --git a/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/doc.go b/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/doc.go new file mode 100644 index 0000000000..45896e0ec8 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/doc.go @@ -0,0 +1,64 @@ +// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. + +// Package resourcegroupstaggingapi provides the client and types for making API +// requests to AWS Resource Groups Tagging API. +// +// This guide describes the API operations for the resource groups tagging. +// +// A tag is a label that you assign to an AWS resource. A tag consists of a +// key and a value, both of which you define. For example, if you have two Amazon +// EC2 instances, you might assign both a tag key of "Stack." But the value +// of "Stack" might be "Testing" for one and "Production" for the other. +// +// Tagging can help you organize your resources and enables you to simplify +// resource management, access management and cost allocation. For more information +// about tagging, see Working with Tag Editor (http://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/tag-editor.html) +// and Working with Resource Groups (http://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/resource-groups.html). +// For more information about permissions you need to use the resource groups +// tagging APIs, see Obtaining Permissions for Resource Groups (http://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/obtaining-permissions-for-resource-groups.html) +// and Obtaining Permissions for Tagging (http://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/obtaining-permissions-for-tagging.html). +// +// You can use the resource groups tagging APIs to complete the following tasks: +// +// * Tag and untag supported resources located in the specified region for +// the AWS account +// +// * Use tag-based filters to search for resources located in the specified +// region for the AWS account +// +// * List all existing tag keys in the specified region for the AWS account +// +// * List all existing values for the specified key in the specified region +// for the AWS account +// +// Not all resources can have tags. For a lists of resources that you can tag, +// see Supported Resources (http://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/supported-resources.html) +// in the AWS Resource Groups and Tag Editor User Guide. +// +// To make full use of the resource groups tagging APIs, you might need additional +// IAM permissions, including permission to access the resources of individual +// services as well as permission to view and apply tags to those resources. +// For more information, see Obtaining Permissions for Tagging (http://docs.aws.amazon.com/awsconsolehelpdocs/latest/gsg/obtaining-permissions-for-tagging.html) +// in the AWS Resource Groups and Tag Editor User Guide. +// +// See https://docs.aws.amazon.com/goto/WebAPI/resourcegroupstaggingapi-2017-01-26 for more information on this service. +// +// See resourcegroupstaggingapi package documentation for more information. +// https://docs.aws.amazon.com/sdk-for-go/api/service/resourcegroupstaggingapi/ +// +// Using the Client +// +// To contact AWS Resource Groups Tagging API with the SDK use the New function to create +// a new service client. With that client you can make API requests to the service. +// These clients are safe to use concurrently. +// +// See the SDK's documentation for more information on how to use the SDK. +// https://docs.aws.amazon.com/sdk-for-go/api/ +// +// See aws.Config documentation for more information on configuring SDK clients. +// https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config +// +// See the AWS Resource Groups Tagging API client ResourceGroupsTaggingAPI for more +// information on creating client for this service. +// https://docs.aws.amazon.com/sdk-for-go/api/service/resourcegroupstaggingapi/#New +package resourcegroupstaggingapi diff --git a/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/errors.go b/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/errors.go new file mode 100644 index 0000000000..89e5a769db --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/errors.go @@ -0,0 +1,33 @@ +// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. + +package resourcegroupstaggingapi + +const ( + + // ErrCodeInternalServiceException for service response error code + // "InternalServiceException". + // + // The request processing failed because of an unknown error, exception, or + // failure. You can retry the request. + ErrCodeInternalServiceException = "InternalServiceException" + + // ErrCodeInvalidParameterException for service response error code + // "InvalidParameterException". + // + // A parameter is missing or a malformed string or invalid or out-of-range value + // was supplied for the request parameter. + ErrCodeInvalidParameterException = "InvalidParameterException" + + // ErrCodePaginationTokenExpiredException for service response error code + // "PaginationTokenExpiredException". + // + // A PaginationToken is valid for a maximum of 15 minutes. Your request was + // denied because the specified PaginationToken has expired. + ErrCodePaginationTokenExpiredException = "PaginationTokenExpiredException" + + // ErrCodeThrottledException for service response error code + // "ThrottledException". + // + // The request was denied to limit the frequency of submitted requests. + ErrCodeThrottledException = "ThrottledException" +) diff --git a/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/service.go b/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/service.go new file mode 100644 index 0000000000..705db6b0d7 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/service.go @@ -0,0 +1,97 @@ +// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. + +package resourcegroupstaggingapi + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/client" + "github.com/aws/aws-sdk-go/aws/client/metadata" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/aws/signer/v4" + "github.com/aws/aws-sdk-go/private/protocol/jsonrpc" +) + +// ResourceGroupsTaggingAPI provides the API operation methods for making requests to +// AWS Resource Groups Tagging API. See this package's package overview docs +// for details on the service. +// +// ResourceGroupsTaggingAPI methods are safe to use concurrently. It is not safe to +// modify mutate any of the struct's properties though. +type ResourceGroupsTaggingAPI struct { + *client.Client +} + +// Used for custom client initialization logic +var initClient func(*client.Client) + +// Used for custom request initialization logic +var initRequest func(*request.Request) + +// Service information constants +const ( + ServiceName = "tagging" // Name of service. + EndpointsID = ServiceName // ID to lookup a service endpoint with. + ServiceID = "Resource Groups Tagging API" // ServiceID is a unique identifer of a specific service. +) + +// New creates a new instance of the ResourceGroupsTaggingAPI client with a session. +// If additional configuration is needed for the client instance use the optional +// aws.Config parameter to add your extra config. +// +// Example: +// // Create a ResourceGroupsTaggingAPI client from just a session. +// svc := resourcegroupstaggingapi.New(mySession) +// +// // Create a ResourceGroupsTaggingAPI client with additional configuration +// svc := resourcegroupstaggingapi.New(mySession, aws.NewConfig().WithRegion("us-west-2")) +func New(p client.ConfigProvider, cfgs ...*aws.Config) *ResourceGroupsTaggingAPI { + c := p.ClientConfig(EndpointsID, cfgs...) + return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, c.SigningName) +} + +// newClient creates, initializes and returns a new service client instance. +func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion, signingName string) *ResourceGroupsTaggingAPI { + svc := &ResourceGroupsTaggingAPI{ + Client: client.New( + cfg, + metadata.ClientInfo{ + ServiceName: ServiceName, + ServiceID: ServiceID, + SigningName: signingName, + SigningRegion: signingRegion, + Endpoint: endpoint, + APIVersion: "2017-01-26", + JSONVersion: "1.1", + TargetPrefix: "ResourceGroupsTaggingAPI_20170126", + }, + handlers, + ), + } + + // Handlers + svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler) + svc.Handlers.Build.PushBackNamed(jsonrpc.BuildHandler) + svc.Handlers.Unmarshal.PushBackNamed(jsonrpc.UnmarshalHandler) + svc.Handlers.UnmarshalMeta.PushBackNamed(jsonrpc.UnmarshalMetaHandler) + svc.Handlers.UnmarshalError.PushBackNamed(jsonrpc.UnmarshalErrorHandler) + + // Run custom client initialization if present + if initClient != nil { + initClient(svc.Client) + } + + return svc +} + +// newRequest creates a new request for a ResourceGroupsTaggingAPI operation and runs any +// custom request initialization. +func (c *ResourceGroupsTaggingAPI) newRequest(op *request.Operation, params, data interface{}) *request.Request { + req := c.NewRequest(op, params, data) + + // Run custom request initialization if present + if initRequest != nil { + initRequest(req) + } + + return req +}