From 65e948a0d49c12d7b1cfc70fa4c15bf3f386f22a Mon Sep 17 00:00:00 2001 From: Peter Rifel Date: Tue, 9 Apr 2024 21:21:46 -0500 Subject: [PATCH 01/14] Add EC2 API interface --- util/pkg/awsinterfaces/ec2.go | 105 ++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 util/pkg/awsinterfaces/ec2.go diff --git a/util/pkg/awsinterfaces/ec2.go b/util/pkg/awsinterfaces/ec2.go new file mode 100644 index 0000000000000..f0df1a103fc12 --- /dev/null +++ b/util/pkg/awsinterfaces/ec2.go @@ -0,0 +1,105 @@ +/* +Copyright 2024 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 awsinterfaces + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/service/ec2" +) + +type EC2API interface { + AllocateAddress(ctx context.Context, params *ec2.AllocateAddressInput, optFns ...func(*ec2.Options)) (*ec2.AllocateAddressOutput, error) + AssignIpv6Addresses(ctx context.Context, params *ec2.AssignIpv6AddressesInput, optFns ...func(*ec2.Options)) (*ec2.AssignIpv6AddressesOutput, error) + AssociateDhcpOptions(ctx context.Context, params *ec2.AssociateDhcpOptionsInput, optFns ...func(*ec2.Options)) (*ec2.AssociateDhcpOptionsOutput, error) + AssociateRouteTable(ctx context.Context, params *ec2.AssociateRouteTableInput, optFns ...func(*ec2.Options)) (*ec2.AssociateRouteTableOutput, error) + AssociateSubnetCidrBlock(ctx context.Context, params *ec2.AssociateSubnetCidrBlockInput, optFns ...func(*ec2.Options)) (*ec2.AssociateSubnetCidrBlockOutput, error) + AssociateVpcCidrBlock(ctx context.Context, params *ec2.AssociateVpcCidrBlockInput, optFns ...func(*ec2.Options)) (*ec2.AssociateVpcCidrBlockOutput, error) + AttachInternetGateway(ctx context.Context, params *ec2.AttachInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.AttachInternetGatewayOutput, error) + AuthorizeSecurityGroupEgress(ctx context.Context, params *ec2.AuthorizeSecurityGroupEgressInput, optFns ...func(*ec2.Options)) (*ec2.AuthorizeSecurityGroupEgressOutput, error) + AuthorizeSecurityGroupIngress(ctx context.Context, params *ec2.AuthorizeSecurityGroupIngressInput, optFns ...func(*ec2.Options)) (*ec2.AuthorizeSecurityGroupIngressOutput, error) + + CreateDhcpOptions(ctx context.Context, params *ec2.CreateDhcpOptionsInput, optFns ...func(*ec2.Options)) (*ec2.CreateDhcpOptionsOutput, error) + CreateEgressOnlyInternetGateway(ctx context.Context, params *ec2.CreateEgressOnlyInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.CreateEgressOnlyInternetGatewayOutput, error) + CreateInternetGateway(ctx context.Context, params *ec2.CreateInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.CreateInternetGatewayOutput, error) + CreateLaunchTemplate(ctx context.Context, params *ec2.CreateLaunchTemplateInput, optFns ...func(*ec2.Options)) (*ec2.CreateLaunchTemplateOutput, error) + CreateLaunchTemplateVersion(ctx context.Context, params *ec2.CreateLaunchTemplateVersionInput, optFns ...func(*ec2.Options)) (*ec2.CreateLaunchTemplateVersionOutput, error) + CreateNatGateway(ctx context.Context, params *ec2.CreateNatGatewayInput, optFns ...func(*ec2.Options)) (*ec2.CreateNatGatewayOutput, error) + CreateRoute(ctx context.Context, params *ec2.CreateRouteInput, optFns ...func(*ec2.Options)) (*ec2.CreateRouteOutput, error) + CreateRouteTable(ctx context.Context, params *ec2.CreateRouteTableInput, optFns ...func(*ec2.Options)) (*ec2.CreateRouteTableOutput, error) + CreateSecurityGroup(ctx context.Context, params *ec2.CreateSecurityGroupInput, optFns ...func(*ec2.Options)) (*ec2.CreateSecurityGroupOutput, error) + CreateSubnet(ctx context.Context, params *ec2.CreateSubnetInput, optFns ...func(*ec2.Options)) (*ec2.CreateSubnetOutput, error) + CreateTags(ctx context.Context, params *ec2.CreateTagsInput, optFns ...func(*ec2.Options)) (*ec2.CreateTagsOutput, error) + CreateVolume(ctx context.Context, params *ec2.CreateVolumeInput, optFns ...func(*ec2.Options)) (*ec2.CreateVolumeOutput, error) + CreateVpc(ctx context.Context, params *ec2.CreateVpcInput, optFns ...func(*ec2.Options)) (*ec2.CreateVpcOutput, error) + + DeleteDhcpOptions(ctx context.Context, params *ec2.DeleteDhcpOptionsInput, optFns ...func(*ec2.Options)) (*ec2.DeleteDhcpOptionsOutput, error) + DeleteEgressOnlyInternetGateway(ctx context.Context, params *ec2.DeleteEgressOnlyInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DeleteEgressOnlyInternetGatewayOutput, error) + DeleteInternetGateway(ctx context.Context, params *ec2.DeleteInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DeleteInternetGatewayOutput, error) + DeleteKeyPair(ctx context.Context, params *ec2.DeleteKeyPairInput, optFns ...func(*ec2.Options)) (*ec2.DeleteKeyPairOutput, error) + DeleteLaunchTemplate(ctx context.Context, params *ec2.DeleteLaunchTemplateInput, optFns ...func(*ec2.Options)) (*ec2.DeleteLaunchTemplateOutput, error) + DeleteNatGateway(ctx context.Context, params *ec2.DeleteNatGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DeleteNatGatewayOutput, error) + DeleteNetworkInterface(ctx context.Context, params *ec2.DeleteNetworkInterfaceInput, optFns ...func(*ec2.Options)) (*ec2.DeleteNetworkInterfaceOutput, error) + DeleteRouteTable(ctx context.Context, params *ec2.DeleteRouteTableInput, optFns ...func(*ec2.Options)) (*ec2.DeleteRouteTableOutput, error) + DeleteSecurityGroup(ctx context.Context, params *ec2.DeleteSecurityGroupInput, optFns ...func(*ec2.Options)) (*ec2.DeleteSecurityGroupOutput, error) + DeleteSubnet(ctx context.Context, params *ec2.DeleteSubnetInput, optFns ...func(*ec2.Options)) (*ec2.DeleteSubnetOutput, error) + DeleteTags(ctx context.Context, params *ec2.DeleteTagsInput, optFns ...func(*ec2.Options)) (*ec2.DeleteTagsOutput, error) + DeleteVolume(ctx context.Context, params *ec2.DeleteVolumeInput, optFns ...func(*ec2.Options)) (*ec2.DeleteVolumeOutput, error) + DeleteVpc(ctx context.Context, params *ec2.DeleteVpcInput, optFns ...func(*ec2.Options)) (*ec2.DeleteVpcOutput, error) + + DescribeAddresses(ctx context.Context, params *ec2.DescribeAddressesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeAddressesOutput, error) + DescribeAvailabilityZones(ctx context.Context, params *ec2.DescribeAvailabilityZonesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeAvailabilityZonesOutput, error) + DescribeDhcpOptions(ctx context.Context, params *ec2.DescribeDhcpOptionsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeDhcpOptionsOutput, error) + DescribeEgressOnlyInternetGateways(ctx context.Context, params *ec2.DescribeEgressOnlyInternetGatewaysInput, optFns ...func(*ec2.Options)) (*ec2.DescribeEgressOnlyInternetGatewaysOutput, error) + DescribeImages(ctx context.Context, params *ec2.DescribeImagesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeImagesOutput, error) + DescribeInstanceAttribute(ctx context.Context, params *ec2.DescribeInstanceAttributeInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstanceAttributeOutput, error) + DescribeInstances(ctx context.Context, params *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) + DescribeInstanceTypes(ctx context.Context, params *ec2.DescribeInstanceTypesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstanceTypesOutput, error) + DescribeInternetGateways(ctx context.Context, params *ec2.DescribeInternetGatewaysInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInternetGatewaysOutput, error) + DescribeKeyPairs(ctx context.Context, params *ec2.DescribeKeyPairsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeKeyPairsOutput, error) + DescribeLaunchTemplates(ctx context.Context, params *ec2.DescribeLaunchTemplatesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeLaunchTemplatesOutput, error) + DescribeLaunchTemplateVersions(ctx context.Context, params *ec2.DescribeLaunchTemplateVersionsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeLaunchTemplateVersionsOutput, error) + DescribeNatGateways(ctx context.Context, params *ec2.DescribeNatGatewaysInput, optFns ...func(*ec2.Options)) (*ec2.DescribeNatGatewaysOutput, error) + DescribeNetworkInterfaces(ctx context.Context, params *ec2.DescribeNetworkInterfacesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeNetworkInterfacesOutput, error) + DescribeRegions(ctx context.Context, params *ec2.DescribeRegionsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error) + DescribeReservedInstancesOfferings(ctx context.Context, params *ec2.DescribeReservedInstancesOfferingsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeReservedInstancesOfferingsOutput, error) + DescribeRouteTables(ctx context.Context, params *ec2.DescribeRouteTablesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeRouteTablesOutput, error) + DescribeSecurityGroupRules(ctx context.Context, params *ec2.DescribeSecurityGroupRulesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeSecurityGroupRulesOutput, error) + DescribeSecurityGroups(ctx context.Context, params *ec2.DescribeSecurityGroupsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeSecurityGroupsOutput, error) + DescribeSubnets(ctx context.Context, params *ec2.DescribeSubnetsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeSubnetsOutput, error) + DescribeTags(ctx context.Context, params *ec2.DescribeTagsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeTagsOutput, error) + DescribeVolumes(ctx context.Context, params *ec2.DescribeVolumesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeVolumesOutput, error) + DescribeVpcAttribute(ctx context.Context, params *ec2.DescribeVpcAttributeInput, optFns ...func(*ec2.Options)) (*ec2.DescribeVpcAttributeOutput, error) + DescribeVpcs(ctx context.Context, params *ec2.DescribeVpcsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeVpcsOutput, error) + + DetachInternetGateway(ctx context.Context, params *ec2.DetachInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DetachInternetGatewayOutput, error) + DisassociateRouteTable(ctx context.Context, params *ec2.DisassociateRouteTableInput, optFns ...func(*ec2.Options)) (*ec2.DisassociateRouteTableOutput, error) + DisassociateSubnetCidrBlock(ctx context.Context, params *ec2.DisassociateSubnetCidrBlockInput, optFns ...func(*ec2.Options)) (*ec2.DisassociateSubnetCidrBlockOutput, error) + DisassociateVpcCidrBlock(ctx context.Context, params *ec2.DisassociateVpcCidrBlockInput, optFns ...func(*ec2.Options)) (*ec2.DisassociateVpcCidrBlockOutput, error) + GetInstanceTypesFromInstanceRequirements(ctx context.Context, params *ec2.GetInstanceTypesFromInstanceRequirementsInput, optFns ...func(*ec2.Options)) (*ec2.GetInstanceTypesFromInstanceRequirementsOutput, error) + ImportKeyPair(ctx context.Context, params *ec2.ImportKeyPairInput, optFns ...func(*ec2.Options)) (*ec2.ImportKeyPairOutput, error) + ModifyLaunchTemplate(ctx context.Context, params *ec2.ModifyLaunchTemplateInput, optFns ...func(*ec2.Options)) (*ec2.ModifyLaunchTemplateOutput, error) + ModifySubnetAttribute(ctx context.Context, params *ec2.ModifySubnetAttributeInput, optFns ...func(*ec2.Options)) (*ec2.ModifySubnetAttributeOutput, error) + ModifyVolume(ctx context.Context, params *ec2.ModifyVolumeInput, optFns ...func(*ec2.Options)) (*ec2.ModifyVolumeOutput, error) + ModifyVpcAttribute(ctx context.Context, params *ec2.ModifyVpcAttributeInput, optFns ...func(*ec2.Options)) (*ec2.ModifyVpcAttributeOutput, error) + ReleaseAddress(ctx context.Context, params *ec2.ReleaseAddressInput, optFns ...func(*ec2.Options)) (*ec2.ReleaseAddressOutput, error) + ReplaceRoute(ctx context.Context, params *ec2.ReplaceRouteInput, optFns ...func(*ec2.Options)) (*ec2.ReplaceRouteOutput, error) + RevokeSecurityGroupIngress(ctx context.Context, params *ec2.RevokeSecurityGroupIngressInput, optFns ...func(*ec2.Options)) (*ec2.RevokeSecurityGroupIngressOutput, error) + RevokeSecurityGroupEgress(ctx context.Context, params *ec2.RevokeSecurityGroupEgressInput, optFns ...func(*ec2.Options)) (*ec2.RevokeSecurityGroupEgressOutput, error) + RunInstances(ctx context.Context, params *ec2.RunInstancesInput, optFns ...func(*ec2.Options)) (*ec2.RunInstancesOutput, error) + TerminateInstances(ctx context.Context, params *ec2.TerminateInstancesInput, optFns ...func(*ec2.Options)) (*ec2.TerminateInstancesOutput, error) +} From f0c0c29121835b7a0349d6146445f94c4bb2b732 Mon Sep 17 00:00:00 2001 From: Peter Rifel Date: Tue, 9 Apr 2024 20:18:25 -0500 Subject: [PATCH 02/14] Migrate EC2 Networking resource types to aws-sdk-go-v2 --- cloudmock/aws/mockec2/address.go | 83 ++------ cloudmock/aws/mockec2/api.go | 17 +- cloudmock/aws/mockec2/dhcpoptions.go | 69 ++----- .../aws/mockec2/egressonlyinternetgateways.go | 65 ++----- cloudmock/aws/mockec2/eni.go | 12 +- cloudmock/aws/mockec2/internetgateways.go | 89 +++------ cloudmock/aws/mockec2/natgateway.go | 79 +++----- cloudmock/aws/mockec2/routetable.go | 73 ++----- cloudmock/aws/mockec2/securitygroups.go | 183 +++++------------- cloudmock/aws/mockec2/subnets.go | 93 +++------ cloudmock/aws/mockec2/vpcs.go | 123 ++++-------- cloudmock/aws/mockelbv2/loadbalancers.go | 10 +- cmd/kops/create_cluster_integration_test.go | 6 +- pkg/model/awsmodel/api_loadbalancer.go | 36 ++-- pkg/model/awsmodel/bastion.go | 32 +-- pkg/model/awsmodel/external_access.go | 20 +- pkg/model/awsmodel/firewall.go | 8 +- pkg/resources/aws/aws.go | 100 ++++++---- pkg/resources/aws/elasticip.go | 4 +- pkg/resources/aws/eni.go | 33 ++-- pkg/resources/aws/natgateway.go | 4 +- pkg/resources/aws/routetable.go | 17 +- pkg/resources/aws/securitygroup.go | 26 +-- pkg/resources/aws/subnet.go | 4 +- pkg/resources/aws/vpc.go | 24 ++- pkg/resources/aws/vpc_test.go | 41 ++-- pkg/testutils/integrationtestharness.go | 19 +- upup/pkg/fi/cloudup/awstasks/dhcp_options.go | 21 +- .../awstasks/egressonlyinternetgateway.go | 27 +-- .../egressonlyinternetgateway_test.go | 19 +- upup/pkg/fi/cloudup/awstasks/elastic_ip.go | 39 ++-- .../fi/cloudup/awstasks/elastic_ip_test.go | 8 +- .../fi/cloudup/awstasks/internetgateway.go | 29 +-- .../cloudup/awstasks/internetgateway_test.go | 25 +-- upup/pkg/fi/cloudup/awstasks/natgateway.go | 56 +++--- upup/pkg/fi/cloudup/awstasks/route.go | 16 +- upup/pkg/fi/cloudup/awstasks/routetable.go | 42 ++-- .../cloudup/awstasks/routetableassociation.go | 23 ++- upup/pkg/fi/cloudup/awstasks/securitygroup.go | 60 +++--- .../fi/cloudup/awstasks/securitygroup_test.go | 44 ++--- .../fi/cloudup/awstasks/securitygrouprule.go | 70 +++---- upup/pkg/fi/cloudup/awstasks/subnet.go | 66 ++++--- upup/pkg/fi/cloudup/awstasks/subnet_test.go | 55 +++--- upup/pkg/fi/cloudup/awstasks/vpc.go | 45 +++-- .../awstasks/vpc_dhcpoptions_association.go | 6 +- upup/pkg/fi/cloudup/awstasks/vpc_test.go | 28 +-- .../awstasks/vpcamazonipv6cidrblock.go | 11 +- upup/pkg/fi/cloudup/awstasks/vpccidrblock.go | 13 +- upup/pkg/fi/cloudup/awsup/aws_cloud.go | 46 +++-- upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go | 4 +- upup/pkg/fi/cloudup/new_cluster.go | 5 +- upup/pkg/fi/nodeup/nodetasks/prefix.go | 8 +- 52 files changed, 894 insertions(+), 1142 deletions(-) diff --git a/cloudmock/aws/mockec2/address.go b/cloudmock/aws/mockec2/address.go index ba7bbfdd6cefc..a6a732f5e28be 100644 --- a/cloudmock/aws/mockec2/address.go +++ b/cloudmock/aws/mockec2/address.go @@ -17,24 +17,17 @@ limitations under the License. package mockec2 import ( + "context" "encoding/binary" "fmt" "net" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) -func (m *MockEC2) AllocateAddressRequest(*ec2.AllocateAddressInput) (*request.Request, *ec2.AllocateAddressOutput) { - panic("Not implemented") -} - -func (m *MockEC2) AllocateAddressWithContext(aws.Context, *ec2.AllocateAddressInput, ...request.Option) (*ec2.AllocateAddressOutput, error) { - panic("Not implemented") -} - func (m *MockEC2) AllocateAddressWithId(request *ec2.AllocateAddressInput, id string) (*ec2.AllocateAddressOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -50,10 +43,10 @@ func (m *MockEC2) AllocateAddressWithId(request *ec2.AllocateAddressInput, id st binary.BigEndian.PutUint32(publicIP, v) } - tags := tagSpecificationsToTags(request.TagSpecifications, ec2.ResourceTypeElasticIp) - address := &ec2.Address{ + tags := tagSpecificationsToTags(request.TagSpecifications, ec2types.ResourceTypeElasticIp) + address := &ec2types.Address{ AllocationId: s(id), - Domain: s("vpc"), + Domain: ec2types.DomainTypeVpc, PublicIp: s(publicIP.String()), Tags: tags, } @@ -62,7 +55,7 @@ func (m *MockEC2) AllocateAddressWithId(request *ec2.AllocateAddressInput, id st } if m.Addresses == nil { - m.Addresses = make(map[string]*ec2.Address) + m.Addresses = make(map[string]*ec2types.Address) } m.Addresses[id] = address m.addTags(id, tags...) @@ -75,54 +68,22 @@ func (m *MockEC2) AllocateAddressWithId(request *ec2.AllocateAddressInput, id st return response, nil } -func (m *MockEC2) AllocateAddress(request *ec2.AllocateAddressInput) (*ec2.AllocateAddressOutput, error) { +func (m *MockEC2) AllocateAddress(ctx context.Context, request *ec2.AllocateAddressInput, optFns ...func(*ec2.Options)) (*ec2.AllocateAddressOutput, error) { klog.Infof("AllocateAddress: %v", request) id := m.allocateId("eipalloc") return m.AllocateAddressWithId(request, id) } -func (m *MockEC2) AssignPrivateIpAddressesRequest(*ec2.AssignPrivateIpAddressesInput) (*request.Request, *ec2.AssignPrivateIpAddressesOutput) { - panic("Not implemented") -} - -func (m *MockEC2) AssignPrivateIpAddressesWithContext(aws.Context, *ec2.AssignPrivateIpAddressesInput, ...request.Option) (*ec2.AssignPrivateIpAddressesOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) AssignPrivateIpAddresses(*ec2.AssignPrivateIpAddressesInput) (*ec2.AssignPrivateIpAddressesOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) AssociateAddressRequest(*ec2.AssociateAddressInput) (*request.Request, *ec2.AssociateAddressOutput) { - panic("Not implemented") -} - -func (m *MockEC2) AssociateAddressWithContext(aws.Context, *ec2.AssociateAddressInput, ...request.Option) (*ec2.AssociateAddressOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) AssociateAddress(*ec2.AssociateAddressInput) (*ec2.AssociateAddressOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeAddressesRequest(*ec2.DescribeAddressesInput) (*request.Request, *ec2.DescribeAddressesOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeAddressesWithContext(aws.Context, *ec2.DescribeAddressesInput, ...request.Option) (*ec2.DescribeAddressesOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeAddresses(request *ec2.DescribeAddressesInput) (*ec2.DescribeAddressesOutput, error) { +func (m *MockEC2) DescribeAddresses(ctx context.Context, request *ec2.DescribeAddressesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeAddressesOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DescribeAddresses: %v", request) - var addresses []*ec2.Address + var addresses []ec2types.Address if len(request.AllocationIds) != 0 { - request.Filters = append(request.Filters, &ec2.Filter{Name: s("allocation-id"), Values: request.AllocationIds}) + request.Filters = append(request.Filters, ec2types.Filter{Name: s("allocation-id"), Values: request.AllocationIds}) } for _, address := range m.Addresses { allFiltersMatch := true @@ -132,14 +93,14 @@ func (m *MockEC2) DescribeAddresses(request *ec2.DescribeAddressesInput) (*ec2.D case "allocation-id": for _, v := range filter.Values { - if *address.AllocationId == *v { + if *address.AllocationId == v { match = true } } case "public-ip": for _, v := range filter.Values { - if *address.PublicIp == *v { + if *address.PublicIp == v { match = true } } @@ -159,8 +120,8 @@ func (m *MockEC2) DescribeAddresses(request *ec2.DescribeAddressesInput) (*ec2.D } copy := *address - copy.Tags = m.getTags(ec2.ResourceTypeElasticIp, *address.AllocationId) - addresses = append(addresses, ©) + copy.Tags = m.getTags(ec2types.ResourceTypeElasticIp, *address.AllocationId) + addresses = append(addresses, copy) } response := &ec2.DescribeAddressesOutput{ @@ -170,21 +131,13 @@ func (m *MockEC2) DescribeAddresses(request *ec2.DescribeAddressesInput) (*ec2.D return response, nil } -func (m *MockEC2) ReleaseAddressRequest(*ec2.ReleaseAddressInput) (*request.Request, *ec2.ReleaseAddressOutput) { - panic("Not implemented") -} - -func (m *MockEC2) ReleaseAddressWithContext(aws.Context, *ec2.ReleaseAddressInput, ...request.Option) (*ec2.ReleaseAddressOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) ReleaseAddress(request *ec2.ReleaseAddressInput) (*ec2.ReleaseAddressOutput, error) { +func (m *MockEC2) ReleaseAddress(ctx context.Context, request *ec2.ReleaseAddressInput, optFns ...func(*ec2.Options)) (*ec2.ReleaseAddressOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("ReleaseAddress: %v", request) - id := aws.StringValue(request.AllocationId) + id := aws.ToString(request.AllocationId) o := m.Addresses[id] if o == nil { return nil, fmt.Errorf("Address %q not found", id) diff --git a/cloudmock/aws/mockec2/api.go b/cloudmock/aws/mockec2/api.go index 40bdc65fd9616..d562a6d9ec525 100644 --- a/cloudmock/aws/mockec2/api.go +++ b/cloudmock/aws/mockec2/api.go @@ -23,6 +23,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" ) type MockEC2 struct { @@ -32,17 +33,17 @@ type MockEC2 struct { mutex sync.Mutex addressNumber int - Addresses map[string]*ec2.Address + Addresses map[string]*ec2types.Address - RouteTables map[string]*ec2.RouteTable + RouteTables map[string]*ec2types.RouteTable - DhcpOptions map[string]*ec2.DhcpOptions + DhcpOptions map[string]*ec2types.DhcpOptions Images []*ec2.Image securityGroupNumber int - SecurityGroups map[string]*ec2.SecurityGroup - SecurityGroupRules map[string]*ec2.SecurityGroupRule + SecurityGroups map[string]*ec2types.SecurityGroup + SecurityGroupRules map[string]*ec2types.SecurityGroupRule subnets map[string]*subnetInfo @@ -54,13 +55,13 @@ type MockEC2 struct { Vpcs map[string]*vpcInfo - InternetGateways map[string]*ec2.InternetGateway - EgressOnlyInternetGateways map[string]*ec2.EgressOnlyInternetGateway + InternetGateways map[string]*ec2types.InternetGateway + EgressOnlyInternetGateways map[string]*ec2types.EgressOnlyInternetGateway launchTemplateNumber int LaunchTemplates map[string]*launchTemplateInfo - NatGateways map[string]*ec2.NatGateway + NatGateways map[string]*ec2types.NatGateway idsMutex sync.Mutex ids map[string]*idAllocator diff --git a/cloudmock/aws/mockec2/dhcpoptions.go b/cloudmock/aws/mockec2/dhcpoptions.go index a3b31fb13cfa7..dbe846258b8e5 100644 --- a/cloudmock/aws/mockec2/dhcpoptions.go +++ b/cloudmock/aws/mockec2/dhcpoptions.go @@ -17,16 +17,17 @@ limitations under the License. package mockec2 import ( + "context" "fmt" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) -func (m *MockEC2) DescribeDhcpOptions(request *ec2.DescribeDhcpOptionsInput) (*ec2.DescribeDhcpOptionsOutput, error) { +func (m *MockEC2) DescribeDhcpOptions(ctx context.Context, request *ec2.DescribeDhcpOptionsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeDhcpOptionsOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -52,7 +53,7 @@ func (m *MockEC2) DescribeDhcpOptions(request *ec2.DescribeDhcpOptionsInput) (*e // } default: if strings.HasPrefix(*filter.Name, "tag:") { - match = m.hasTag(ec2.ResourceTypeDhcpOptions, *dhcpOptions.DhcpOptionsId, filter) + match = m.hasTag(ec2types.ResourceTypeDhcpOptions, *dhcpOptions.DhcpOptionsId, filter) } else { return nil, fmt.Errorf("unknown filter name: %q", *filter.Name) } @@ -69,22 +70,14 @@ func (m *MockEC2) DescribeDhcpOptions(request *ec2.DescribeDhcpOptionsInput) (*e } copy := *dhcpOptions - copy.Tags = m.getTags(ec2.ResourceTypeDhcpOptions, id) - response.DhcpOptions = append(response.DhcpOptions, ©) + copy.Tags = m.getTags(ec2types.ResourceTypeDhcpOptions, id) + response.DhcpOptions = append(response.DhcpOptions, copy) } return response, nil } -func (m *MockEC2) DescribeDhcpOptionsWithContext(aws.Context, *ec2.DescribeDhcpOptionsInput, ...request.Option) (*ec2.DescribeDhcpOptionsOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeDhcpOptionsRequest(*ec2.DescribeDhcpOptionsInput) (*request.Request, *ec2.DescribeDhcpOptionsOutput) { - panic("Not implemented") -} - -func (m *MockEC2) AssociateDhcpOptions(request *ec2.AssociateDhcpOptionsInput) (*ec2.AssociateDhcpOptionsOutput, error) { +func (m *MockEC2) AssociateDhcpOptions(ctx context.Context, request *ec2.AssociateDhcpOptionsInput, optFns ...func(*ec2.Options)) (*ec2.AssociateDhcpOptionsOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -110,15 +103,7 @@ func (m *MockEC2) AssociateDhcpOptions(request *ec2.AssociateDhcpOptionsInput) ( return response, nil } -func (m *MockEC2) AssociateDhcpOptionsWithContext(aws.Context, *ec2.AssociateDhcpOptionsInput, ...request.Option) (*ec2.AssociateDhcpOptionsOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) AssociateDhcpOptionsRequest(*ec2.AssociateDhcpOptionsInput) (*request.Request, *ec2.AssociateDhcpOptionsOutput) { - panic("Not implemented") -} - -func (m *MockEC2) CreateDhcpOptions(request *ec2.CreateDhcpOptionsInput) (*ec2.CreateDhcpOptionsOutput, error) { +func (m *MockEC2) CreateDhcpOptions(ctx context.Context, request *ec2.CreateDhcpOptionsInput, optFns ...func(*ec2.Options)) (*ec2.CreateDhcpOptionsOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -131,48 +116,40 @@ func (m *MockEC2) CreateDhcpOptions(request *ec2.CreateDhcpOptionsInput) (*ec2.C n := len(m.DhcpOptions) + 1 id := fmt.Sprintf("dopt-%d", n) - dhcpOptions := &ec2.DhcpOptions{ + dhcpOptions := &ec2types.DhcpOptions{ DhcpOptionsId: s(id), } for _, o := range request.DhcpConfigurations { - c := &ec2.DhcpConfiguration{ + c := ec2types.DhcpConfiguration{ Key: o.Key, } for _, v := range o.Values { - c.Values = append(c.Values, &ec2.AttributeValue{ - Value: v, + c.Values = append(c.Values, ec2types.AttributeValue{ + Value: aws.String(v), }) } dhcpOptions.DhcpConfigurations = append(dhcpOptions.DhcpConfigurations, c) } if m.DhcpOptions == nil { - m.DhcpOptions = make(map[string]*ec2.DhcpOptions) + m.DhcpOptions = make(map[string]*ec2types.DhcpOptions) } m.DhcpOptions[*dhcpOptions.DhcpOptionsId] = dhcpOptions - m.addTags(id, tagSpecificationsToTags(request.TagSpecifications, ec2.ResourceTypeDhcpOptions)...) + m.addTags(id, tagSpecificationsToTags(request.TagSpecifications, ec2types.ResourceTypeDhcpOptions)...) copy := *dhcpOptions - copy.Tags = m.getTags(ec2.ResourceTypeDhcpOptions, *dhcpOptions.DhcpOptionsId) + copy.Tags = m.getTags(ec2types.ResourceTypeDhcpOptions, *dhcpOptions.DhcpOptionsId) return &ec2.CreateDhcpOptionsOutput{DhcpOptions: ©}, nil } -func (m *MockEC2) CreateDhcpOptionsWithContext(aws.Context, *ec2.CreateDhcpOptionsInput, ...request.Option) (*ec2.CreateDhcpOptionsOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) CreateDhcpOptionsRequest(*ec2.CreateDhcpOptionsInput) (*request.Request, *ec2.CreateDhcpOptionsOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteDhcpOptions(request *ec2.DeleteDhcpOptionsInput) (*ec2.DeleteDhcpOptionsOutput, error) { +func (m *MockEC2) DeleteDhcpOptions(ctx context.Context, request *ec2.DeleteDhcpOptionsInput, optFns ...func(*ec2.Options)) (*ec2.DeleteDhcpOptionsOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DeleteDhcpOptions: %v", request) - id := aws.StringValue(request.DhcpOptionsId) + id := aws.ToString(request.DhcpOptionsId) o := m.DhcpOptions[id] if o == nil { return nil, fmt.Errorf("DhcpOptions %q not found", id) @@ -181,11 +158,3 @@ func (m *MockEC2) DeleteDhcpOptions(request *ec2.DeleteDhcpOptionsInput) (*ec2.D return &ec2.DeleteDhcpOptionsOutput{}, nil } - -func (m *MockEC2) DeleteDhcpOptionsWithContext(aws.Context, *ec2.DeleteDhcpOptionsInput, ...request.Option) (*ec2.DeleteDhcpOptionsOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteDhcpOptionsRequest(*ec2.DeleteDhcpOptionsInput) (*request.Request, *ec2.DeleteDhcpOptionsOutput) { - panic("Not implemented") -} diff --git a/cloudmock/aws/mockec2/egressonlyinternetgateways.go b/cloudmock/aws/mockec2/egressonlyinternetgateways.go index d0150fdad7baf..d2a13f240511c 100644 --- a/cloudmock/aws/mockec2/egressonlyinternetgateways.go +++ b/cloudmock/aws/mockec2/egressonlyinternetgateways.go @@ -17,16 +17,17 @@ limitations under the License. package mockec2 import ( + "context" "fmt" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) -func (m *MockEC2) FindEgressOnlyInternetGateway(id string) *ec2.EgressOnlyInternetGateway { +func (m *MockEC2) FindEgressOnlyInternetGateway(id string) *ec2types.EgressOnlyInternetGateway { m.mutex.Lock() defer m.mutex.Unlock() @@ -36,7 +37,7 @@ func (m *MockEC2) FindEgressOnlyInternetGateway(id string) *ec2.EgressOnlyIntern } copy := *internetGateway - copy.Tags = m.getTags(ec2.ResourceTypeEgressOnlyInternetGateway, id) + copy.Tags = m.getTags(ec2types.ResourceTypeEgressOnlyInternetGateway, id) return © } @@ -51,26 +52,18 @@ func (m *MockEC2) EgressOnlyInternetGatewayIds() []string { return ids } -func (m *MockEC2) CreateEgressOnlyInternetGatewayRequest(*ec2.CreateEgressOnlyInternetGatewayInput) (*request.Request, *ec2.CreateEgressOnlyInternetGatewayOutput) { - panic("Not implemented") -} - -func (m *MockEC2) CreateEgressOnlyInternetGatewayWithContext(aws.Context, *ec2.CreateEgressOnlyInternetGatewayInput, ...request.Option) (*ec2.CreateEgressOnlyInternetGatewayOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) CreateEgressOnlyInternetGateway(request *ec2.CreateEgressOnlyInternetGatewayInput) (*ec2.CreateEgressOnlyInternetGatewayOutput, error) { +func (m *MockEC2) CreateEgressOnlyInternetGateway(ctx context.Context, request *ec2.CreateEgressOnlyInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.CreateEgressOnlyInternetGatewayOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("CreateEgressOnlyInternetGateway: %v", request) id := m.allocateId("eigw") - tags := tagSpecificationsToTags(request.TagSpecifications, ec2.ResourceTypeEgressOnlyInternetGateway) + tags := tagSpecificationsToTags(request.TagSpecifications, ec2types.ResourceTypeEgressOnlyInternetGateway) - eigw := &ec2.EgressOnlyInternetGateway{ + eigw := &ec2types.EgressOnlyInternetGateway{ EgressOnlyInternetGatewayId: s(id), - Attachments: []*ec2.InternetGatewayAttachment{ + Attachments: []ec2types.InternetGatewayAttachment{ { VpcId: request.VpcId, }, @@ -79,7 +72,7 @@ func (m *MockEC2) CreateEgressOnlyInternetGateway(request *ec2.CreateEgressOnlyI } if m.EgressOnlyInternetGateways == nil { - m.EgressOnlyInternetGateways = make(map[string]*ec2.EgressOnlyInternetGateway) + m.EgressOnlyInternetGateways = make(map[string]*ec2types.EgressOnlyInternetGateway) } m.EgressOnlyInternetGateways[id] = eigw @@ -91,24 +84,16 @@ func (m *MockEC2) CreateEgressOnlyInternetGateway(request *ec2.CreateEgressOnlyI return response, nil } -func (m *MockEC2) DescribeEgressOnlyInternetGatewaysRequest(*ec2.DescribeEgressOnlyInternetGatewaysInput) (*request.Request, *ec2.DescribeEgressOnlyInternetGatewaysOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeEgressOnlyInternetGatewaysWithContext(aws.Context, *ec2.DescribeEgressOnlyInternetGatewaysInput, ...request.Option) (*ec2.DescribeEgressOnlyInternetGatewaysOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeEgressOnlyInternetGateways(request *ec2.DescribeEgressOnlyInternetGatewaysInput) (*ec2.DescribeEgressOnlyInternetGatewaysOutput, error) { +func (m *MockEC2) DescribeEgressOnlyInternetGateways(ctx context.Context, request *ec2.DescribeEgressOnlyInternetGatewaysInput, optFns ...func(*ec2.Options)) (*ec2.DescribeEgressOnlyInternetGatewaysOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DescribeEgressOnlyInternetGateways: %v", request) - var internetGateways []*ec2.EgressOnlyInternetGateway + var internetGateways []ec2types.EgressOnlyInternetGateway if len(request.EgressOnlyInternetGatewayIds) != 0 { - request.Filters = append(request.Filters, &ec2.Filter{Name: s("egress-only-internet-gateway-id"), Values: request.EgressOnlyInternetGatewayIds}) + request.Filters = append(request.Filters, ec2types.Filter{Name: s("egress-only-internet-gateway-id"), Values: request.EgressOnlyInternetGatewayIds}) } for id, internetGateway := range m.EgressOnlyInternetGateways { @@ -118,7 +103,7 @@ func (m *MockEC2) DescribeEgressOnlyInternetGateways(request *ec2.DescribeEgress switch *filter.Name { case "internet-gateway-id": for _, v := range filter.Values { - if id == aws.StringValue(v) { + if id == v { match = true } } @@ -127,7 +112,7 @@ func (m *MockEC2) DescribeEgressOnlyInternetGateways(request *ec2.DescribeEgress for _, v := range filter.Values { if internetGateway.Attachments != nil { for _, attachment := range internetGateway.Attachments { - if *attachment.VpcId == *v { + if *attachment.VpcId == v { match = true } } @@ -136,7 +121,7 @@ func (m *MockEC2) DescribeEgressOnlyInternetGateways(request *ec2.DescribeEgress default: if strings.HasPrefix(*filter.Name, "tag:") { - match = m.hasTag(ec2.ResourceTypeEgressOnlyInternetGateway, id, filter) + match = m.hasTag(ec2types.ResourceTypeEgressOnlyInternetGateway, id, filter) } else { return nil, fmt.Errorf("unknown filter name: %q", *filter.Name) } @@ -153,8 +138,8 @@ func (m *MockEC2) DescribeEgressOnlyInternetGateways(request *ec2.DescribeEgress } copy := *internetGateway - copy.Tags = m.getTags(ec2.ResourceTypeEgressOnlyInternetGateway, id) - internetGateways = append(internetGateways, ©) + copy.Tags = m.getTags(ec2types.ResourceTypeEgressOnlyInternetGateway, id) + internetGateways = append(internetGateways, copy) } response := &ec2.DescribeEgressOnlyInternetGatewaysOutput{ @@ -164,13 +149,13 @@ func (m *MockEC2) DescribeEgressOnlyInternetGateways(request *ec2.DescribeEgress return response, nil } -func (m *MockEC2) DeleteEgressOnlyInternetGateway(request *ec2.DeleteEgressOnlyInternetGatewayInput) (*ec2.DeleteEgressOnlyInternetGatewayOutput, error) { +func (m *MockEC2) DeleteEgressOnlyInternetGateway(ctx context.Context, request *ec2.DeleteEgressOnlyInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DeleteEgressOnlyInternetGatewayOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DeleteEgressOnlyInternetGateway: %v", request) - id := aws.StringValue(request.EgressOnlyInternetGatewayId) + id := aws.ToString(request.EgressOnlyInternetGatewayId) o := m.EgressOnlyInternetGateways[id] if o == nil { return nil, fmt.Errorf("EgressOnlyInternetGateway %q not found", id) @@ -179,11 +164,3 @@ func (m *MockEC2) DeleteEgressOnlyInternetGateway(request *ec2.DeleteEgressOnlyI return &ec2.DeleteEgressOnlyInternetGatewayOutput{}, nil } - -func (m *MockEC2) DeleteEgressOnlyInternetGatewayWithContext(aws.Context, *ec2.DeleteEgressOnlyInternetGatewayInput, ...request.Option) (*ec2.DeleteEgressOnlyInternetGatewayOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteEgressOnlyInternetGatewayRequest(*ec2.DeleteEgressOnlyInternetGatewayInput) (*request.Request, *ec2.DeleteEgressOnlyInternetGatewayOutput) { - panic("Not implemented") -} diff --git a/cloudmock/aws/mockec2/eni.go b/cloudmock/aws/mockec2/eni.go index f84fae1c0babc..3167e1609f3dc 100644 --- a/cloudmock/aws/mockec2/eni.go +++ b/cloudmock/aws/mockec2/eni.go @@ -16,13 +16,13 @@ limitations under the License. package mockec2 -import "github.com/aws/aws-sdk-go/service/ec2" +import ( + "context" -func (m *MockEC2) DescribeNetworkInterfaces(input *ec2.DescribeNetworkInterfacesInput) (*ec2.DescribeNetworkInterfacesOutput, error) { + "github.com/aws/aws-sdk-go-v2/service/ec2" +) + +func (m *MockEC2) DescribeNetworkInterfaces(ctx context.Context, params *ec2.DescribeNetworkInterfacesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeNetworkInterfacesOutput, error) { output := &ec2.DescribeNetworkInterfacesOutput{} return output, nil } - -func (m *MockEC2) DescribeNetworkInterfacesPages(*ec2.DescribeNetworkInterfacesInput, func(*ec2.DescribeNetworkInterfacesOutput, bool) bool) error { - return nil -} diff --git a/cloudmock/aws/mockec2/internetgateways.go b/cloudmock/aws/mockec2/internetgateways.go index 647ab7ac1081f..817e183b57809 100644 --- a/cloudmock/aws/mockec2/internetgateways.go +++ b/cloudmock/aws/mockec2/internetgateways.go @@ -17,16 +17,17 @@ limitations under the License. package mockec2 import ( + "context" "fmt" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) -func (m *MockEC2) FindInternetGateway(id string) *ec2.InternetGateway { +func (m *MockEC2) FindInternetGateway(id string) *ec2types.InternetGateway { m.mutex.Lock() defer m.mutex.Unlock() @@ -36,7 +37,7 @@ func (m *MockEC2) FindInternetGateway(id string) *ec2.InternetGateway { } copy := *internetGateway - copy.Tags = m.getTags(ec2.ResourceTypeInternetGateway, id) + copy.Tags = m.getTags(ec2types.ResourceTypeInternetGateway, id) return © } @@ -51,30 +52,22 @@ func (m *MockEC2) InternetGatewayIds() []string { return ids } -func (m *MockEC2) CreateInternetGatewayRequest(*ec2.CreateInternetGatewayInput) (*request.Request, *ec2.CreateInternetGatewayOutput) { - panic("Not implemented") -} - -func (m *MockEC2) CreateInternetGatewayWithContext(aws.Context, *ec2.CreateInternetGatewayInput, ...request.Option) (*ec2.CreateInternetGatewayOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) CreateInternetGateway(request *ec2.CreateInternetGatewayInput) (*ec2.CreateInternetGatewayOutput, error) { +func (m *MockEC2) CreateInternetGateway(ctx context.Context, request *ec2.CreateInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.CreateInternetGatewayOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("CreateInternetGateway: %v", request) id := m.allocateId("igw") - tags := tagSpecificationsToTags(request.TagSpecifications, ec2.ResourceTypeInternetGateway) + tags := tagSpecificationsToTags(request.TagSpecifications, ec2types.ResourceTypeInternetGateway) - igw := &ec2.InternetGateway{ + igw := &ec2types.InternetGateway{ InternetGatewayId: s(id), Tags: tags, } if m.InternetGateways == nil { - m.InternetGateways = make(map[string]*ec2.InternetGateway) + m.InternetGateways = make(map[string]*ec2types.InternetGateway) } m.InternetGateways[id] = igw @@ -86,24 +79,16 @@ func (m *MockEC2) CreateInternetGateway(request *ec2.CreateInternetGatewayInput) return response, nil } -func (m *MockEC2) DescribeInternetGatewaysRequest(*ec2.DescribeInternetGatewaysInput) (*request.Request, *ec2.DescribeInternetGatewaysOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeInternetGatewaysWithContext(aws.Context, *ec2.DescribeInternetGatewaysInput, ...request.Option) (*ec2.DescribeInternetGatewaysOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeInternetGateways(request *ec2.DescribeInternetGatewaysInput) (*ec2.DescribeInternetGatewaysOutput, error) { +func (m *MockEC2) DescribeInternetGateways(ctx context.Context, request *ec2.DescribeInternetGatewaysInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInternetGatewaysOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DescribeInternetGateways: %v", request) - var internetGateways []*ec2.InternetGateway + var internetGateways []ec2types.InternetGateway if len(request.InternetGatewayIds) != 0 { - request.Filters = append(request.Filters, &ec2.Filter{Name: s("internet-gateway-id"), Values: request.InternetGatewayIds}) + request.Filters = append(request.Filters, ec2types.Filter{Name: s("internet-gateway-id"), Values: request.InternetGatewayIds}) } for id, internetGateway := range m.InternetGateways { @@ -113,7 +98,7 @@ func (m *MockEC2) DescribeInternetGateways(request *ec2.DescribeInternetGateways switch *filter.Name { case "internet-gateway-id": for _, v := range filter.Values { - if id == aws.StringValue(v) { + if id == v { match = true } } @@ -122,7 +107,7 @@ func (m *MockEC2) DescribeInternetGateways(request *ec2.DescribeInternetGateways for _, v := range filter.Values { if internetGateway.Attachments != nil { for _, attachment := range internetGateway.Attachments { - if *attachment.VpcId == *v { + if *attachment.VpcId == v { match = true } } @@ -131,7 +116,7 @@ func (m *MockEC2) DescribeInternetGateways(request *ec2.DescribeInternetGateways default: if strings.HasPrefix(*filter.Name, "tag:") { - match = m.hasTag(ec2.ResourceTypeInternetGateway, id, filter) + match = m.hasTag(ec2types.ResourceTypeInternetGateway, id, filter) } else { return nil, fmt.Errorf("unknown filter name: %q", *filter.Name) } @@ -148,8 +133,8 @@ func (m *MockEC2) DescribeInternetGateways(request *ec2.DescribeInternetGateways } copy := *internetGateway - copy.Tags = m.getTags(ec2.ResourceTypeInternetGateway, id) - internetGateways = append(internetGateways, ©) + copy.Tags = m.getTags(ec2types.ResourceTypeInternetGateway, id) + internetGateways = append(internetGateways, copy) } response := &ec2.DescribeInternetGatewaysOutput{ @@ -159,14 +144,14 @@ func (m *MockEC2) DescribeInternetGateways(request *ec2.DescribeInternetGateways return response, nil } -func (m *MockEC2) AttachInternetGateway(request *ec2.AttachInternetGatewayInput) (*ec2.AttachInternetGatewayOutput, error) { +func (m *MockEC2) AttachInternetGateway(ctx context.Context, request *ec2.AttachInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.AttachInternetGatewayOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() for id, internetGateway := range m.InternetGateways { if id == *request.InternetGatewayId { internetGateway.Attachments = append(internetGateway.Attachments, - &ec2.InternetGatewayAttachment{ + ec2types.InternetGatewayAttachment{ VpcId: request.VpcId, }) return &ec2.AttachInternetGatewayOutput{}, nil @@ -176,24 +161,16 @@ func (m *MockEC2) AttachInternetGateway(request *ec2.AttachInternetGatewayInput) return nil, fmt.Errorf("InternetGateway not found") } -func (m *MockEC2) AttachInternetGatewayWithContext(aws.Context, *ec2.AttachInternetGatewayInput, ...request.Option) (*ec2.AttachInternetGatewayOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) AttachInternetGatewayRequest(*ec2.AttachInternetGatewayInput) (*request.Request, *ec2.AttachInternetGatewayOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DetachInternetGateway(request *ec2.DetachInternetGatewayInput) (*ec2.DetachInternetGatewayOutput, error) { +func (m *MockEC2) DetachInternetGateway(ctx context.Context, request *ec2.DetachInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DetachInternetGatewayOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() for id, igw := range m.InternetGateways { if id == *request.InternetGatewayId { found := false - var newAttachments []*ec2.InternetGatewayAttachment + var newAttachments []ec2types.InternetGatewayAttachment for _, a := range igw.Attachments { - if aws.StringValue(a.VpcId) == aws.StringValue(request.VpcId) { + if a.VpcId == request.VpcId { found = true continue } @@ -212,21 +189,13 @@ func (m *MockEC2) DetachInternetGateway(request *ec2.DetachInternetGatewayInput) return nil, fmt.Errorf("InternetGateway not found") } -func (m *MockEC2) DetachInternetGatewayWithContext(aws.Context, *ec2.DetachInternetGatewayInput, ...request.Option) (*ec2.DetachInternetGatewayOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DetachInternetGatewayRequest(*ec2.DetachInternetGatewayInput) (*request.Request, *ec2.DetachInternetGatewayOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteInternetGateway(request *ec2.DeleteInternetGatewayInput) (*ec2.DeleteInternetGatewayOutput, error) { +func (m *MockEC2) DeleteInternetGateway(ctx context.Context, request *ec2.DeleteInternetGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DeleteInternetGatewayOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DeleteInternetGateway: %v", request) - id := aws.StringValue(request.InternetGatewayId) + id := aws.ToString(request.InternetGatewayId) o := m.InternetGateways[id] if o == nil { return nil, fmt.Errorf("InternetGateway %q not found", id) @@ -235,11 +204,3 @@ func (m *MockEC2) DeleteInternetGateway(request *ec2.DeleteInternetGatewayInput) return &ec2.DeleteInternetGatewayOutput{}, nil } - -func (m *MockEC2) DeleteInternetGatewayWithContext(aws.Context, *ec2.DeleteInternetGatewayInput, ...request.Option) (*ec2.DeleteInternetGatewayOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteInternetGatewayRequest(*ec2.DeleteInternetGatewayInput) (*request.Request, *ec2.DeleteInternetGatewayOutput) { - panic("Not implemented") -} diff --git a/cloudmock/aws/mockec2/natgateway.go b/cloudmock/aws/mockec2/natgateway.go index cbcea93f0f8af..c42eed357a519 100644 --- a/cloudmock/aws/mockec2/natgateway.go +++ b/cloudmock/aws/mockec2/natgateway.go @@ -17,12 +17,13 @@ limitations under the License. package mockec2 import ( + "context" "fmt" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) @@ -30,25 +31,25 @@ func (m *MockEC2) CreateNatGatewayWithId(request *ec2.CreateNatGatewayInput, id m.mutex.Lock() defer m.mutex.Unlock() - tags := tagSpecificationsToTags(request.TagSpecifications, ec2.ResourceTypeNatgateway) + tags := tagSpecificationsToTags(request.TagSpecifications, ec2types.ResourceTypeNatgateway) - ngw := &ec2.NatGateway{ + ngw := &ec2types.NatGateway{ NatGatewayId: s(id), SubnetId: request.SubnetId, Tags: tags, } if request.AllocationId != nil { - var eip *ec2.Address + var eip *ec2types.Address for _, address := range m.Addresses { - if aws.StringValue(address.AllocationId) == *request.AllocationId { + if aws.ToString(address.AllocationId) == *request.AllocationId { eip = address } } if eip == nil { return nil, fmt.Errorf("AllocationId %q not found", *request.AllocationId) } - ngw.NatGatewayAddresses = append(ngw.NatGatewayAddresses, &ec2.NatGatewayAddress{ + ngw.NatGatewayAddresses = append(ngw.NatGatewayAddresses, ec2types.NatGatewayAddress{ AllocationId: eip.AllocationId, PrivateIp: eip.PrivateIpAddress, PublicIp: eip.PublicIp, @@ -56,7 +57,7 @@ func (m *MockEC2) CreateNatGatewayWithId(request *ec2.CreateNatGatewayInput, id } if m.NatGateways == nil { - m.NatGateways = make(map[string]*ec2.NatGateway) + m.NatGateways = make(map[string]*ec2types.NatGateway) } m.NatGateways[*ngw.NatGatewayId] = ngw @@ -70,14 +71,14 @@ func (m *MockEC2) CreateNatGatewayWithId(request *ec2.CreateNatGatewayInput, id }, nil } -func (m *MockEC2) CreateNatGateway(request *ec2.CreateNatGatewayInput) (*ec2.CreateNatGatewayOutput, error) { +func (m *MockEC2) CreateNatGateway(ctx context.Context, request *ec2.CreateNatGatewayInput, optFns ...func(*ec2.Options)) (*ec2.CreateNatGatewayOutput, error) { klog.Infof("CreateNatGateway: %v", request) id := m.allocateId("nat") return m.CreateNatGatewayWithId(request, id) } -func (m *MockEC2) WaitUntilNatGatewayAvailable(request *ec2.DescribeNatGatewaysInput) error { +/*func (m *MockEC2) WaitUntilNatGatewayAvailable(request *ec2.DescribeNatGatewaysInput) error { m.mutex.Lock() defer m.mutex.Unlock() @@ -96,30 +97,18 @@ func (m *MockEC2) WaitUntilNatGatewayAvailable(request *ec2.DescribeNatGatewaysI ngw.State = aws.String("Available") return nil -} - -func (m *MockEC2) WaitUntilNatGatewayAvailableWithContext(aws.Context, *ec2.DescribeNatGatewaysInput, ...request.WaiterOption) error { - panic("Not implemented") -} - -func (m *MockEC2) CreateNatGatewayWithContext(aws.Context, *ec2.CreateNatGatewayInput, ...request.Option) (*ec2.CreateNatGatewayOutput, error) { - panic("Not implemented") -} +}*/ -func (m *MockEC2) CreateNatGatewayRequest(*ec2.CreateNatGatewayInput) (*request.Request, *ec2.CreateNatGatewayOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeNatGateways(request *ec2.DescribeNatGatewaysInput) (*ec2.DescribeNatGatewaysOutput, error) { +func (m *MockEC2) DescribeNatGateways(ctx context.Context, request *ec2.DescribeNatGatewaysInput, optFns ...func(*ec2.Options)) (*ec2.DescribeNatGatewaysOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DescribeNatGateways: %v", request) - var ngws []*ec2.NatGateway + var ngws []ec2types.NatGateway if len(request.NatGatewayIds) != 0 { - request.Filter = append(request.Filter, &ec2.Filter{Name: s("nat-gateway-id"), Values: request.NatGatewayIds}) + request.Filter = append(request.Filter, ec2types.Filter{Name: s("nat-gateway-id"), Values: request.NatGatewayIds}) } for id, ngw := range m.NatGateways { @@ -129,13 +118,13 @@ func (m *MockEC2) DescribeNatGateways(request *ec2.DescribeNatGatewaysInput) (*e switch *filter.Name { case "nat-gateway-id": for _, v := range filter.Values { - if *ngw.NatGatewayId == *v { + if *ngw.NatGatewayId == v { match = true } } default: if strings.HasPrefix(*filter.Name, "tag:") { - match = m.hasTag(ec2.ResourceTypeNatgateway, *ngw.NatGatewayId, filter) + match = m.hasTag(ec2types.ResourceTypeNatgateway, *ngw.NatGatewayId, filter) } else { return nil, fmt.Errorf("unknown filter name: %q", *filter.Name) } @@ -152,8 +141,8 @@ func (m *MockEC2) DescribeNatGateways(request *ec2.DescribeNatGatewaysInput) (*e } copy := *ngw - copy.Tags = m.getTags(ec2.ResourceTypeNatgateway, id) - ngws = append(ngws, ©) + copy.Tags = m.getTags(ec2types.ResourceTypeNatgateway, id) + ngws = append(ngws, copy) } response := &ec2.DescribeNatGatewaysOutput{ @@ -163,29 +152,13 @@ func (m *MockEC2) DescribeNatGateways(request *ec2.DescribeNatGatewaysInput) (*e return response, nil } -func (m *MockEC2) DescribeNatGatewaysWithContext(aws.Context, *ec2.DescribeNatGatewaysInput, ...request.Option) (*ec2.DescribeNatGatewaysOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeNatGatewaysRequest(*ec2.DescribeNatGatewaysInput) (*request.Request, *ec2.DescribeNatGatewaysOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeNatGatewaysPages(*ec2.DescribeNatGatewaysInput, func(*ec2.DescribeNatGatewaysOutput, bool) bool) error { - panic("Not implemented") -} - -func (m *MockEC2) DescribeNatGatewaysPagesWithContext(aws.Context, *ec2.DescribeNatGatewaysInput, func(*ec2.DescribeNatGatewaysOutput, bool) bool, ...request.Option) error { - panic("Not implemented") -} - -func (m *MockEC2) DeleteNatGateway(request *ec2.DeleteNatGatewayInput) (*ec2.DeleteNatGatewayOutput, error) { +func (m *MockEC2) DeleteNatGateway(ctx context.Context, request *ec2.DeleteNatGatewayInput, optFns ...func(*ec2.Options)) (*ec2.DeleteNatGatewayOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DeleteNatGateway: %v", request) - id := aws.StringValue(request.NatGatewayId) + id := aws.ToString(request.NatGatewayId) o := m.NatGateways[id] if o == nil { return nil, fmt.Errorf("NatGateway %q not found", id) @@ -194,11 +167,3 @@ func (m *MockEC2) DeleteNatGateway(request *ec2.DeleteNatGatewayInput) (*ec2.Del return &ec2.DeleteNatGatewayOutput{}, nil } - -func (m *MockEC2) DeleteNatGatewayWithContext(aws.Context, *ec2.DeleteNatGatewayInput, ...request.Option) (*ec2.DeleteNatGatewayOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteNatGatewayRequest(*ec2.DeleteNatGatewayInput) (*request.Request, *ec2.DeleteNatGatewayOutput) { - panic("Not implemented") -} diff --git a/cloudmock/aws/mockec2/routetable.go b/cloudmock/aws/mockec2/routetable.go index fd3dd7e9cc0d1..030addf5903a2 100644 --- a/cloudmock/aws/mockec2/routetable.go +++ b/cloudmock/aws/mockec2/routetable.go @@ -17,20 +17,21 @@ limitations under the License. package mockec2 import ( + "context" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) -func (m *MockEC2) AddRouteTable(rt *ec2.RouteTable) { +func (m *MockEC2) AddRouteTable(rt *ec2types.RouteTable) { m.mutex.Lock() defer m.mutex.Unlock() if m.RouteTables == nil { - m.RouteTables = make(map[string]*ec2.RouteTable) + m.RouteTables = make(map[string]*ec2types.RouteTable) } m.addTags(*rt.RouteTableId, rt.Tags...) @@ -38,15 +39,7 @@ func (m *MockEC2) AddRouteTable(rt *ec2.RouteTable) { m.RouteTables[*rt.RouteTableId] = rt } -func (m *MockEC2) DescribeRouteTablesRequest(*ec2.DescribeRouteTablesInput) (*request.Request, *ec2.DescribeRouteTablesOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeRouteTablesWithContext(aws.Context, *ec2.DescribeRouteTablesInput, ...request.Option) (*ec2.DescribeRouteTablesOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeRouteTables(request *ec2.DescribeRouteTablesInput) (*ec2.DescribeRouteTablesOutput, error) { +func (m *MockEC2) DescribeRouteTables(ctx context.Context, request *ec2.DescribeRouteTablesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeRouteTablesOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -57,7 +50,7 @@ func (m *MockEC2) DescribeRouteTables(request *ec2.DescribeRouteTablesInput) (*e } if len(request.RouteTableIds) != 0 { - request.Filters = append(request.Filters, &ec2.Filter{Name: s("route-table-id"), Values: request.RouteTableIds}) + request.Filters = append(request.Filters, ec2types.Filter{Name: s("route-table-id"), Values: request.RouteTableIds}) } response := &ec2.DescribeRouteTablesOutput{} @@ -68,20 +61,20 @@ func (m *MockEC2) DescribeRouteTables(request *ec2.DescribeRouteTablesInput) (*e switch *filter.Name { case "route-table-id": for _, v := range filter.Values { - if *rt.RouteTableId == *v { + if *rt.RouteTableId == v { match = true } } case "association.subnet-id": for _, a := range rt.Associations { for _, v := range filter.Values { - if aws.StringValue(a.SubnetId) == *v { + if aws.ToString(a.SubnetId) == v { match = true } } } default: - match = m.hasTag(ec2.ResourceTypeRouteTable, *rt.RouteTableId, filter) + match = m.hasTag(ec2types.ResourceTypeRouteTable, *rt.RouteTableId, filter) } if !match { @@ -95,14 +88,14 @@ func (m *MockEC2) DescribeRouteTables(request *ec2.DescribeRouteTablesInput) (*e } copy := *rt - copy.Tags = m.getTags(ec2.ResourceTypeRouteTable, *rt.RouteTableId) - response.RouteTables = append(response.RouteTables, ©) + copy.Tags = m.getTags(ec2types.ResourceTypeRouteTable, *rt.RouteTableId) + response.RouteTables = append(response.RouteTables, copy) } return response, nil } -func (m *MockEC2) CreateRouteTable(request *ec2.CreateRouteTableInput) (*ec2.CreateRouteTableOutput, error) { +func (m *MockEC2) CreateRouteTable(ctx context.Context, request *ec2.CreateRouteTableInput, optFns ...func(*ec2.Options)) (*ec2.CreateRouteTableOutput, error) { klog.Infof("CreateRouteTable: %v", request) id := m.allocateId("rtb") @@ -117,13 +110,13 @@ func (m *MockEC2) CreateRouteTableWithId(request *ec2.CreateRouteTableInput, id klog.Fatalf("DryRun") } - rt := &ec2.RouteTable{ + rt := &ec2types.RouteTable{ RouteTableId: s(id), VpcId: request.VpcId, } if m.RouteTables == nil { - m.RouteTables = make(map[string]*ec2.RouteTable) + m.RouteTables = make(map[string]*ec2types.RouteTable) } m.RouteTables[id] = rt @@ -134,15 +127,7 @@ func (m *MockEC2) CreateRouteTableWithId(request *ec2.CreateRouteTableInput, id return response, nil } -func (m *MockEC2) CreateRouteTableWithContext(aws.Context, *ec2.CreateRouteTableInput, ...request.Option) (*ec2.CreateRouteTableOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) CreateRouteTableRequest(*ec2.CreateRouteTableInput) (*request.Request, *ec2.CreateRouteTableOutput) { - panic("Not implemented") -} - -func (m *MockEC2) CreateRoute(request *ec2.CreateRouteInput) (*ec2.CreateRouteOutput, error) { +func (m *MockEC2) CreateRoute(ctx context.Context, request *ec2.CreateRouteInput, optFns ...func(*ec2.Options)) (*ec2.CreateRouteOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -152,12 +137,12 @@ func (m *MockEC2) CreateRoute(request *ec2.CreateRouteInput) (*ec2.CreateRouteOu klog.Fatalf("DryRun") } - rt := m.RouteTables[aws.StringValue(request.RouteTableId)] + rt := m.RouteTables[aws.ToString(request.RouteTableId)] if rt == nil { return nil, fmt.Errorf("RouteTable not found") } - r := &ec2.Route{ + r := ec2types.Route{ DestinationCidrBlock: request.DestinationCidrBlock, DestinationIpv6CidrBlock: request.DestinationIpv6CidrBlock, EgressOnlyInternetGatewayId: request.EgressOnlyInternetGatewayId, @@ -173,21 +158,13 @@ func (m *MockEC2) CreateRoute(request *ec2.CreateRouteInput) (*ec2.CreateRouteOu return &ec2.CreateRouteOutput{Return: aws.Bool(true)}, nil } -func (m *MockEC2) CreateRouteWithContext(aws.Context, *ec2.CreateRouteInput, ...request.Option) (*ec2.CreateRouteOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) CreateRouteRequest(*ec2.CreateRouteInput) (*request.Request, *ec2.CreateRouteOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteRouteTable(request *ec2.DeleteRouteTableInput) (*ec2.DeleteRouteTableOutput, error) { +func (m *MockEC2) DeleteRouteTable(ctx context.Context, request *ec2.DeleteRouteTableInput, optFns ...func(*ec2.Options)) (*ec2.DeleteRouteTableOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DeleteRouteTable: %v", request) - id := aws.StringValue(request.RouteTableId) + id := aws.ToString(request.RouteTableId) o := m.RouteTables[id] if o == nil { return nil, fmt.Errorf("RouteTable %q not found", id) @@ -196,11 +173,3 @@ func (m *MockEC2) DeleteRouteTable(request *ec2.DeleteRouteTableInput) (*ec2.Del return &ec2.DeleteRouteTableOutput{}, nil } - -func (m *MockEC2) DeleteRouteTableWithContext(aws.Context, *ec2.DeleteRouteTableInput, ...request.Option) (*ec2.DeleteRouteTableOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteRouteTableRequest(*ec2.DeleteRouteTableInput) (*request.Request, *ec2.DeleteRouteTableOutput) { - panic("Not implemented") -} diff --git a/cloudmock/aws/mockec2/securitygroups.go b/cloudmock/aws/mockec2/securitygroups.go index 61ed49596ca5d..57064c80e14cf 100644 --- a/cloudmock/aws/mockec2/securitygroups.go +++ b/cloudmock/aws/mockec2/securitygroups.go @@ -17,23 +17,16 @@ limitations under the License. package mockec2 import ( + "context" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) -func (m *MockEC2) CreateSecurityGroupRequest(*ec2.CreateSecurityGroupInput) (*request.Request, *ec2.CreateSecurityGroupOutput) { - panic("MockEC2 CreateSecurityGroupRequest not implemented") -} - -func (m *MockEC2) CreateSecurityGroupWithContext(aws.Context, *ec2.CreateSecurityGroupInput, ...request.Option) (*ec2.CreateSecurityGroupOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) CreateSecurityGroup(request *ec2.CreateSecurityGroupInput) (*ec2.CreateSecurityGroupOutput, error) { +func (m *MockEC2) CreateSecurityGroup(ctx context.Context, request *ec2.CreateSecurityGroupInput, optFns ...func(*ec2.Options)) (*ec2.CreateSecurityGroupOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -42,9 +35,9 @@ func (m *MockEC2) CreateSecurityGroup(request *ec2.CreateSecurityGroupInput) (*e m.securityGroupNumber++ n := m.securityGroupNumber id := fmt.Sprintf("sg-%d", n) - tags := tagSpecificationsToTags(request.TagSpecifications, ec2.ResourceTypeSecurityGroup) + tags := tagSpecificationsToTags(request.TagSpecifications, ec2types.ResourceTypeSecurityGroup) - sg := &ec2.SecurityGroup{ + sg := &ec2types.SecurityGroup{ GroupName: request.GroupName, GroupId: s(id), VpcId: request.VpcId, @@ -52,7 +45,7 @@ func (m *MockEC2) CreateSecurityGroup(request *ec2.CreateSecurityGroupInput) (*e Tags: tags, } if m.SecurityGroups == nil { - m.SecurityGroups = make(map[string]*ec2.SecurityGroup) + m.SecurityGroups = make(map[string]*ec2types.SecurityGroup) } m.SecurityGroups[*sg.GroupId] = sg @@ -64,21 +57,13 @@ func (m *MockEC2) CreateSecurityGroup(request *ec2.CreateSecurityGroupInput) (*e return response, nil } -func (m *MockEC2) DeleteSecurityGroupRequest(request *ec2.DeleteSecurityGroupInput) (*request.Request, *ec2.DeleteSecurityGroupOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteSecurityGroupWithContext(aws.Context, *ec2.DeleteSecurityGroupInput, ...request.Option) (*ec2.DeleteSecurityGroupOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteSecurityGroup(request *ec2.DeleteSecurityGroupInput) (*ec2.DeleteSecurityGroupOutput, error) { +func (m *MockEC2) DeleteSecurityGroup(ctx context.Context, request *ec2.DeleteSecurityGroupInput, optFns ...func(*ec2.Options)) (*ec2.DeleteSecurityGroupOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DeleteSecurityGroup: %v", request) - id := aws.StringValue(request.GroupId) + id := aws.ToString(request.GroupId) o := m.SecurityGroups[id] if o == nil { return nil, fmt.Errorf("SecurityGroup %q not found", id) @@ -88,37 +73,17 @@ func (m *MockEC2) DeleteSecurityGroup(request *ec2.DeleteSecurityGroupInput) (*e return &ec2.DeleteSecurityGroupOutput{}, nil } -func (m *MockEC2) DescribeSecurityGroupReferencesRequest(*ec2.DescribeSecurityGroupReferencesInput) (*request.Request, *ec2.DescribeSecurityGroupReferencesOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeSecurityGroupReferencesWithContext(aws.Context, *ec2.DescribeSecurityGroupReferencesInput, ...request.Option) (*ec2.DescribeSecurityGroupReferencesOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeSecurityGroupReferences(*ec2.DescribeSecurityGroupReferencesInput) (*ec2.DescribeSecurityGroupReferencesOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeSecurityGroupsRequest(*ec2.DescribeSecurityGroupsInput) (*request.Request, *ec2.DescribeSecurityGroupsOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeSecurityGroupsWithContext(aws.Context, *ec2.DescribeSecurityGroupsInput, ...request.Option) (*ec2.DescribeSecurityGroupsOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeSecurityGroups(request *ec2.DescribeSecurityGroupsInput) (*ec2.DescribeSecurityGroupsOutput, error) { +func (m *MockEC2) DescribeSecurityGroups(ctx context.Context, request *ec2.DescribeSecurityGroupsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeSecurityGroupsOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DescribeSecurityGroups: %v", request) if len(request.GroupIds) != 0 { - request.Filters = append(request.Filters, &ec2.Filter{Name: s("group-id"), Values: request.GroupIds}) + request.Filters = append(request.Filters, ec2types.Filter{Name: s("group-id"), Values: request.GroupIds}) } - var groups []*ec2.SecurityGroup + var groups []ec2types.SecurityGroup for _, sg := range m.SecurityGroups { allFiltersMatch := true @@ -127,26 +92,26 @@ func (m *MockEC2) DescribeSecurityGroups(request *ec2.DescribeSecurityGroupsInpu switch *filter.Name { case "vpc-id": for _, v := range filter.Values { - if sg.VpcId != nil && *sg.VpcId == *v { + if sg.VpcId != nil && *sg.VpcId == v { match = true } } case "group-name": for _, v := range filter.Values { - if sg.GroupName != nil && *sg.GroupName == *v { + if sg.GroupName != nil && *sg.GroupName == v { match = true } } case "group-id": for _, v := range filter.Values { - if sg.GroupId != nil && *sg.GroupId == *v { + if sg.GroupId != nil && *sg.GroupId == v { match = true } } default: - match = m.hasTag(ec2.ResourceTypeSecurityGroup, *sg.GroupId, filter) + match = m.hasTag(ec2types.ResourceTypeSecurityGroup, *sg.GroupId, filter) } if !match { @@ -160,8 +125,8 @@ func (m *MockEC2) DescribeSecurityGroups(request *ec2.DescribeSecurityGroupsInpu } copy := *sg - copy.Tags = m.getTags(ec2.ResourceTypeSecurityGroup, *sg.GroupId) - groups = append(groups, ©) + copy.Tags = m.getTags(ec2types.ResourceTypeSecurityGroup, *sg.GroupId) + groups = append(groups, copy) } response := &ec2.DescribeSecurityGroupsOutput{ @@ -171,45 +136,17 @@ func (m *MockEC2) DescribeSecurityGroups(request *ec2.DescribeSecurityGroupsInpu return response, nil } -func (m *MockEC2) DescribeStaleSecurityGroupsRequest(*ec2.DescribeStaleSecurityGroupsInput) (*request.Request, *ec2.DescribeStaleSecurityGroupsOutput) { +func (m *MockEC2) RevokeSecurityGroupEgress(ctx context.Context, request *ec2.RevokeSecurityGroupEgressInput, optFns ...func(*ec2.Options)) (*ec2.RevokeSecurityGroupEgressOutput, error) { panic("Not implemented") } -func (m *MockEC2) DescribeStaleSecurityGroupsWithContext(aws.Context, *ec2.DescribeStaleSecurityGroupsInput, ...request.Option) (*ec2.DescribeStaleSecurityGroupsOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeStaleSecurityGroups(*ec2.DescribeStaleSecurityGroupsInput) (*ec2.DescribeStaleSecurityGroupsOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) RevokeSecurityGroupEgressRequest(*ec2.RevokeSecurityGroupEgressInput) (*request.Request, *ec2.RevokeSecurityGroupEgressOutput) { - panic("Not implemented") -} - -func (m *MockEC2) RevokeSecurityGroupEgressWithContext(aws.Context, *ec2.RevokeSecurityGroupEgressInput, ...request.Option) (*ec2.RevokeSecurityGroupEgressOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) RevokeSecurityGroupEgress(*ec2.RevokeSecurityGroupEgressInput) (*ec2.RevokeSecurityGroupEgressOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) RevokeSecurityGroupIngressRequest(*ec2.RevokeSecurityGroupIngressInput) (*request.Request, *ec2.RevokeSecurityGroupIngressOutput) { - panic("Not implemented") -} - -func (m *MockEC2) RevokeSecurityGroupIngressWithContext(aws.Context, *ec2.RevokeSecurityGroupIngressInput, ...request.Option) (*ec2.RevokeSecurityGroupIngressOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) RevokeSecurityGroupIngress(request *ec2.RevokeSecurityGroupIngressInput) (*ec2.RevokeSecurityGroupIngressOutput, error) { +func (m *MockEC2) RevokeSecurityGroupIngress(ctx context.Context, request *ec2.RevokeSecurityGroupIngressInput, optFns ...func(*ec2.Options)) (*ec2.RevokeSecurityGroupIngressOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("RevokeSecurityGroupIngress: %v", request) - if aws.StringValue(request.GroupId) == "" { + if aws.ToString(request.GroupId) == "" { return nil, fmt.Errorf("GroupId not specified") } @@ -231,21 +168,13 @@ func (m *MockEC2) RevokeSecurityGroupIngress(request *ec2.RevokeSecurityGroupIng return response, nil } -func (m *MockEC2) AuthorizeSecurityGroupEgressRequest(*ec2.AuthorizeSecurityGroupEgressInput) (*request.Request, *ec2.AuthorizeSecurityGroupEgressOutput) { - panic("Not implemented") -} - -func (m *MockEC2) AuthorizeSecurityGroupEgressWithContext(aws.Context, *ec2.AuthorizeSecurityGroupEgressInput, ...request.Option) (*ec2.AuthorizeSecurityGroupEgressOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) AuthorizeSecurityGroupEgress(request *ec2.AuthorizeSecurityGroupEgressInput) (*ec2.AuthorizeSecurityGroupEgressOutput, error) { +func (m *MockEC2) AuthorizeSecurityGroupEgress(ctx context.Context, request *ec2.AuthorizeSecurityGroupEgressInput, optFns ...func(*ec2.Options)) (*ec2.AuthorizeSecurityGroupEgressOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("AuthorizeSecurityGroupEgress: %v", request) - if aws.StringValue(request.GroupId) == "" { + if aws.ToString(request.GroupId) == "" { return nil, fmt.Errorf("GroupId not specified") } @@ -266,14 +195,14 @@ func (m *MockEC2) AuthorizeSecurityGroupEgress(request *ec2.AuthorizeSecurityGro klog.Fatalf("SourceSecurityGroupOwnerId not implemented") } - p := &ec2.IpPermission{ + p := ec2types.IpPermission{ FromPort: request.FromPort, ToPort: request.ToPort, IpProtocol: request.IpProtocol, } if request.CidrIp != nil { - p.IpRanges = append(p.IpRanges, &ec2.IpRange{CidrIp: request.CidrIp}) + p.IpRanges = append(p.IpRanges, ec2types.IpRange{CidrIp: request.CidrIp}) } sg.IpPermissionsEgress = append(sg.IpPermissionsEgress, p) @@ -284,7 +213,7 @@ func (m *MockEC2) AuthorizeSecurityGroupEgress(request *ec2.AuthorizeSecurityGro // TODO: We need to fold permissions if m.SecurityGroupRules == nil { - m.SecurityGroupRules = make(map[string]*ec2.SecurityGroupRule) + m.SecurityGroupRules = make(map[string]*ec2types.SecurityGroupRule) } for _, permission := range request.IpPermissions { @@ -293,7 +222,7 @@ func (m *MockEC2) AuthorizeSecurityGroupEgress(request *ec2.AuthorizeSecurityGro n := len(m.SecurityGroupRules) + 1 id := fmt.Sprintf("sgr-%d", n) - rule := &ec2.SecurityGroupRule{ + rule := &ec2types.SecurityGroupRule{ SecurityGroupRuleId: &id, GroupId: sg.GroupId, FromPort: permission.FromPort, @@ -301,13 +230,13 @@ func (m *MockEC2) AuthorizeSecurityGroupEgress(request *ec2.AuthorizeSecurityGro IsEgress: aws.Bool(true), CidrIpv4: iprange.CidrIp, IpProtocol: permission.IpProtocol, - Tags: tagSpecificationsToTags(request.TagSpecifications, ec2.ResourceTypeSecurityGroupRule), + Tags: tagSpecificationsToTags(request.TagSpecifications, ec2types.ResourceTypeSecurityGroupRule), } if permission.FromPort == nil { - rule.FromPort = aws.Int64(int64(-1)) + rule.FromPort = aws.Int32(int32(-1)) } if permission.ToPort == nil { - rule.ToPort = aws.Int64(int64(-1)) + rule.ToPort = aws.Int32(int32(-1)) } m.SecurityGroupRules[id] = rule @@ -317,7 +246,7 @@ func (m *MockEC2) AuthorizeSecurityGroupEgress(request *ec2.AuthorizeSecurityGro n := len(m.SecurityGroupRules) + 1 id := fmt.Sprintf("sgr-%d", n) - rule := &ec2.SecurityGroupRule{ + rule := &ec2types.SecurityGroupRule{ SecurityGroupRuleId: &id, GroupId: sg.GroupId, FromPort: permission.FromPort, @@ -325,13 +254,13 @@ func (m *MockEC2) AuthorizeSecurityGroupEgress(request *ec2.AuthorizeSecurityGro IsEgress: aws.Bool(true), CidrIpv6: iprange.CidrIpv6, IpProtocol: permission.IpProtocol, - Tags: tagSpecificationsToTags(request.TagSpecifications, ec2.ResourceTypeSecurityGroupRule), + Tags: tagSpecificationsToTags(request.TagSpecifications, ec2types.ResourceTypeSecurityGroupRule), } if permission.FromPort == nil { - rule.FromPort = aws.Int64(int64(-1)) + rule.FromPort = aws.Int32(int32(-1)) } if permission.ToPort == nil { - rule.ToPort = aws.Int64(int64(-1)) + rule.ToPort = aws.Int32(int32(-1)) } m.SecurityGroupRules[id] = rule @@ -342,21 +271,13 @@ func (m *MockEC2) AuthorizeSecurityGroupEgress(request *ec2.AuthorizeSecurityGro return response, nil } -func (m *MockEC2) AuthorizeSecurityGroupIngressRequest(*ec2.AuthorizeSecurityGroupIngressInput) (*request.Request, *ec2.AuthorizeSecurityGroupIngressOutput) { - panic("Not implemented") -} - -func (m *MockEC2) AuthorizeSecurityGroupIngressWithContext(aws.Context, *ec2.AuthorizeSecurityGroupIngressInput, ...request.Option) (*ec2.AuthorizeSecurityGroupIngressOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) AuthorizeSecurityGroupIngress(request *ec2.AuthorizeSecurityGroupIngressInput) (*ec2.AuthorizeSecurityGroupIngressOutput, error) { +func (m *MockEC2) AuthorizeSecurityGroupIngress(ctx context.Context, request *ec2.AuthorizeSecurityGroupIngressInput, optFns ...func(*ec2.Options)) (*ec2.AuthorizeSecurityGroupIngressOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("AuthorizeSecurityGroupIngress: %v", request) - if aws.StringValue(request.GroupId) == "" { + if aws.ToString(request.GroupId) == "" { return nil, fmt.Errorf("GroupId not specified") } @@ -380,14 +301,14 @@ func (m *MockEC2) AuthorizeSecurityGroupIngress(request *ec2.AuthorizeSecurityGr klog.Fatalf("SourceSecurityGroupOwnerId not implemented") } - p := &ec2.IpPermission{ + p := ec2types.IpPermission{ FromPort: request.FromPort, ToPort: request.ToPort, IpProtocol: request.IpProtocol, } if request.CidrIp != nil { - p.IpRanges = append(p.IpRanges, &ec2.IpRange{CidrIp: request.CidrIp}) + p.IpRanges = append(p.IpRanges, ec2types.IpRange{CidrIp: request.CidrIp}) } sg.IpPermissions = append(sg.IpPermissions, p) @@ -398,26 +319,26 @@ func (m *MockEC2) AuthorizeSecurityGroupIngress(request *ec2.AuthorizeSecurityGr // TODO: We need to fold permissions if m.SecurityGroupRules == nil { - m.SecurityGroupRules = make(map[string]*ec2.SecurityGroupRule) + m.SecurityGroupRules = make(map[string]*ec2types.SecurityGroupRule) } - newSecurityGroupRule := func(permission *ec2.IpPermission) (string, *ec2.SecurityGroupRule) { + newSecurityGroupRule := func(permission ec2types.IpPermission) (string, *ec2types.SecurityGroupRule) { n := len(m.SecurityGroupRules) + 1 id := fmt.Sprintf("sgr-%d", n) - rule := &ec2.SecurityGroupRule{ + rule := &ec2types.SecurityGroupRule{ SecurityGroupRuleId: &id, GroupId: sg.GroupId, FromPort: permission.FromPort, ToPort: permission.ToPort, IsEgress: aws.Bool(false), IpProtocol: permission.IpProtocol, - Tags: tagSpecificationsToTags(request.TagSpecifications, ec2.ResourceTypeSecurityGroupRule), + Tags: tagSpecificationsToTags(request.TagSpecifications, ec2types.ResourceTypeSecurityGroupRule), } if permission.FromPort == nil { - rule.FromPort = aws.Int64(int64(-1)) + rule.FromPort = aws.Int32(int32(-1)) } if permission.ToPort == nil { - rule.ToPort = aws.Int64(int64(-1)) + rule.ToPort = aws.Int32(int32(-1)) } return id, rule @@ -446,7 +367,7 @@ func (m *MockEC2) AuthorizeSecurityGroupIngress(request *ec2.AuthorizeSecurityGr for _, group := range permission.UserIdGroupPairs { id, rule := newSecurityGroupRule(permission) - rule.ReferencedGroupInfo = &ec2.ReferencedSecurityGroup{ + rule.ReferencedGroupInfo = &ec2types.ReferencedSecurityGroup{ GroupId: group.GroupId, } m.SecurityGroupRules[id] = rule @@ -457,22 +378,22 @@ func (m *MockEC2) AuthorizeSecurityGroupIngress(request *ec2.AuthorizeSecurityGr return response, nil } -func (m *MockEC2) DescribeSecurityGroupRules(request *ec2.DescribeSecurityGroupRulesInput) (*ec2.DescribeSecurityGroupRulesOutput, error) { +func (m *MockEC2) DescribeSecurityGroupRules(ctx context.Context, request *ec2.DescribeSecurityGroupRulesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeSecurityGroupRulesOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() - rules := []*ec2.SecurityGroupRule{} + rules := []ec2types.SecurityGroupRule{} sgid := "" for _, filter := range request.Filters { - if aws.StringValue(filter.Name) == "group-id" { - sgid = aws.StringValue(filter.Values[0]) + if aws.ToString(filter.Name) == "group-id" { + sgid = filter.Values[0] } } for _, rule := range m.SecurityGroupRules { - if aws.StringValue(rule.GroupId) == sgid { - rules = append(rules, rule) + if aws.ToString(rule.GroupId) == sgid { + rules = append(rules, *rule) } } diff --git a/cloudmock/aws/mockec2/subnets.go b/cloudmock/aws/mockec2/subnets.go index 0ffa89ef30330..f672d3a4a710f 100644 --- a/cloudmock/aws/mockec2/subnets.go +++ b/cloudmock/aws/mockec2/subnets.go @@ -17,20 +17,21 @@ limitations under the License. package mockec2 import ( + "context" "fmt" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) type subnetInfo struct { - main ec2.Subnet + main ec2types.Subnet } -func (m *MockEC2) FindSubnet(id string) *ec2.Subnet { +func (m *MockEC2) FindSubnet(id string) *ec2types.Subnet { m.mutex.Lock() defer m.mutex.Unlock() @@ -40,7 +41,7 @@ func (m *MockEC2) FindSubnet(id string) *ec2.Subnet { } copy := subnet.main - copy.Tags = m.getTags(ec2.ResourceTypeSubnet, id) + copy.Tags = m.getTags(ec2types.ResourceTypeSubnet, id) return © } @@ -55,37 +56,29 @@ func (m *MockEC2) SubnetIds() []string { return ids } -func (m *MockEC2) CreateSubnetRequest(*ec2.CreateSubnetInput) (*request.Request, *ec2.CreateSubnetOutput) { - panic("Not implemented") -} - -func (m *MockEC2) CreateSubnetWithContext(aws.Context, *ec2.CreateSubnetInput, ...request.Option) (*ec2.CreateSubnetOutput, error) { - panic("Not implemented") -} - func (m *MockEC2) CreateSubnetWithId(request *ec2.CreateSubnetInput, id string) (*ec2.CreateSubnetOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() - subnet := &ec2.Subnet{ + subnet := &ec2types.Subnet{ SubnetId: s(id), VpcId: request.VpcId, CidrBlock: request.CidrBlock, AvailabilityZone: request.AvailabilityZone, - PrivateDnsNameOptionsOnLaunch: &ec2.PrivateDnsNameOptionsOnLaunch{ + PrivateDnsNameOptionsOnLaunch: &ec2types.PrivateDnsNameOptionsOnLaunch{ EnableResourceNameDnsAAAARecord: aws.Bool(false), EnableResourceNameDnsARecord: aws.Bool(false), - HostnameType: aws.String(ec2.HostnameTypeIpName), + HostnameType: ec2types.HostnameTypeIpName, }, } if request.Ipv6CidrBlock != nil { - subnet.Ipv6CidrBlockAssociationSet = []*ec2.SubnetIpv6CidrBlockAssociation{ + subnet.Ipv6CidrBlockAssociationSet = []ec2types.SubnetIpv6CidrBlockAssociation{ { AssociationId: aws.String("subnet-cidr-assoc-ipv6-" + id), Ipv6CidrBlock: request.Ipv6CidrBlock, - Ipv6CidrBlockState: &ec2.SubnetCidrBlockState{ - State: aws.String(ec2.SubnetCidrBlockStateCodeAssociated), + Ipv6CidrBlockState: &ec2types.SubnetCidrBlockState{ + State: ec2types.SubnetCidrBlockStateCodeAssociated, }, }, } @@ -98,7 +91,7 @@ func (m *MockEC2) CreateSubnetWithId(request *ec2.CreateSubnetInput, id string) main: *subnet, } - m.addTags(id, tagSpecificationsToTags(request.TagSpecifications, ec2.ResourceTypeSubnet)...) + m.addTags(id, tagSpecificationsToTags(request.TagSpecifications, ec2types.ResourceTypeSubnet)...) response := &ec2.CreateSubnetOutput{ Subnet: subnet, @@ -106,32 +99,24 @@ func (m *MockEC2) CreateSubnetWithId(request *ec2.CreateSubnetInput, id string) return response, nil } -func (m *MockEC2) CreateSubnet(request *ec2.CreateSubnetInput) (*ec2.CreateSubnetOutput, error) { +func (m *MockEC2) CreateSubnet(ctx context.Context, request *ec2.CreateSubnetInput, optFns ...func(*ec2.Options)) (*ec2.CreateSubnetOutput, error) { klog.Infof("CreateSubnet: %v", request) id := m.allocateId("subnet") return m.CreateSubnetWithId(request, id) } -func (m *MockEC2) DescribeSubnetsRequest(*ec2.DescribeSubnetsInput) (*request.Request, *ec2.DescribeSubnetsOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeSubnetsWithContext(aws.Context, *ec2.DescribeSubnetsInput, ...request.Option) (*ec2.DescribeSubnetsOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeSubnets(request *ec2.DescribeSubnetsInput) (*ec2.DescribeSubnetsOutput, error) { +func (m *MockEC2) DescribeSubnets(ctx context.Context, request *ec2.DescribeSubnetsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeSubnetsOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DescribeSubnets: %v", request) if len(request.SubnetIds) != 0 { - request.Filters = append(request.Filters, &ec2.Filter{Name: s("subnet-id"), Values: request.SubnetIds}) + request.Filters = append(request.Filters, ec2types.Filter{Name: s("subnet-id"), Values: request.SubnetIds}) } - var subnets []*ec2.Subnet + var subnets []ec2types.Subnet for id, subnet := range m.subnets { allFiltersMatch := true @@ -139,18 +124,18 @@ func (m *MockEC2) DescribeSubnets(request *ec2.DescribeSubnetsInput) (*ec2.Descr match := false switch *filter.Name { case "vpc-id": - if *subnet.main.VpcId == *filter.Values[0] { + if *subnet.main.VpcId == filter.Values[0] { match = true } case "subnet-id": for _, v := range filter.Values { - if *subnet.main.SubnetId == *v { + if *subnet.main.SubnetId == v { match = true } } default: if strings.HasPrefix(*filter.Name, "tag:") { - match = m.hasTag(ec2.ResourceTypeSubnet, *subnet.main.SubnetId, filter) + match = m.hasTag(ec2types.ResourceTypeSubnet, *subnet.main.SubnetId, filter) } else { return nil, fmt.Errorf("unknown filter name: %q", *filter.Name) } @@ -167,8 +152,8 @@ func (m *MockEC2) DescribeSubnets(request *ec2.DescribeSubnetsInput) (*ec2.Descr } copy := subnet.main - copy.Tags = m.getTags(ec2.ResourceTypeSubnet, id) - subnets = append(subnets, ©) + copy.Tags = m.getTags(ec2types.ResourceTypeSubnet, id) + subnets = append(subnets, copy) } response := &ec2.DescribeSubnetsOutput{ @@ -178,16 +163,16 @@ func (m *MockEC2) DescribeSubnets(request *ec2.DescribeSubnetsInput) (*ec2.Descr return response, nil } -func (m *MockEC2) AssociateRouteTable(request *ec2.AssociateRouteTableInput) (*ec2.AssociateRouteTableOutput, error) { +func (m *MockEC2) AssociateRouteTable(ctx context.Context, request *ec2.AssociateRouteTableInput, optFns ...func(*ec2.Options)) (*ec2.AssociateRouteTableOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("AuthorizeSecurityGroupIngress: %v", request) - if aws.StringValue(request.SubnetId) == "" { + if aws.ToString(request.SubnetId) == "" { return nil, fmt.Errorf("SubnetId not specified") } - if aws.StringValue(request.RouteTableId) == "" { + if aws.ToString(request.RouteTableId) == "" { return nil, fmt.Errorf("RouteTableId not specified") } @@ -206,7 +191,7 @@ func (m *MockEC2) AssociateRouteTable(request *ec2.AssociateRouteTableInput) (*e associationID := m.allocateId("rta") - rt.Associations = append(rt.Associations, &ec2.RouteTableAssociation{ + rt.Associations = append(rt.Associations, ec2types.RouteTableAssociation{ RouteTableId: rt.RouteTableId, SubnetId: subnet.main.SubnetId, RouteTableAssociationId: &associationID, @@ -223,21 +208,13 @@ func (m *MockEC2) AssociateRouteTable(request *ec2.AssociateRouteTableInput) (*e return response, nil } -func (m *MockEC2) AssociateRouteTableWithContext(aws.Context, *ec2.AssociateRouteTableInput, ...request.Option) (*ec2.AssociateRouteTableOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) AssociateRouteTableRequest(*ec2.AssociateRouteTableInput) (*request.Request, *ec2.AssociateRouteTableOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteSubnet(request *ec2.DeleteSubnetInput) (*ec2.DeleteSubnetOutput, error) { +func (m *MockEC2) DeleteSubnet(ctx context.Context, request *ec2.DeleteSubnetInput, optFns ...func(*ec2.Options)) (*ec2.DeleteSubnetOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DeleteSubnet: %v", request) - id := aws.StringValue(request.SubnetId) + id := aws.ToString(request.SubnetId) o := m.subnets[id] if o == nil { return nil, fmt.Errorf("Subnet %q not found", id) @@ -247,15 +224,7 @@ func (m *MockEC2) DeleteSubnet(request *ec2.DeleteSubnetInput) (*ec2.DeleteSubne return &ec2.DeleteSubnetOutput{}, nil } -func (m *MockEC2) DeleteSubnetWithContext(aws.Context, *ec2.DeleteSubnetInput, ...request.Option) (*ec2.DeleteSubnetOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteSubnetRequest(*ec2.DeleteSubnetInput) (*request.Request, *ec2.DeleteSubnetOutput) { - panic("Not implemented") -} - -func (m *MockEC2) ModifySubnetAttribute(request *ec2.ModifySubnetAttributeInput) (*ec2.ModifySubnetAttributeOutput, error) { +func (m *MockEC2) ModifySubnetAttribute(ctx context.Context, request *ec2.ModifySubnetAttributeInput, optFns ...func(*ec2.Options)) (*ec2.ModifySubnetAttributeOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -269,7 +238,7 @@ func (m *MockEC2) ModifySubnetAttribute(request *ec2.ModifySubnetAttributeInput) if request.EnableResourceNameDnsARecordOnLaunch != nil { subnet.main.PrivateDnsNameOptionsOnLaunch.EnableResourceNameDnsARecord = request.EnableResourceNameDnsARecordOnLaunch.Value } - if request.PrivateDnsHostnameTypeOnLaunch != nil { + if len(request.PrivateDnsHostnameTypeOnLaunch) > 0 { subnet.main.PrivateDnsNameOptionsOnLaunch.HostnameType = request.PrivateDnsHostnameTypeOnLaunch } return &ec2.ModifySubnetAttributeOutput{}, nil diff --git a/cloudmock/aws/mockec2/vpcs.go b/cloudmock/aws/mockec2/vpcs.go index d170bdbe75166..75d2b40b8d140 100644 --- a/cloudmock/aws/mockec2/vpcs.go +++ b/cloudmock/aws/mockec2/vpcs.go @@ -17,20 +17,21 @@ limitations under the License. package mockec2 import ( + "context" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) type vpcInfo struct { - main ec2.Vpc + main ec2types.Vpc attributes ec2.DescribeVpcAttributeOutput } -func (m *MockEC2) FindVpc(id string) *ec2.Vpc { +func (m *MockEC2) FindVpc(id string) *ec2types.Vpc { m.mutex.Lock() defer m.mutex.Unlock() @@ -40,35 +41,27 @@ func (m *MockEC2) FindVpc(id string) *ec2.Vpc { } copy := vpc.main - copy.Tags = m.getTags(ec2.ResourceTypeVpc, *vpc.main.VpcId) + copy.Tags = m.getTags(ec2types.ResourceTypeVpc, *vpc.main.VpcId) return © } -func (m *MockEC2) CreateVpcRequest(*ec2.CreateVpcInput) (*request.Request, *ec2.CreateVpcOutput) { - panic("Not implemented") -} - -func (m *MockEC2) CreateVpcWithContext(aws.Context, *ec2.CreateVpcInput, ...request.Option) (*ec2.CreateVpcOutput, error) { - panic("Not implemented") -} - func (m *MockEC2) CreateVpcWithId(request *ec2.CreateVpcInput, id string) (*ec2.CreateVpcOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() - tags := tagSpecificationsToTags(request.TagSpecifications, ec2.ResourceTypeVpc) + tags := tagSpecificationsToTags(request.TagSpecifications, ec2types.ResourceTypeVpc) vpc := &vpcInfo{ - main: ec2.Vpc{ + main: ec2types.Vpc{ VpcId: s(id), CidrBlock: request.CidrBlock, IsDefault: aws.Bool(false), Tags: tags, }, attributes: ec2.DescribeVpcAttributeOutput{ - EnableDnsHostnames: &ec2.AttributeBooleanValue{Value: aws.Bool(true)}, - EnableDnsSupport: &ec2.AttributeBooleanValue{Value: aws.Bool(true)}, + EnableDnsHostnames: &ec2types.AttributeBooleanValue{Value: aws.Bool(true)}, + EnableDnsSupport: &ec2types.AttributeBooleanValue{Value: aws.Bool(true)}, }, } @@ -85,7 +78,7 @@ func (m *MockEC2) CreateVpcWithId(request *ec2.CreateVpcInput, id string) (*ec2. return response, nil } -func (m *MockEC2) CreateVpc(request *ec2.CreateVpcInput) (*ec2.CreateVpcOutput, error) { +func (m *MockEC2) CreateVpc(ctx context.Context, request *ec2.CreateVpcInput, optFns ...func(*ec2.Options)) (*ec2.CreateVpcOutput, error) { klog.Infof("CreateVpc: %v", request) if request.DryRun != nil { @@ -97,25 +90,17 @@ func (m *MockEC2) CreateVpc(request *ec2.CreateVpcInput) (*ec2.CreateVpcOutput, return m.CreateVpcWithId(request, id) } -func (m *MockEC2) DescribeVpcsRequest(*ec2.DescribeVpcsInput) (*request.Request, *ec2.DescribeVpcsOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeVpcsWithContext(aws.Context, *ec2.DescribeVpcsInput, ...request.Option) (*ec2.DescribeVpcsOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeVpcs(request *ec2.DescribeVpcsInput) (*ec2.DescribeVpcsOutput, error) { +func (m *MockEC2) DescribeVpcs(ctx context.Context, request *ec2.DescribeVpcsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeVpcsOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DescribeVpcs: %v", request) if len(request.VpcIds) != 0 { - request.Filters = append(request.Filters, &ec2.Filter{Name: s("vpc-id"), Values: request.VpcIds}) + request.Filters = append(request.Filters, ec2types.Filter{Name: s("vpc-id"), Values: request.VpcIds}) } - var vpcs []*ec2.Vpc + var vpcs []ec2types.Vpc for k, vpc := range m.Vpcs { allFiltersMatch := true @@ -124,12 +109,12 @@ func (m *MockEC2) DescribeVpcs(request *ec2.DescribeVpcsInput) (*ec2.DescribeVpc switch *filter.Name { case "vpc-id": for _, v := range filter.Values { - if k == *v { + if k == v { match = true } } default: - match = m.hasTag(ec2.ResourceTypeVpc, *vpc.main.VpcId, filter) + match = m.hasTag(ec2types.ResourceTypeVpc, *vpc.main.VpcId, filter) } if !match { @@ -143,8 +128,8 @@ func (m *MockEC2) DescribeVpcs(request *ec2.DescribeVpcsInput) (*ec2.DescribeVpc } copy := vpc.main - copy.Tags = m.getTags(ec2.ResourceTypeVpc, *vpc.main.VpcId) - vpcs = append(vpcs, ©) + copy.Tags = m.getTags(ec2types.ResourceTypeVpc, *vpc.main.VpcId) + vpcs = append(vpcs, copy) } response := &ec2.DescribeVpcsOutput{ @@ -154,15 +139,7 @@ func (m *MockEC2) DescribeVpcs(request *ec2.DescribeVpcsInput) (*ec2.DescribeVpc return response, nil } -func (m *MockEC2) DescribeVpcAttributeRequest(*ec2.DescribeVpcAttributeInput) (*request.Request, *ec2.DescribeVpcAttributeOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeVpcAttributeWithContext(aws.Context, *ec2.DescribeVpcAttributeInput, ...request.Option) (*ec2.DescribeVpcAttributeOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeVpcAttribute(request *ec2.DescribeVpcAttributeInput) (*ec2.DescribeVpcAttributeOutput, error) { +func (m *MockEC2) DescribeVpcAttribute(ctx context.Context, request *ec2.DescribeVpcAttributeInput, optFns ...func(*ec2.Options)) (*ec2.DescribeVpcAttributeOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -183,7 +160,7 @@ func (m *MockEC2) DescribeVpcAttribute(request *ec2.DescribeVpcAttributeInput) ( return response, nil } -func (m *MockEC2) ModifyVpcAttribute(request *ec2.ModifyVpcAttributeInput) (*ec2.ModifyVpcAttributeOutput, error) { +func (m *MockEC2) ModifyVpcAttribute(ctx context.Context, request *ec2.ModifyVpcAttributeInput, optFns ...func(*ec2.Options)) (*ec2.ModifyVpcAttributeOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -207,21 +184,13 @@ func (m *MockEC2) ModifyVpcAttribute(request *ec2.ModifyVpcAttributeInput) (*ec2 return response, nil } -func (m *MockEC2) ModifyVpcAttributeWithContext(aws.Context, *ec2.ModifyVpcAttributeInput, ...request.Option) (*ec2.ModifyVpcAttributeOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) ModifyVpcAttributeRequest(*ec2.ModifyVpcAttributeInput) (*request.Request, *ec2.ModifyVpcAttributeOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteVpc(request *ec2.DeleteVpcInput) (*ec2.DeleteVpcOutput, error) { +func (m *MockEC2) DeleteVpc(ctx context.Context, request *ec2.DeleteVpcInput, optFns ...func(*ec2.Options)) (*ec2.DeleteVpcOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DeleteVpc: %v", request) - id := aws.StringValue(request.VpcId) + id := aws.ToString(request.VpcId) o := m.Vpcs[id] if o == nil { return nil, fmt.Errorf("VPC %q not found", id) @@ -231,46 +200,38 @@ func (m *MockEC2) DeleteVpc(request *ec2.DeleteVpcInput) (*ec2.DeleteVpcOutput, return &ec2.DeleteVpcOutput{}, nil } -func (m *MockEC2) DeleteVpcWithContext(aws.Context, *ec2.DeleteVpcInput, ...request.Option) (*ec2.DeleteVpcOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteVpcRequest(*ec2.DeleteVpcInput) (*request.Request, *ec2.DeleteVpcOutput) { - panic("Not implemented") -} - -func (m *MockEC2) AssociateVpcCidrBlock(request *ec2.AssociateVpcCidrBlockInput) (*ec2.AssociateVpcCidrBlockOutput, error) { +func (m *MockEC2) AssociateVpcCidrBlock(ctx context.Context, request *ec2.AssociateVpcCidrBlockInput, optFns ...func(*ec2.Options)) (*ec2.AssociateVpcCidrBlockOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("AssociateVpcCidrBlock: %v", request) - id := aws.StringValue(request.VpcId) + id := aws.ToString(request.VpcId) vpc, ok := m.Vpcs[id] if !ok { return nil, fmt.Errorf("VPC %q not found", id) } - var ipv4association *ec2.VpcCidrBlockAssociation - var ipv6association *ec2.VpcIpv6CidrBlockAssociation - if aws.BoolValue(request.AmazonProvidedIpv6CidrBlock) { - ipv6association = &ec2.VpcIpv6CidrBlockAssociation{ + var ipv4association *ec2types.VpcCidrBlockAssociation + var ipv6association *ec2types.VpcIpv6CidrBlockAssociation + if aws.ToBool(request.AmazonProvidedIpv6CidrBlock) { + ipv6association = &ec2types.VpcIpv6CidrBlockAssociation{ Ipv6Pool: aws.String("Amazon"), Ipv6CidrBlock: aws.String("2001:db8::/56"), AssociationId: aws.String(fmt.Sprintf("%v-%v", id, len(vpc.main.Ipv6CidrBlockAssociationSet))), - Ipv6CidrBlockState: &ec2.VpcCidrBlockState{ - State: aws.String(ec2.VpcCidrBlockStateCodeAssociated), + Ipv6CidrBlockState: &ec2types.VpcCidrBlockState{ + State: ec2types.VpcCidrBlockStateCodeAssociated, }, } - vpc.main.Ipv6CidrBlockAssociationSet = append(vpc.main.Ipv6CidrBlockAssociationSet, ipv6association) + vpc.main.Ipv6CidrBlockAssociationSet = append(vpc.main.Ipv6CidrBlockAssociationSet, *ipv6association) } else { - ipv4association = &ec2.VpcCidrBlockAssociation{ + ipv4association = &ec2types.VpcCidrBlockAssociation{ CidrBlock: request.CidrBlock, AssociationId: aws.String(fmt.Sprintf("%v-%v", id, len(vpc.main.CidrBlockAssociationSet))), - CidrBlockState: &ec2.VpcCidrBlockState{ - State: aws.String(ec2.VpcCidrBlockStateCodeAssociated), + CidrBlockState: &ec2types.VpcCidrBlockState{ + State: ec2types.VpcCidrBlockStateCodeAssociated, }, } - vpc.main.CidrBlockAssociationSet = append(vpc.main.CidrBlockAssociationSet, ipv4association) + vpc.main.CidrBlockAssociationSet = append(vpc.main.CidrBlockAssociationSet, *ipv4association) } return &ec2.AssociateVpcCidrBlockOutput{ @@ -280,20 +241,20 @@ func (m *MockEC2) AssociateVpcCidrBlock(request *ec2.AssociateVpcCidrBlockInput) }, nil } -func (m *MockEC2) DisassociateVpcCidrBlock(request *ec2.DisassociateVpcCidrBlockInput) (*ec2.DisassociateVpcCidrBlockOutput, error) { +func (m *MockEC2) DisassociateVpcCidrBlock(ctx context.Context, request *ec2.DisassociateVpcCidrBlockInput, optFns ...func(*ec2.Options)) (*ec2.DisassociateVpcCidrBlockOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DisassociateVpcCidrBlock: %v", request) - id := aws.StringValue(request.AssociationId) - var association *ec2.VpcCidrBlockAssociation + id := aws.ToString(request.AssociationId) + var association *ec2types.VpcCidrBlockAssociation var vpcID *string for _, vpc := range m.Vpcs { for _, a := range vpc.main.CidrBlockAssociationSet { - if aws.StringValue(a.AssociationId) == id { - a.CidrBlockState.State = aws.String(ec2.VpcCidrBlockStateCodeDisassociated) - association = a + if aws.ToString(a.AssociationId) == id { + a.CidrBlockState.State = ec2types.VpcCidrBlockStateCodeDisassociated + association = &a vpcID = vpc.main.VpcId break } diff --git a/cloudmock/aws/mockelbv2/loadbalancers.go b/cloudmock/aws/mockelbv2/loadbalancers.go index 93426e047c717..7cf5a4711c4cd 100644 --- a/cloudmock/aws/mockelbv2/loadbalancers.go +++ b/cloudmock/aws/mockelbv2/loadbalancers.go @@ -21,9 +21,9 @@ import ( "fmt" "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2" elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types" - "github.com/aws/aws-sdk-go/service/ec2" "k8s.io/klog/v2" ) @@ -85,8 +85,8 @@ func (m *MockELBV2) CreateLoadBalancer(ctx context.Context, request *elbv2.Creat zones = append(zones, elbv2types.AvailabilityZone{ SubnetId: aws.String(subnet), }) - subnetsOutput, err := m.EC2.DescribeSubnets(&ec2.DescribeSubnetsInput{ - SubnetIds: []*string{aws.String(subnet)}, + subnetsOutput, err := m.EC2.DescribeSubnets(ctx, &ec2.DescribeSubnetsInput{ + SubnetIds: []string{subnet}, }) if err == nil { vpc = *subnetsOutput.Subnets[0].VpcId @@ -104,8 +104,8 @@ func (m *MockELBV2) CreateLoadBalancer(ctx context.Context, request *elbv2.Creat SubnetId: subnetMapping.SubnetId, LoadBalancerAddresses: lbAddrs, }) - subnetsOutput, err := m.EC2.DescribeSubnets(&ec2.DescribeSubnetsInput{ - SubnetIds: []*string{subnetMapping.SubnetId}, + subnetsOutput, err := m.EC2.DescribeSubnets(ctx, &ec2.DescribeSubnetsInput{ + SubnetIds: []string{aws.ToString(subnetMapping.SubnetId)}, }) if err == nil { vpc = *subnetsOutput.Subnets[0].VpcId diff --git a/cmd/kops/create_cluster_integration_test.go b/cmd/kops/create_cluster_integration_test.go index c0ce061f0eea1..0dcdfba825dd5 100644 --- a/cmd/kops/create_cluster_integration_test.go +++ b/cmd/kops/create_cluster_integration_test.go @@ -26,7 +26,7 @@ import ( "time" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -220,13 +220,13 @@ func runCreateClusterIntegrationTest(t *testing.T, srcDir string, version string CidrBlock: aws.String("10.0.0.0/12"), }, "vpc-12345678") - awsCloud.EC2().CreateSubnet(&ec2.CreateSubnetInput{ + awsCloud.EC2().CreateSubnet(ctx, &ec2.CreateSubnetInput{ AvailabilityZone: aws.String("us-test-1a"), VpcId: aws.String("vpc-12345678"), CidrBlock: aws.String("10.10.0.0/24"), }) - awsCloud.EC2().CreateSubnet(&ec2.CreateSubnetInput{ + awsCloud.EC2().CreateSubnet(ctx, &ec2.CreateSubnetInput{ AvailabilityZone: aws.String("us-test-1a"), VpcId: aws.String("vpc-12345678"), CidrBlock: aws.String("10.11.0.0/24"), diff --git a/pkg/model/awsmodel/api_loadbalancer.go b/pkg/model/awsmodel/api_loadbalancer.go index 77461f74d2481..04ab4b94e9238 100644 --- a/pkg/model/awsmodel/api_loadbalancer.go +++ b/pkg/model/awsmodel/api_loadbalancer.go @@ -432,10 +432,10 @@ func (b *APILoadBalancerBuilder) Build(c *fi.CloudupModelBuilderContext) error { t := &awstasks.SecurityGroupRule{ Name: fi.PtrTo("https-api-elb-" + cidr), Lifecycle: b.SecurityLifecycle, - FromPort: fi.PtrTo(int64(443)), + FromPort: fi.PtrTo(int32(443)), Protocol: fi.PtrTo("tcp"), SecurityGroup: lbSG, - ToPort: fi.PtrTo(int64(443)), + ToPort: fi.PtrTo(int32(443)), } t.SetCidrOrPrefix(cidr) AddDirectionalGroupRule(c, t) @@ -446,8 +446,8 @@ func (b *APILoadBalancerBuilder) Build(c *fi.CloudupModelBuilderContext) error { t := &awstasks.SecurityGroupRule{ Name: fi.PtrTo("https-api-elb-8443-" + cidr), Lifecycle: b.SecurityLifecycle, - FromPort: fi.PtrTo(int64(8443)), - ToPort: fi.PtrTo(int64(8443)), + FromPort: fi.PtrTo(int32(8443)), + ToPort: fi.PtrTo(int32(8443)), Protocol: fi.PtrTo("tcp"), SecurityGroup: lbSG, } @@ -462,10 +462,10 @@ func (b *APILoadBalancerBuilder) Build(c *fi.CloudupModelBuilderContext) error { t := &awstasks.SecurityGroupRule{ Name: fi.PtrTo("icmpv6-pmtu-api-elb-" + cidr), Lifecycle: b.SecurityLifecycle, - FromPort: fi.PtrTo(int64(-1)), + FromPort: fi.PtrTo(int32(-1)), Protocol: fi.PtrTo("icmpv6"), SecurityGroup: lbSG, - ToPort: fi.PtrTo(int64(-1)), + ToPort: fi.PtrTo(int32(-1)), } t.SetCidrOrPrefix(cidr) if t.CIDR == nil { @@ -476,10 +476,10 @@ func (b *APILoadBalancerBuilder) Build(c *fi.CloudupModelBuilderContext) error { t := &awstasks.SecurityGroupRule{ Name: fi.PtrTo("icmp-pmtu-api-elb-" + cidr), Lifecycle: b.SecurityLifecycle, - FromPort: fi.PtrTo(int64(3)), + FromPort: fi.PtrTo(int32(3)), Protocol: fi.PtrTo("icmp"), SecurityGroup: lbSG, - ToPort: fi.PtrTo(int64(4)), + ToPort: fi.PtrTo(int32(4)), } t.SetCidrOrPrefix(cidr) if t.IPv6CIDR == nil { @@ -519,11 +519,11 @@ func (b *APILoadBalancerBuilder) Build(c *fi.CloudupModelBuilderContext) error { t := &awstasks.SecurityGroupRule{ Name: fi.PtrTo(fmt.Sprintf("tcp-api-cp%s", suffix)), Lifecycle: b.SecurityLifecycle, - FromPort: fi.PtrTo(int64(8443)), + FromPort: fi.PtrTo(int32(8443)), Protocol: fi.PtrTo("tcp"), SecurityGroup: masterGroup.Task, SourceGroup: lbSG, - ToPort: fi.PtrTo(int64(8443)), + ToPort: fi.PtrTo(int32(8443)), } c.AddTask(t) } @@ -551,29 +551,29 @@ func (b *APILoadBalancerBuilder) Build(c *fi.CloudupModelBuilderContext) error { c.AddTask(&awstasks.SecurityGroupRule{ Name: fi.PtrTo(fmt.Sprintf("https-elb-to-master%s", suffix)), Lifecycle: b.SecurityLifecycle, - FromPort: fi.PtrTo(int64(443)), + FromPort: fi.PtrTo(int32(443)), Protocol: fi.PtrTo("tcp"), SecurityGroup: masterGroup.Task, SourceGroup: lbSG, - ToPort: fi.PtrTo(int64(443)), + ToPort: fi.PtrTo(int32(443)), }) c.AddTask(&awstasks.SecurityGroupRule{ Name: fi.PtrTo(fmt.Sprintf("icmp-pmtu-elb-to-cp%s", suffix)), Lifecycle: b.SecurityLifecycle, - FromPort: fi.PtrTo(int64(3)), + FromPort: fi.PtrTo(int32(3)), Protocol: fi.PtrTo("icmp"), SecurityGroup: masterGroup.Task, SourceGroup: lbSG, - ToPort: fi.PtrTo(int64(4)), + ToPort: fi.PtrTo(int32(4)), }) c.AddTask(&awstasks.SecurityGroupRule{ Name: fi.PtrTo(fmt.Sprintf("icmp-pmtu-cp%s-to-elb", suffix)), Lifecycle: b.SecurityLifecycle, - FromPort: fi.PtrTo(int64(3)), + FromPort: fi.PtrTo(int32(3)), Protocol: fi.PtrTo("icmp"), SecurityGroup: lbSG, SourceGroup: masterGroup.Task, - ToPort: fi.PtrTo(int64(4)), + ToPort: fi.PtrTo(int32(4)), }) if b.Cluster.UsesNoneDNS() { nlb.WellKnownServices = append(nlb.WellKnownServices, wellknownservices.KopsController) @@ -582,10 +582,10 @@ func (b *APILoadBalancerBuilder) Build(c *fi.CloudupModelBuilderContext) error { c.AddTask(&awstasks.SecurityGroupRule{ Name: fi.PtrTo(fmt.Sprintf("kops-controller-elb-to-cp%s", suffix)), Lifecycle: b.SecurityLifecycle, - FromPort: fi.PtrTo(int64(wellknownports.KopsControllerPort)), + FromPort: fi.PtrTo(int32(wellknownports.KopsControllerPort)), Protocol: fi.PtrTo("tcp"), SecurityGroup: masterGroup.Task, - ToPort: fi.PtrTo(int64(wellknownports.KopsControllerPort)), + ToPort: fi.PtrTo(int32(wellknownports.KopsControllerPort)), SourceGroup: lbSG, }) } diff --git a/pkg/model/awsmodel/bastion.go b/pkg/model/awsmodel/bastion.go index aed6f454881d0..55fa51910823e 100644 --- a/pkg/model/awsmodel/bastion.go +++ b/pkg/model/awsmodel/bastion.go @@ -131,8 +131,8 @@ func (b *BastionModelBuilder) Build(c *fi.CloudupModelBuilderContext) error { SecurityGroup: dest.Task, SourceGroup: src.Task, Protocol: fi.PtrTo("tcp"), - FromPort: fi.PtrTo(int64(22)), - ToPort: fi.PtrTo(int64(22)), + FromPort: fi.PtrTo(int32(22)), + ToPort: fi.PtrTo(int32(22)), } AddDirectionalGroupRule(c, t) } @@ -147,8 +147,8 @@ func (b *BastionModelBuilder) Build(c *fi.CloudupModelBuilderContext) error { SecurityGroup: dest.Task, SourceGroup: src.Task, Protocol: fi.PtrTo("tcp"), - FromPort: fi.PtrTo(int64(22)), - ToPort: fi.PtrTo(int64(22)), + FromPort: fi.PtrTo(int32(22)), + ToPort: fi.PtrTo(int32(22)), } AddDirectionalGroupRule(c, t) } @@ -237,8 +237,8 @@ func (b *BastionModelBuilder) Build(c *fi.CloudupModelBuilderContext) error { Lifecycle: b.SecurityLifecycle, SecurityGroup: lbSG, Protocol: fi.PtrTo("tcp"), - FromPort: fi.PtrTo(int64(22)), - ToPort: fi.PtrTo(int64(22)), + FromPort: fi.PtrTo(int32(22)), + ToPort: fi.PtrTo(int32(22)), } t.SetCidrOrPrefix(cidr) AddDirectionalGroupRule(c, t) @@ -249,10 +249,10 @@ func (b *BastionModelBuilder) Build(c *fi.CloudupModelBuilderContext) error { t := &awstasks.SecurityGroupRule{ Name: fi.PtrTo("icmpv6-pmtu-ssh-nlb-" + cidr), Lifecycle: b.SecurityLifecycle, - FromPort: fi.PtrTo(int64(-1)), + FromPort: fi.PtrTo(int32(-1)), Protocol: fi.PtrTo("icmpv6"), SecurityGroup: lbSG, - ToPort: fi.PtrTo(int64(-1)), + ToPort: fi.PtrTo(int32(-1)), } t.SetCidrOrPrefix(cidr) if t.CIDR == nil { @@ -263,10 +263,10 @@ func (b *BastionModelBuilder) Build(c *fi.CloudupModelBuilderContext) error { t := &awstasks.SecurityGroupRule{ Name: fi.PtrTo("icmp-pmtu-ssh-nlb-" + cidr), Lifecycle: b.SecurityLifecycle, - FromPort: fi.PtrTo(int64(3)), + FromPort: fi.PtrTo(int32(3)), Protocol: fi.PtrTo("icmp"), SecurityGroup: lbSG, - ToPort: fi.PtrTo(int64(4)), + ToPort: fi.PtrTo(int32(4)), } t.SetCidrOrPrefix(cidr) if t.IPv6CIDR == nil { @@ -285,8 +285,8 @@ func (b *BastionModelBuilder) Build(c *fi.CloudupModelBuilderContext) error { SecurityGroup: bastionGroup.Task, SourceGroup: lbSG, Protocol: fi.PtrTo("tcp"), - FromPort: fi.PtrTo(int64(22)), - ToPort: fi.PtrTo(int64(22)), + FromPort: fi.PtrTo(int32(22)), + ToPort: fi.PtrTo(int32(22)), } AddDirectionalGroupRule(c, t) } @@ -298,8 +298,8 @@ func (b *BastionModelBuilder) Build(c *fi.CloudupModelBuilderContext) error { SecurityGroup: bastionGroup.Task, SourceGroup: lbSG, Protocol: fi.PtrTo("icmp"), - FromPort: fi.PtrTo(int64(3)), - ToPort: fi.PtrTo(int64(4)), + FromPort: fi.PtrTo(int32(3)), + ToPort: fi.PtrTo(int32(4)), } AddDirectionalGroupRule(c, t) } @@ -311,8 +311,8 @@ func (b *BastionModelBuilder) Build(c *fi.CloudupModelBuilderContext) error { SecurityGroup: lbSG, SourceGroup: bastionGroup.Task, Protocol: fi.PtrTo("icmp"), - FromPort: fi.PtrTo(int64(3)), - ToPort: fi.PtrTo(int64(4)), + FromPort: fi.PtrTo(int32(3)), + ToPort: fi.PtrTo(int32(4)), } AddDirectionalGroupRule(c, t) } diff --git a/pkg/model/awsmodel/external_access.go b/pkg/model/awsmodel/external_access.go index a840b2b5e6f63..a62bdc9caa55a 100644 --- a/pkg/model/awsmodel/external_access.go +++ b/pkg/model/awsmodel/external_access.go @@ -67,8 +67,8 @@ func (b *ExternalAccessModelBuilder) Build(c *fi.CloudupModelBuilderContext) err Lifecycle: b.Lifecycle, SecurityGroup: masterGroup.Task, Protocol: fi.PtrTo("tcp"), - FromPort: fi.PtrTo(int64(22)), - ToPort: fi.PtrTo(int64(22)), + FromPort: fi.PtrTo(int32(22)), + ToPort: fi.PtrTo(int32(22)), } t.SetCidrOrPrefix(sshAccess) AddDirectionalGroupRule(c, t) @@ -81,8 +81,8 @@ func (b *ExternalAccessModelBuilder) Build(c *fi.CloudupModelBuilderContext) err Lifecycle: b.Lifecycle, SecurityGroup: nodeGroup.Task, Protocol: fi.PtrTo("tcp"), - FromPort: fi.PtrTo(int64(22)), - ToPort: fi.PtrTo(int64(22)), + FromPort: fi.PtrTo(int32(22)), + ToPort: fi.PtrTo(int32(22)), } t.SetCidrOrPrefix(sshAccess) AddDirectionalGroupRule(c, t) @@ -104,8 +104,8 @@ func (b *ExternalAccessModelBuilder) Build(c *fi.CloudupModelBuilderContext) err Lifecycle: b.Lifecycle, SecurityGroup: nodeGroup.Task, Protocol: fi.PtrTo("tcp"), - FromPort: fi.PtrTo(int64(nodePortRange.Base)), - ToPort: fi.PtrTo(int64(nodePortRange.Base + nodePortRange.Size - 1)), + FromPort: fi.PtrTo(int32(nodePortRange.Base)), + ToPort: fi.PtrTo(int32(nodePortRange.Base + nodePortRange.Size - 1)), } t.SetCidrOrPrefix(nodePortAccess) c.AddTask(t) @@ -116,8 +116,8 @@ func (b *ExternalAccessModelBuilder) Build(c *fi.CloudupModelBuilderContext) err Lifecycle: b.Lifecycle, SecurityGroup: nodeGroup.Task, Protocol: fi.PtrTo("udp"), - FromPort: fi.PtrTo(int64(nodePortRange.Base)), - ToPort: fi.PtrTo(int64(nodePortRange.Base + nodePortRange.Size - 1)), + FromPort: fi.PtrTo(int32(nodePortRange.Base)), + ToPort: fi.PtrTo(int32(nodePortRange.Base + nodePortRange.Size - 1)), } t.SetCidrOrPrefix(nodePortAccess) c.AddTask(t) @@ -139,8 +139,8 @@ func (b *ExternalAccessModelBuilder) Build(c *fi.CloudupModelBuilderContext) err Lifecycle: b.Lifecycle, SecurityGroup: masterGroup.Task, Protocol: fi.PtrTo("tcp"), - FromPort: fi.PtrTo(int64(443)), - ToPort: fi.PtrTo(int64(443)), + FromPort: fi.PtrTo(int32(443)), + ToPort: fi.PtrTo(int32(443)), } t.SetCidrOrPrefix(apiAccess) AddDirectionalGroupRule(c, t) diff --git a/pkg/model/awsmodel/firewall.go b/pkg/model/awsmodel/firewall.go index 8b5476439cad3..e0c3329655ba6 100644 --- a/pkg/model/awsmodel/firewall.go +++ b/pkg/model/awsmodel/firewall.go @@ -172,8 +172,8 @@ func (b *FirewallModelBuilder) applyNodeToMasterBlockSpecificPorts(c *fi.Cloudup Lifecycle: b.Lifecycle, SecurityGroup: masterGroup.Task, SourceGroup: nodeGroup.Task, - FromPort: fi.PtrTo(int64(r.From)), - ToPort: fi.PtrTo(int64(r.To)), + FromPort: fi.PtrTo(int32(r.From)), + ToPort: fi.PtrTo(int32(r.To)), Protocol: fi.PtrTo("udp"), } AddDirectionalGroupRule(c, t) @@ -184,8 +184,8 @@ func (b *FirewallModelBuilder) applyNodeToMasterBlockSpecificPorts(c *fi.Cloudup Lifecycle: b.Lifecycle, SecurityGroup: masterGroup.Task, SourceGroup: nodeGroup.Task, - FromPort: fi.PtrTo(int64(r.From)), - ToPort: fi.PtrTo(int64(r.To)), + FromPort: fi.PtrTo(int32(r.From)), + ToPort: fi.PtrTo(int32(r.To)), Protocol: fi.PtrTo("tcp"), } AddDirectionalGroupRule(c, t) diff --git a/pkg/resources/aws/aws.go b/pkg/resources/aws/aws.go index e628a49c55e79..9371ee716d5a3 100644 --- a/pkg/resources/aws/aws.go +++ b/pkg/resources/aws/aws.go @@ -26,6 +26,8 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/autoscaling" autoscalingtypes "github.com/aws/aws-sdk-go-v2/service/autoscaling/types" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" elb "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing" elbtypes "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2" @@ -34,7 +36,6 @@ import ( iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" "github.com/aws/aws-sdk-go-v2/service/route53" route53types "github.com/aws/aws-sdk-go-v2/service/route53/types" - "github.com/aws/aws-sdk-go/service/ec2" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" "k8s.io/kops/pkg/dns" @@ -185,7 +186,7 @@ func ListResourcesAWS(cloud awsup.AWSCloud, clusterInfo resources.ClusterInfo) ( // We delete a NAT gateway if it is linked to our route table routeTableIds := make(map[string]*resources.Resource) for _, resource := range resourceTrackers { - if resource.Type != ec2.ResourceTypeRouteTable { + if resource.Type != string(ec2types.ResourceTypeRouteTable) { continue } id := resource.ID @@ -710,6 +711,7 @@ func ListKeypairs(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resou } func DeleteSubnet(cloud fi.Cloud, tracker *resources.Resource) error { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) id := tracker.ID @@ -718,7 +720,7 @@ func DeleteSubnet(cloud fi.Cloud, tracker *resources.Resource) error { request := &ec2.DeleteSubnetInput{ SubnetId: &id, } - _, err := c.EC2().DeleteSubnet(request) + _, err := c.EC2().DeleteSubnet(ctx, request) if err != nil { if awsup.AWSErrorCode(err) == "InvalidSubnetID.NotFound" { klog.V(2).Infof("Got InvalidSubnetID.NotFound error deleting subnet %q; will treat as already-deleted", id) @@ -732,6 +734,7 @@ func DeleteSubnet(cloud fi.Cloud, tracker *resources.Resource) error { } func ListSubnets(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resource, error) { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) subnets, err := DescribeSubnets(cloud) if err != nil { @@ -750,7 +753,7 @@ func ListSubnets(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resour resourceTracker := &resources.Resource{ Name: FindName(subnet.Tags), ID: subnetID, - Type: ec2.ResourceTypeSubnet, + Type: string(ec2types.ResourceTypeSubnet), Deleter: DeleteSubnet, Dumper: DumpSubnet, Shared: shared, @@ -789,7 +792,7 @@ func ListSubnets(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resour if elasticIPs.Len() != 0 { klog.V(2).Infof("Querying EC2 Elastic IPs") request := &ec2.DescribeAddressesInput{} - response, err := c.EC2().DescribeAddresses(request) + response, err := c.EC2().DescribeAddresses(ctx, request) if err != nil { return nil, fmt.Errorf("error describing addresses: %v", err) } @@ -809,7 +812,7 @@ func ListSubnets(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resour if natGatewayIds.Len() != 0 { rtRequest := &ec2.DescribeRouteTablesInput{} - rtResponse, err := c.EC2().DescribeRouteTables(rtRequest) + rtResponse, err := c.EC2().DescribeRouteTables(ctx, rtRequest) if err != nil && awsup.AWSErrorCode(err) != "InvalidRouteTableID.NotFound" { return nil, fmt.Errorf("error describing RouteTables: %v", err) } @@ -830,7 +833,7 @@ func ListSubnets(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resour klog.V(2).Infof("Querying Nat Gateways") request := &ec2.DescribeNatGatewaysInput{} - response, err := c.EC2().DescribeNatGateways(request) + response, err := c.EC2().DescribeNatGateways(ctx, request) if err != nil { return nil, fmt.Errorf("error describing NatGateways: %v", err) } @@ -850,14 +853,15 @@ func ListSubnets(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resour return resourceTrackers, nil } -func DescribeSubnets(cloud fi.Cloud) ([]*ec2.Subnet, error) { +func DescribeSubnets(cloud fi.Cloud) ([]ec2types.Subnet, error) { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) klog.V(2).Infof("Listing EC2 subnets") request := &ec2.DescribeSubnetsInput{ Filters: BuildEC2Filters(cloud), } - response, err := c.EC2().DescribeSubnets(request) + response, err := c.EC2().DescribeSubnets(ctx, request) if err != nil { return nil, fmt.Errorf("error listing subnets: %v", err) } @@ -866,6 +870,7 @@ func DescribeSubnets(cloud fi.Cloud) ([]*ec2.Subnet, error) { } func DeleteRouteTable(cloud fi.Cloud, r *resources.Resource) error { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) id := r.ID @@ -874,7 +879,7 @@ func DeleteRouteTable(cloud fi.Cloud, r *resources.Resource) error { request := &ec2.DeleteRouteTableInput{ RouteTableId: &id, } - _, err := c.EC2().DeleteRouteTable(request) + _, err := c.EC2().DeleteRouteTable(ctx, request) if err != nil { if awsup.AWSErrorCode(err) == "InvalidRouteTableID.NotFound" { klog.V(2).Infof("Got InvalidRouteTableID.NotFound error describing RouteTable %q; will treat as already-deleted", id) @@ -890,12 +895,13 @@ func DeleteRouteTable(cloud fi.Cloud, r *resources.Resource) error { } // DescribeRouteTablesIgnoreTags returns all ec2.RouteTable, ignoring tags -func DescribeRouteTablesIgnoreTags(cloud fi.Cloud) ([]*ec2.RouteTable, error) { +func DescribeRouteTablesIgnoreTags(cloud fi.Cloud) ([]ec2types.RouteTable, error) { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) klog.V(2).Infof("Listing all RouteTables") request := &ec2.DescribeRouteTablesInput{} - response, err := c.EC2().DescribeRouteTables(request) + response, err := c.EC2().DescribeRouteTables(ctx, request) if err != nil { return nil, fmt.Errorf("error listing RouteTables: %v", err) } @@ -904,6 +910,7 @@ func DescribeRouteTablesIgnoreTags(cloud fi.Cloud) ([]*ec2.RouteTable, error) { } func DeleteDhcpOptions(cloud fi.Cloud, r *resources.Resource) error { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) id := r.ID @@ -912,7 +919,7 @@ func DeleteDhcpOptions(cloud fi.Cloud, r *resources.Resource) error { request := &ec2.DeleteDhcpOptionsInput{ DhcpOptionsId: &id, } - _, err := c.EC2().DeleteDhcpOptions(request) + _, err := c.EC2().DeleteDhcpOptions(ctx, request) if err != nil { if awsup.AWSErrorCode(err) == "InvalidDhcpOptionsID.NotFound" { klog.V(2).Infof("Got InvalidDhcpOptionsID.NotFound error deleting DhcpOptions %q; will treat as already-deleted", id) @@ -939,7 +946,7 @@ func ListDhcpOptions(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Re ID: aws.ToString(o.DhcpOptionsId), Type: "dhcp-options", Deleter: DeleteDhcpOptions, - Shared: HasSharedTag(ec2.ResourceTypeDhcpOptions+":"+aws.ToString(o.DhcpOptionsId), o.Tags, clusterName), + Shared: HasSharedTag(string(ec2types.ResourceTypeDhcpOptions)+":"+aws.ToString(o.DhcpOptionsId), o.Tags, clusterName), } var blocks []string @@ -952,14 +959,15 @@ func ListDhcpOptions(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Re return resourceTrackers, nil } -func DescribeDhcpOptions(cloud fi.Cloud) ([]*ec2.DhcpOptions, error) { +func DescribeDhcpOptions(cloud fi.Cloud) ([]ec2types.DhcpOptions, error) { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) klog.V(2).Infof("Listing EC2 DhcpOptions") request := &ec2.DescribeDhcpOptionsInput{ Filters: BuildEC2Filters(cloud), } - response, err := c.EC2().DescribeDhcpOptions(request) + response, err := c.EC2().DescribeDhcpOptions(ctx, request) if err != nil { return nil, fmt.Errorf("error listing DhcpOptions: %v", err) } @@ -968,16 +976,17 @@ func DescribeDhcpOptions(cloud fi.Cloud) ([]*ec2.DhcpOptions, error) { } func DeleteInternetGateway(cloud fi.Cloud, r *resources.Resource) error { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) id := r.ID - var igw *ec2.InternetGateway + var igw *ec2types.InternetGateway { request := &ec2.DescribeInternetGatewaysInput{ - InternetGatewayIds: []*string{&id}, + InternetGatewayIds: []string{id}, } - response, err := c.EC2().DescribeInternetGateways(request) + response, err := c.EC2().DescribeInternetGateways(ctx, request) if err != nil { if awsup.AWSErrorCode(err) == "InvalidInternetGatewayID.NotFound" { klog.Infof("Internet gateway %q not found; assuming already deleted", id) @@ -992,7 +1001,7 @@ func DeleteInternetGateway(cloud fi.Cloud, r *resources.Resource) error { if len(response.InternetGateways) != 1 { return fmt.Errorf("found multiple InternetGateways with id %q", id) } - igw = response.InternetGateways[0] + igw = &response.InternetGateways[0] } for _, a := range igw.Attachments { @@ -1001,7 +1010,7 @@ func DeleteInternetGateway(cloud fi.Cloud, r *resources.Resource) error { InternetGatewayId: &id, VpcId: a.VpcId, } - _, err := c.EC2().DetachInternetGateway(request) + _, err := c.EC2().DetachInternetGateway(ctx, request) if err != nil { if IsDependencyViolation(err) { return err @@ -1015,7 +1024,7 @@ func DeleteInternetGateway(cloud fi.Cloud, r *resources.Resource) error { request := &ec2.DeleteInternetGatewayInput{ InternetGatewayId: &id, } - _, err := c.EC2().DeleteInternetGateway(request) + _, err := c.EC2().DeleteInternetGateway(ctx, request) if err != nil { if IsDependencyViolation(err) { return err @@ -1054,7 +1063,7 @@ func ListInternetGateways(cloud fi.Cloud, vpcID, clusterName string) ([]*resourc ID: aws.ToString(o.InternetGatewayId), Type: "internet-gateway", Deleter: DeleteInternetGateway, - Shared: HasSharedTag(ec2.ResourceTypeInternetGateway+":"+aws.ToString(o.InternetGatewayId), o.Tags, clusterName), + Shared: HasSharedTag(string(ec2types.ResourceTypeInternetGateway)+":"+aws.ToString(o.InternetGatewayId), o.Tags, clusterName), } var blocks []string @@ -1071,19 +1080,20 @@ func ListInternetGateways(cloud fi.Cloud, vpcID, clusterName string) ([]*resourc return resourceTrackers, nil } -func DescribeInternetGateways(cloud fi.Cloud) ([]*ec2.InternetGateway, error) { +func DescribeInternetGateways(cloud fi.Cloud) ([]ec2types.InternetGateway, error) { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) klog.V(2).Infof("Listing EC2 InternetGateways") request := &ec2.DescribeInternetGatewaysInput{ Filters: BuildEC2Filters(cloud), } - response, err := c.EC2().DescribeInternetGateways(request) + response, err := c.EC2().DescribeInternetGateways(ctx, request) if err != nil { return nil, fmt.Errorf("error listing InternetGateway: %v", err) } - var gateways []*ec2.InternetGateway + var gateways []ec2types.InternetGateway gateways = append(gateways, response.InternetGateways...) return gateways, nil @@ -1091,18 +1101,19 @@ func DescribeInternetGateways(cloud fi.Cloud) ([]*ec2.InternetGateway, error) { // DescribeInternetGatewaysIgnoreTags returns all ec2.InternetGateways, ignoring tags // (gateways were not always tagged in kube-up) -func DescribeInternetGatewaysIgnoreTags(cloud fi.Cloud) ([]*ec2.InternetGateway, error) { +func DescribeInternetGatewaysIgnoreTags(cloud fi.Cloud) ([]ec2types.InternetGateway, error) { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) klog.V(2).Infof("Listing all Internet Gateways") request := &ec2.DescribeInternetGatewaysInput{} - response, err := c.EC2().DescribeInternetGateways(request) + response, err := c.EC2().DescribeInternetGateways(ctx, request) if err != nil { return nil, fmt.Errorf("error listing (all) InternetGateways: %v", err) } - var gateways []*ec2.InternetGateway + var gateways []ec2types.InternetGateway gateways = append(gateways, response.InternetGateways...) @@ -1119,6 +1130,7 @@ func DumpEgressOnlyInternetGateway(op *resources.DumpOperation, r *resources.Res } func DeleteEgressOnlyInternetGateway(cloud fi.Cloud, r *resources.Resource) error { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) id := r.ID @@ -1128,7 +1140,7 @@ func DeleteEgressOnlyInternetGateway(cloud fi.Cloud, r *resources.Resource) erro request := &ec2.DeleteEgressOnlyInternetGatewayInput{ EgressOnlyInternetGatewayId: &id, } - _, err := c.EC2().DeleteEgressOnlyInternetGateway(request) + _, err := c.EC2().DeleteEgressOnlyInternetGateway(ctx, request) if err != nil { if IsDependencyViolation(err) { return err @@ -1160,7 +1172,7 @@ func ListEgressOnlyInternetGateways(cloud fi.Cloud, vpcID, clusterName string) ( Obj: o, Dumper: DumpEgressOnlyInternetGateway, Deleter: DeleteEgressOnlyInternetGateway, - Shared: HasSharedTag(ec2.ResourceTypeEgressOnlyInternetGateway+":"+aws.ToString(o.EgressOnlyInternetGatewayId), o.Tags, clusterName), + Shared: HasSharedTag(string(ec2types.ResourceTypeEgressOnlyInternetGateway)+":"+aws.ToString(o.EgressOnlyInternetGatewayId), o.Tags, clusterName), } var blocks []string @@ -1177,19 +1189,20 @@ func ListEgressOnlyInternetGateways(cloud fi.Cloud, vpcID, clusterName string) ( return resourceTrackers, nil } -func DescribeEgressOnlyInternetGateways(cloud fi.Cloud) ([]*ec2.EgressOnlyInternetGateway, error) { +func DescribeEgressOnlyInternetGateways(cloud fi.Cloud) ([]ec2types.EgressOnlyInternetGateway, error) { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) klog.V(2).Infof("Listing EC2 EgressOnlyInternetGateways") request := &ec2.DescribeEgressOnlyInternetGatewaysInput{ Filters: BuildEC2Filters(cloud), } - response, err := c.EC2().DescribeEgressOnlyInternetGateways(request) + response, err := c.EC2().DescribeEgressOnlyInternetGateways(ctx, request) if err != nil { return nil, fmt.Errorf("error listing EgressOnlyInternetGateway: %v", err) } - var gateways []*ec2.EgressOnlyInternetGateway + var gateways []ec2types.EgressOnlyInternetGateway gateways = append(gateways, response.EgressOnlyInternetGateways...) return gateways, nil @@ -1299,6 +1312,7 @@ func FindNatGateways(cloud fi.Cloud, routeTables map[string]*resources.Resource, return nil, nil } + ctx := context.TODO() c := cloud.(awsup.AWSCloud) natGatewayIds := sets.NewString() @@ -1306,9 +1320,9 @@ func FindNatGateways(cloud fi.Cloud, routeTables map[string]*resources.Resource, { request := &ec2.DescribeRouteTablesInput{} for _, routeTable := range routeTables { - request.RouteTableIds = append(request.RouteTableIds, aws.String(routeTable.ID)) + request.RouteTableIds = append(request.RouteTableIds, routeTable.ID) } - response, err := c.EC2().DescribeRouteTables(request) + response, err := c.EC2().DescribeRouteTables(ctx, request) if err != nil && awsup.AWSErrorCode(err) != "InvalidRouteTableID.NotFound" { return nil, fmt.Errorf("error from DescribeRouteTables: %v", err) } @@ -1346,9 +1360,9 @@ func FindNatGateways(cloud fi.Cloud, routeTables map[string]*resources.Resource, var resourceTrackers []*resources.Resource for natGatewayId := range natGatewayIds { request := &ec2.DescribeNatGatewaysInput{ - NatGatewayIds: []*string{aws.String(natGatewayId)}, + NatGatewayIds: []string{natGatewayId}, } - response, err := c.EC2().DescribeNatGateways(request) + response, err := c.EC2().DescribeNatGateways(ctx, request) if err != nil { if awsup.AWSErrorCode(err) == "NatGatewayNotFound" { klog.V(2).Infof("Got NatGatewayNotFound describing NatGateway %s; will treat as already-deleted", natGatewayId) @@ -1377,8 +1391,8 @@ func FindNatGateways(cloud fi.Cloud, routeTables map[string]*resources.Resource, for _, address := range ngw.NatGatewayAddresses { if address.AllocationId != nil { request := &ec2.DescribeAddressesInput{} - request.AllocationIds = []*string{address.AllocationId} - response, err := c.EC2().DescribeAddresses(request) + request.AllocationIds = []string{aws.ToString(address.AllocationId)} + response, err := c.EC2().DescribeAddresses(ctx, request) if err != nil { return nil, fmt.Errorf("error from DescribeAddresses: %v", err) } @@ -1662,6 +1676,7 @@ func listMatchingTargetGroups(cloud fi.Cloud) ([]*awsup.TargetGroupInfo, error) } func DeleteElasticIP(cloud fi.Cloud, t *resources.Resource) error { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) id := t.ID @@ -1670,7 +1685,7 @@ func DeleteElasticIP(cloud fi.Cloud, t *resources.Resource) error { request := &ec2.ReleaseAddressInput{ AllocationId: &id, } - _, err := c.EC2().ReleaseAddress(request) + _, err := c.EC2().ReleaseAddress(ctx, request) if err != nil { if awsup.AWSErrorCode(err) == "InvalidAllocationID.NotFound" { klog.V(2).Infof("Got InvalidAllocationID.NotFound error deleting ElasticIP %q; will treat as already-deleted", id) @@ -1686,6 +1701,7 @@ func DeleteElasticIP(cloud fi.Cloud, t *resources.Resource) error { } func DeleteNatGateway(cloud fi.Cloud, t *resources.Resource) error { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) id := t.ID @@ -1694,7 +1710,7 @@ func DeleteNatGateway(cloud fi.Cloud, t *resources.Resource) error { request := &ec2.DeleteNatGatewayInput{ NatGatewayId: &id, } - _, err := c.EC2().DeleteNatGateway(request) + _, err := c.EC2().DeleteNatGateway(ctx, request) if err != nil { if IsDependencyViolation(err) { return err diff --git a/pkg/resources/aws/elasticip.go b/pkg/resources/aws/elasticip.go index b7e44dfa289a3..0f0f47099e058 100644 --- a/pkg/resources/aws/elasticip.go +++ b/pkg/resources/aws/elasticip.go @@ -18,12 +18,12 @@ package aws import ( "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/pkg/resources" ) -func buildElasticIPResource(address *ec2.Address, forceShared bool, clusterName string) *resources.Resource { +func buildElasticIPResource(address ec2types.Address, forceShared bool, clusterName string) *resources.Resource { name := aws.ToString(address.PublicIp) if name == "" { name = aws.ToString(address.PrivateIpAddress) diff --git a/pkg/resources/aws/eni.go b/pkg/resources/aws/eni.go index f31439fe94230..bb3cbddea75dc 100644 --- a/pkg/resources/aws/eni.go +++ b/pkg/resources/aws/eni.go @@ -17,10 +17,12 @@ limitations under the License. package aws import ( + "context" "fmt" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/pkg/resources" @@ -29,6 +31,7 @@ import ( ) func DeleteENI(cloud fi.Cloud, r *resources.Resource) error { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) id := r.ID @@ -37,7 +40,7 @@ func DeleteENI(cloud fi.Cloud, r *resources.Resource) error { request := &ec2.DeleteNetworkInterfaceInput{ NetworkInterfaceId: &id, } - _, err := c.EC2().DeleteNetworkInterface(request) + _, err := c.EC2().DeleteNetworkInterface(ctx, request) if err != nil { if awsup.AWSErrorCode(err) == "InvalidNetworkInterfaceID.NotFound" { // Concurrently deleted @@ -55,7 +58,7 @@ func DeleteENI(cloud fi.Cloud, r *resources.Resource) error { func DumpENI(op *resources.DumpOperation, r *resources.Resource) error { data := make(map[string]interface{}) data["id"] = r.ID - data["type"] = ec2.ResourceTypeNetworkInterface + data["type"] = ec2types.ResourceTypeNetworkInterface data["raw"] = r.Obj op.Dump.Resources = append(op.Dump.Resources, data) @@ -63,29 +66,31 @@ func DumpENI(op *resources.DumpOperation, r *resources.Resource) error { return nil } -func DescribeENIs(cloud fi.Cloud, vpcID, clusterName string) (map[string]*ec2.NetworkInterface, error) { +func DescribeENIs(cloud fi.Cloud, vpcID, clusterName string) (map[string]ec2types.NetworkInterface, error) { if vpcID == "" { return nil, nil } + ctx := context.TODO() c := cloud.(awsup.AWSCloud) vpcFilter := awsup.NewEC2Filter("vpc-id", vpcID) - statusFilter := awsup.NewEC2Filter("status", ec2.NetworkInterfaceStatusAvailable) - enis := make(map[string]*ec2.NetworkInterface) + statusFilter := awsup.NewEC2Filter("status", string(ec2types.NetworkInterfaceStatusAvailable)) + enis := make(map[string]ec2types.NetworkInterface) klog.V(2).Info("Listing ENIs") for _, filters := range buildEC2FiltersForCluster(clusterName) { request := &ec2.DescribeNetworkInterfacesInput{ Filters: append(filters, vpcFilter, statusFilter), } - err := c.EC2().DescribeNetworkInterfacesPages(request, func(dnio *ec2.DescribeNetworkInterfacesOutput, b bool) bool { + paginator := ec2.NewDescribeNetworkInterfacesPaginator(c.EC2(), request) + for paginator.HasMorePages() { + dnio, err := paginator.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("error listing ENIs: %v", err) + } for _, eni := range dnio.NetworkInterfaces { enis[aws.ToString(eni.NetworkInterfaceId)] = eni } - return true - }) - if err != nil { - return nil, fmt.Errorf("error listing ENIs: %v", err) } } @@ -104,15 +109,15 @@ func ListENIs(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resource, resourceTracker := &resources.Resource{ ID: eniID, - Type: ec2.ResourceTypeNetworkInterface, + Type: string(ec2types.ResourceTypeNetworkInterface), Deleter: DeleteENI, Dumper: DumpENI, Obj: v, - Shared: !HasOwnedTag(ec2.ResourceTypeNetworkInterface+":"+eniID, v.TagSet, clusterName), + Shared: !HasOwnedTag(string(ec2types.ResourceTypeNetworkInterface)+":"+eniID, v.TagSet, clusterName), } var blocks []string - blocks = append(blocks, ec2.ResourceTypeVpc+":"+aws.ToString(v.VpcId)) + blocks = append(blocks, string(ec2types.ResourceTypeVpc)+":"+aws.ToString(v.VpcId)) resourceTracker.Blocks = blocks diff --git a/pkg/resources/aws/natgateway.go b/pkg/resources/aws/natgateway.go index e878532783ea8..2c362495d27b4 100644 --- a/pkg/resources/aws/natgateway.go +++ b/pkg/resources/aws/natgateway.go @@ -18,7 +18,7 @@ package aws import ( "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/pkg/resources" ) @@ -32,7 +32,7 @@ func DumpNatGateway(op *resources.DumpOperation, r *resources.Resource) error { return nil } -func buildNatGatewayResource(ngw *ec2.NatGateway, forceShared bool, clusterName string) *resources.Resource { +func buildNatGatewayResource(ngw ec2types.NatGateway, forceShared bool, clusterName string) *resources.Resource { id := aws.ToString(ngw.NatGatewayId) r := &resources.Resource{ diff --git a/pkg/resources/aws/routetable.go b/pkg/resources/aws/routetable.go index 2b7a2ba1f3609..e55daffd5f0ef 100644 --- a/pkg/resources/aws/routetable.go +++ b/pkg/resources/aws/routetable.go @@ -17,10 +17,12 @@ limitations under the License. package aws import ( + "context" "fmt" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/pkg/resources" @@ -29,16 +31,17 @@ import ( ) // DescribeRouteTables lists route-tables tagged for the cluster (shared and owned) -func DescribeRouteTables(cloud fi.Cloud, clusterName string) (map[string]*ec2.RouteTable, error) { +func DescribeRouteTables(cloud fi.Cloud, clusterName string) (map[string]ec2types.RouteTable, error) { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) - routeTables := make(map[string]*ec2.RouteTable) + routeTables := make(map[string]ec2types.RouteTable) klog.V(2).Info("Listing EC2 RouteTables") for _, filters := range buildEC2FiltersForCluster(clusterName) { request := &ec2.DescribeRouteTablesInput{ Filters: filters, } - response, err := c.EC2().DescribeRouteTables(request) + response, err := c.EC2().DescribeRouteTables(ctx, request) if err != nil { return nil, fmt.Errorf("error listing RouteTables: %v", err) } @@ -76,15 +79,15 @@ func dumpRouteTable(op *resources.DumpOperation, r *resources.Resource) error { return nil } -func buildTrackerForRouteTable(rt *ec2.RouteTable, clusterName string) *resources.Resource { +func buildTrackerForRouteTable(rt ec2types.RouteTable, clusterName string) *resources.Resource { resourceTracker := &resources.Resource{ Name: FindName(rt.Tags), ID: aws.ToString(rt.RouteTableId), - Type: ec2.ResourceTypeRouteTable, + Type: string(ec2types.ResourceTypeRouteTable), Obj: rt, Dumper: dumpRouteTable, Deleter: DeleteRouteTable, - Shared: !HasOwnedTag(ec2.ResourceTypeRouteTable+":"+*rt.RouteTableId, rt.Tags, clusterName), + Shared: !HasOwnedTag(string(ec2types.ResourceTypeRouteTable)+":"+*rt.RouteTableId, rt.Tags, clusterName), } var blocks []string diff --git a/pkg/resources/aws/securitygroup.go b/pkg/resources/aws/securitygroup.go index ee7685bbefeb1..a181b1f117653 100644 --- a/pkg/resources/aws/securitygroup.go +++ b/pkg/resources/aws/securitygroup.go @@ -17,10 +17,12 @@ limitations under the License. package aws import ( + "context" "fmt" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/pkg/resources" @@ -29,6 +31,7 @@ import ( ) func DeleteSecurityGroup(cloud fi.Cloud, t *resources.Resource) error { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) id := t.ID @@ -36,9 +39,9 @@ func DeleteSecurityGroup(cloud fi.Cloud, t *resources.Resource) error { // TODO: Move to a "pre-execute" phase? { request := &ec2.DescribeSecurityGroupsInput{ - GroupIds: []*string{&id}, + GroupIds: []string{id}, } - response, err := c.EC2().DescribeSecurityGroups(request) + response, err := c.EC2().DescribeSecurityGroups(ctx, request) if err != nil { if awsup.AWSErrorCode(err) == "InvalidGroup.NotFound" { klog.V(2).Infof("Got InvalidGroup.NotFound error describing SecurityGroup %q; will treat as already-deleted", id) @@ -60,7 +63,7 @@ func DeleteSecurityGroup(cloud fi.Cloud, t *resources.Resource) error { GroupId: &id, IpPermissions: sg.IpPermissions, } - _, err = c.EC2().RevokeSecurityGroupIngress(revoke) + _, err = c.EC2().RevokeSecurityGroupIngress(ctx, revoke) if err != nil { return fmt.Errorf("cannot revoke ingress for ID %q: %v", id, err) } @@ -72,7 +75,7 @@ func DeleteSecurityGroup(cloud fi.Cloud, t *resources.Resource) error { request := &ec2.DeleteSecurityGroupInput{ GroupId: &id, } - _, err := c.EC2().DeleteSecurityGroup(request) + _, err := c.EC2().DeleteSecurityGroup(ctx, request) if err != nil { if IsDependencyViolation(err) { return err @@ -86,7 +89,7 @@ func DeleteSecurityGroup(cloud fi.Cloud, t *resources.Resource) error { func DumpSecurityGroup(op *resources.DumpOperation, r *resources.Resource) error { data := make(map[string]interface{}) data["id"] = r.ID - data["type"] = ec2.ResourceTypeSecurityGroup + data["type"] = ec2types.ResourceTypeSecurityGroup data["raw"] = r.Obj op.Dump.Resources = append(op.Dump.Resources, data) return nil @@ -104,11 +107,11 @@ func ListSecurityGroups(cloud fi.Cloud, vpcID, clusterName string) ([]*resources resourceTracker := &resources.Resource{ Name: FindName(sg.Tags), ID: id, - Type: ec2.ResourceTypeSecurityGroup, + Type: string(ec2types.ResourceTypeSecurityGroup), Deleter: DeleteSecurityGroup, Dumper: DumpSecurityGroup, Obj: sg, - Shared: !HasOwnedTag(ec2.ResourceTypeSecurityGroup+":"+id, sg.Tags, clusterName), + Shared: !HasOwnedTag(string(ec2types.ResourceTypeSecurityGroup)+":"+id, sg.Tags, clusterName), } var blocks []string @@ -122,16 +125,17 @@ func ListSecurityGroups(cloud fi.Cloud, vpcID, clusterName string) ([]*resources return resourceTrackers, nil } -func DescribeSecurityGroups(cloud fi.Cloud, clusterName string) (map[string]*ec2.SecurityGroup, error) { +func DescribeSecurityGroups(cloud fi.Cloud, clusterName string) (map[string]ec2types.SecurityGroup, error) { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) - groups := make(map[string]*ec2.SecurityGroup) + groups := make(map[string]ec2types.SecurityGroup) klog.V(2).Infof("Listing EC2 SecurityGroups") for _, filters := range buildEC2FiltersForCluster(clusterName) { request := &ec2.DescribeSecurityGroupsInput{ Filters: filters, } - response, err := c.EC2().DescribeSecurityGroups(request) + response, err := c.EC2().DescribeSecurityGroups(ctx, request) if err != nil { return nil, fmt.Errorf("error listing SecurityGroups: %v", err) } diff --git a/pkg/resources/aws/subnet.go b/pkg/resources/aws/subnet.go index 73f55c6536700..261c32c6454f0 100644 --- a/pkg/resources/aws/subnet.go +++ b/pkg/resources/aws/subnet.go @@ -18,7 +18,7 @@ package aws import ( "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/pkg/resources" ) @@ -30,7 +30,7 @@ func DumpSubnet(op *resources.DumpOperation, r *resources.Resource) error { data["raw"] = r.Obj op.Dump.Resources = append(op.Dump.Resources, data) - ec2Subnet := r.Obj.(*ec2.Subnet) + ec2Subnet := r.Obj.(*ec2types.Subnet) s := &resources.Subnet{ ID: aws.ToString(ec2Subnet.SubnetId), Zone: aws.ToString(ec2Subnet.AvailabilityZone), diff --git a/pkg/resources/aws/vpc.go b/pkg/resources/aws/vpc.go index 1bbc4f6fc251c..e0b90e494b465 100644 --- a/pkg/resources/aws/vpc.go +++ b/pkg/resources/aws/vpc.go @@ -17,10 +17,12 @@ limitations under the License. package aws import ( + "context" "fmt" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/pkg/resources" "k8s.io/kops/upup/pkg/fi" @@ -29,6 +31,7 @@ import ( ) func DeleteVPC(cloud fi.Cloud, r *resources.Resource) error { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) id := r.ID @@ -37,7 +40,7 @@ func DeleteVPC(cloud fi.Cloud, r *resources.Resource) error { request := &ec2.DeleteVpcInput{ VpcId: &id, } - _, err := c.EC2().DeleteVpc(request) + _, err := c.EC2().DeleteVpc(ctx, request) if err != nil { if awsup.AWSErrorCode(err) == "InvalidVpcID.NotFound" { // Concurrently deleted @@ -55,11 +58,11 @@ func DeleteVPC(cloud fi.Cloud, r *resources.Resource) error { func DumpVPC(op *resources.DumpOperation, r *resources.Resource) error { data := make(map[string]interface{}) data["id"] = r.ID - data["type"] = ec2.ResourceTypeVpc + data["type"] = ec2types.ResourceTypeVpc data["raw"] = r.Obj op.Dump.Resources = append(op.Dump.Resources, data) - ec2VPC := r.Obj.(*ec2.Vpc) + ec2VPC := r.Obj.(*ec2types.Vpc) vpc := &resources.VPC{ ID: aws.ToString(ec2VPC.VpcId), } @@ -68,22 +71,23 @@ func DumpVPC(op *resources.DumpOperation, r *resources.Resource) error { return nil } -func DescribeVPC(cloud fi.Cloud, clusterName string) (*ec2.Vpc, error) { +func DescribeVPC(cloud fi.Cloud, clusterName string) (*ec2types.Vpc, error) { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) - vpcs := make(map[string]*ec2.Vpc) + vpcs := make(map[string]*ec2types.Vpc) klog.V(2).Info("Listing EC2 VPC") for _, filters := range buildEC2FiltersForCluster(clusterName) { request := &ec2.DescribeVpcsInput{ Filters: filters, } - response, err := c.EC2().DescribeVpcs(request) + response, err := c.EC2().DescribeVpcs(ctx, request) if err != nil { return nil, fmt.Errorf("error listing VPCs: %v", err) } for _, vpc := range response.Vpcs { - vpcs[aws.ToString(vpc.VpcId)] = vpc + vpcs[aws.ToString(vpc.VpcId)] = &vpc } } @@ -110,11 +114,11 @@ func ListVPCs(cloud fi.Cloud, clusterName string) ([]*resources.Resource, error) resourceTracker := &resources.Resource{ Name: FindName(vpc.Tags), ID: vpcID, - Type: ec2.ResourceTypeVpc, + Type: string(ec2types.ResourceTypeVpc), Deleter: DeleteVPC, Dumper: DumpVPC, Obj: vpc, - Shared: !HasOwnedTag(ec2.ResourceTypeVpc+":"+vpcID, vpc.Tags, clusterName), + Shared: !HasOwnedTag(string(ec2types.ResourceTypeVpc)+":"+vpcID, vpc.Tags, clusterName), } var blocks []string diff --git a/pkg/resources/aws/vpc_test.go b/pkg/resources/aws/vpc_test.go index 562e7228fceab..0eb0bf6100e92 100644 --- a/pkg/resources/aws/vpc_test.go +++ b/pkg/resources/aws/vpc_test.go @@ -17,16 +17,19 @@ limitations under the License. package aws import ( + "context" "testing" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/cloudmock/aws/mockec2" "k8s.io/kops/pkg/testutils" "k8s.io/kops/upup/pkg/fi/utils" ) func TestListVPCs(t *testing.T) { + ctx := context.Background() h := testutils.NewIntegrationTestHarness(t) defer h.Close() @@ -38,9 +41,9 @@ func TestListVPCs(t *testing.T) { mockEC2.CreateVpcWithId(&ec2.CreateVpcInput{ CidrBlock: aws.String("10.0.0.0/12"), }, "vpc-legacy") - mockEC2.CreateTags(&ec2.CreateTagsInput{ - Resources: aws.StringSlice([]string{"vpc-legacy"}), - Tags: []*ec2.Tag{ + mockEC2.CreateTags(ctx, &ec2.CreateTagsInput{ + Resources: []string{"vpc-legacy"}, + Tags: []ec2types.Tag{ {Key: aws.String("KubernetesCluster"), Value: aws.String("legacy.example.com")}, }, }) @@ -48,18 +51,18 @@ func TestListVPCs(t *testing.T) { mockEC2.CreateVpcWithId(&ec2.CreateVpcInput{ CidrBlock: aws.String("10.0.0.0/12"), }, "vpc-shared") - mockEC2.CreateTags(&ec2.CreateTagsInput{ - Resources: aws.StringSlice([]string{"vpc-shared"}), - Tags: []*ec2.Tag{ + mockEC2.CreateTags(ctx, &ec2.CreateTagsInput{ + Resources: []string{"vpc-shared"}, + Tags: []ec2types.Tag{ {Key: aws.String("kubernetes.io/cluster/shared.example.com"), Value: aws.String("shared")}, }, }) mockEC2.CreateVpcWithId(&ec2.CreateVpcInput{ CidrBlock: aws.String("10.0.0.0/12"), }, "vpc-shared-with-legacy") - mockEC2.CreateTags(&ec2.CreateTagsInput{ - Resources: aws.StringSlice([]string{"vpc-shared-with-legacy"}), - Tags: []*ec2.Tag{ + mockEC2.CreateTags(ctx, &ec2.CreateTagsInput{ + Resources: []string{"vpc-shared-with-legacy"}, + Tags: []ec2types.Tag{ {Key: aws.String("KubernetesCluster"), Value: aws.String("shared-with-legacy.example.com")}, {Key: aws.String("kubernetes.io/cluster/shared-with-legacy.example.com"), Value: aws.String("shared")}, }, @@ -68,9 +71,9 @@ func TestListVPCs(t *testing.T) { mockEC2.CreateVpcWithId(&ec2.CreateVpcInput{ CidrBlock: aws.String("10.0.0.0/12"), }, "vpc-owned") - mockEC2.CreateTags(&ec2.CreateTagsInput{ - Resources: aws.StringSlice([]string{"vpc-owned"}), - Tags: []*ec2.Tag{ + mockEC2.CreateTags(ctx, &ec2.CreateTagsInput{ + Resources: []string{"vpc-owned"}, + Tags: []ec2types.Tag{ {Key: aws.String("kubernetes.io/cluster/owned.example.com"), Value: aws.String("owned")}, }, }) @@ -78,9 +81,9 @@ func TestListVPCs(t *testing.T) { mockEC2.CreateVpcWithId(&ec2.CreateVpcInput{ CidrBlock: aws.String("10.0.0.0/12"), }, "vpc-owned-with-legacy") - mockEC2.CreateTags(&ec2.CreateTagsInput{ - Resources: aws.StringSlice([]string{"vpc-owned-with-legacy"}), - Tags: []*ec2.Tag{ + mockEC2.CreateTags(ctx, &ec2.CreateTagsInput{ + Resources: []string{"vpc-owned-with-legacy"}, + Tags: []ec2types.Tag{ {Key: aws.String("KubernetesCluster"), Value: aws.String("owned-with-legacy.example.com")}, {Key: aws.String("kubernetes.io/cluster/owned-with-legacy.example.com"), Value: aws.String("owned")}, }, @@ -89,9 +92,9 @@ func TestListVPCs(t *testing.T) { mockEC2.CreateVpcWithId(&ec2.CreateVpcInput{ CidrBlock: aws.String("10.0.0.0/12"), }, "vpc-other") - mockEC2.CreateTags(&ec2.CreateTagsInput{ - Resources: aws.StringSlice([]string{"vpc-other"}), - Tags: []*ec2.Tag{ + mockEC2.CreateTags(ctx, &ec2.CreateTagsInput{ + Resources: []string{"vpc-other"}, + Tags: []ec2types.Tag{ {Key: aws.String("KubernetesCluster"), Value: aws.String("other.example.com")}, {Key: aws.String("kubernetes.io/cluster/other.example.com"), Value: aws.String("shared")}, }, diff --git a/pkg/testutils/integrationtestharness.go b/pkg/testutils/integrationtestharness.go index 77106c8c316ad..ec4f89db5d56e 100644 --- a/pkg/testutils/integrationtestharness.go +++ b/pkg/testutils/integrationtestharness.go @@ -28,10 +28,11 @@ import ( "k8s.io/kops/cloudmock/aws/mocksqs" "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2" "github.com/aws/aws-sdk-go-v2/service/iam" route53types "github.com/aws/aws-sdk-go-v2/service/route53/types" - "github.com/aws/aws-sdk-go/service/ec2" "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" "github.com/gophercloud/gophercloud/openstack/dns/v2/zones" "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" @@ -189,10 +190,10 @@ func (h *IntegrationTestHarness) SetupMockAWS() *awsup.MockAWSCloud { mockEC2.CreateVpcWithId(&ec2.CreateVpcInput{ CidrBlock: aws.String("172.20.0.0/16"), - TagSpecifications: []*ec2.TagSpecification{ + TagSpecifications: []ec2types.TagSpecification{ { - ResourceType: aws.String(ec2.ResourceTypeVpc), - Tags: []*ec2.Tag{ + ResourceType: ec2types.ResourceTypeVpc, + Tags: []ec2types.Tag{ { Key: aws.String("kubernetes.io/cluster/minimal.example.com"), Value: aws.String(""), @@ -201,12 +202,12 @@ func (h *IntegrationTestHarness) SetupMockAWS() *awsup.MockAWSCloud { }, }, }, "vpc-12345678") - mockEC2.CreateInternetGateway(&ec2.CreateInternetGatewayInput{}) - mockEC2.AttachInternetGateway(&ec2.AttachInternetGatewayInput{ + mockEC2.CreateInternetGateway(ctx, &ec2.CreateInternetGatewayInput{}) + mockEC2.AttachInternetGateway(ctx, &ec2.AttachInternetGatewayInput{ InternetGatewayId: aws.String("igw-1"), VpcId: aws.String("vpc-12345678"), }) - mockEC2.CreateEgressOnlyInternetGateway(&ec2.CreateEgressOnlyInternetGatewayInput{ + mockEC2.CreateEgressOnlyInternetGateway(ctx, &ec2.CreateEgressOnlyInternetGatewayInput{ VpcId: aws.String("vpc-12345678"), }) @@ -219,7 +220,7 @@ func (h *IntegrationTestHarness) SetupMockAWS() *awsup.MockAWSCloud { AvailabilityZone: aws.String("us-test-1a"), CidrBlock: aws.String("172.20.32.0/19"), }, "subnet-12345678") - mockEC2.AssociateRouteTable(&ec2.AssociateRouteTableInput{ + mockEC2.AssociateRouteTable(ctx, &ec2.AssociateRouteTableInput{ RouteTableId: aws.String("rtb-12345678"), SubnetId: aws.String("subnet-12345678"), }) @@ -234,7 +235,7 @@ func (h *IntegrationTestHarness) SetupMockAWS() *awsup.MockAWSCloud { CidrBlock: aws.String("172.20.8.0/22"), }, "subnet-b2345678") - mockEC2.AssociateRouteTable(&ec2.AssociateRouteTableInput{ + mockEC2.AssociateRouteTable(ctx, &ec2.AssociateRouteTableInput{ RouteTableId: aws.String("rtb-12345678"), SubnetId: aws.String("subnet-abcdef"), }) diff --git a/upup/pkg/fi/cloudup/awstasks/dhcp_options.go b/upup/pkg/fi/cloudup/awstasks/dhcp_options.go index 9383e5d20f0ea..db41d6e6df13f 100644 --- a/upup/pkg/fi/cloudup/awstasks/dhcp_options.go +++ b/upup/pkg/fi/cloudup/awstasks/dhcp_options.go @@ -17,11 +17,13 @@ limitations under the License. package awstasks import ( + "context" "fmt" "strings" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" @@ -56,12 +58,12 @@ func (e *DHCPOptions) Find(c *fi.CloudupContext) (*DHCPOptions, error) { request := &ec2.DescribeDhcpOptionsInput{} if e.ID != nil { - request.DhcpOptionsIds = []*string{e.ID} + request.DhcpOptionsIds = []string{aws.ToString(e.ID)} } else { request.Filters = cloud.BuildFilters(e.Name) } - response, err := cloud.EC2().DescribeDhcpOptions(request) + response, err := cloud.EC2().DescribeDhcpOptions(c.Context(), request) if err != nil { return nil, fmt.Errorf("error listing DHCPOptions: %v", err) } @@ -137,28 +139,29 @@ func (s *DHCPOptions) CheckChanges(a, e, changes *DHCPOptions) error { } func (_ *DHCPOptions) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *DHCPOptions) error { + ctx := context.TODO() if a == nil { klog.V(2).Infof("Creating DHCPOptions with Name:%q", *e.Name) request := &ec2.CreateDhcpOptionsInput{ - TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeDhcpOptions, e.Tags), + TagSpecifications: awsup.EC2TagSpecification(ec2types.ResourceTypeDhcpOptions, e.Tags), } if e.DomainNameServers != nil { - o := &ec2.NewDhcpConfiguration{ + o := ec2types.NewDhcpConfiguration{ Key: aws.String("domain-name-servers"), - Values: []*string{e.DomainNameServers}, + Values: []string{aws.ToString(e.DomainNameServers)}, } request.DhcpConfigurations = append(request.DhcpConfigurations, o) } if e.DomainName != nil { - o := &ec2.NewDhcpConfiguration{ + o := ec2types.NewDhcpConfiguration{ Key: aws.String("domain-name"), - Values: []*string{e.DomainName}, + Values: []string{aws.ToString(e.DomainName)}, } request.DhcpConfigurations = append(request.DhcpConfigurations, o) } - response, err := t.Cloud.EC2().CreateDhcpOptions(request) + response, err := t.Cloud.EC2().CreateDhcpOptions(ctx, request) if err != nil { return fmt.Errorf("error creating DHCPOptions: %v", err) } diff --git a/upup/pkg/fi/cloudup/awstasks/egressonlyinternetgateway.go b/upup/pkg/fi/cloudup/awstasks/egressonlyinternetgateway.go index dc76525eb9f36..9ed8924b250a8 100644 --- a/upup/pkg/fi/cloudup/awstasks/egressonlyinternetgateway.go +++ b/upup/pkg/fi/cloudup/awstasks/egressonlyinternetgateway.go @@ -17,9 +17,11 @@ limitations under the License. package awstasks import ( + "context" "fmt" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" @@ -47,8 +49,8 @@ func (e *EgressOnlyInternetGateway) CompareWithID() *string { return e.ID } -func findEgressOnlyInternetGateway(cloud awsup.AWSCloud, request *ec2.DescribeEgressOnlyInternetGatewaysInput) (*ec2.EgressOnlyInternetGateway, error) { - response, err := cloud.EC2().DescribeEgressOnlyInternetGateways(request) +func findEgressOnlyInternetGateway(ctx context.Context, cloud awsup.AWSCloud, request *ec2.DescribeEgressOnlyInternetGatewaysInput) (*ec2types.EgressOnlyInternetGateway, error) { + response, err := cloud.EC2().DescribeEgressOnlyInternetGateways(ctx, request) if err != nil { return nil, fmt.Errorf("error listing EgressOnlyInternetGateways: %v", err) } @@ -60,10 +62,11 @@ func findEgressOnlyInternetGateway(cloud awsup.AWSCloud, request *ec2.DescribeEg return nil, fmt.Errorf("found multiple EgressOnlyInternetGateways matching tags") } igw := response.EgressOnlyInternetGateways[0] - return igw, nil + return &igw, nil } func (e *EgressOnlyInternetGateway) Find(c *fi.CloudupContext) (*EgressOnlyInternetGateway, error) { + ctx := c.Context() cloud := c.T.Cloud.(awsup.AWSCloud) request := &ec2.DescribeEgressOnlyInternetGatewaysInput{} @@ -74,16 +77,16 @@ func (e *EgressOnlyInternetGateway) Find(c *fi.CloudupContext) (*EgressOnlyInter return nil, fmt.Errorf("VPC ID is required when EgressOnlyInternetGateway is shared") } - request.Filters = []*ec2.Filter{awsup.NewEC2Filter("attachment.vpc-id", *e.VPC.ID)} + request.Filters = []ec2types.Filter{awsup.NewEC2Filter("attachment.vpc-id", *e.VPC.ID)} } else { if e.ID != nil { - request.EgressOnlyInternetGatewayIds = []*string{e.ID} + request.EgressOnlyInternetGatewayIds = []string{fi.ValueOf(e.ID)} } else { request.Filters = cloud.BuildFilters(e.Name) } } - eigw, err := findEgressOnlyInternetGateway(cloud, request) + eigw, err := findEgressOnlyInternetGateway(ctx, cloud, request) if err != nil { return nil, err } @@ -135,6 +138,7 @@ func (s *EgressOnlyInternetGateway) CheckChanges(a, e, changes *EgressOnlyIntern } func (_ *EgressOnlyInternetGateway) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *EgressOnlyInternetGateway) error { + ctx := context.TODO() shared := fi.ValueOf(e.Shared) if shared { // Verify the EgressOnlyInternetGateway was found and matches our required settings @@ -150,10 +154,10 @@ func (_ *EgressOnlyInternetGateway) RenderAWS(t *awsup.AWSAPITarget, a, e, chang request := &ec2.CreateEgressOnlyInternetGatewayInput{ VpcId: e.VPC.ID, - TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeEgressOnlyInternetGateway, e.Tags), + TagSpecifications: awsup.EC2TagSpecification(ec2types.ResourceTypeEgressOnlyInternetGateway, e.Tags), } - response, err := t.Cloud.EC2().CreateEgressOnlyInternetGateway(request) + response, err := t.Cloud.EC2().CreateEgressOnlyInternetGateway(ctx, request) if err != nil { return fmt.Errorf("error creating EgressOnlyInternetGateway: %v", err) } @@ -171,6 +175,7 @@ type terraformEgressOnlyInternetGateway struct { } func (_ *EgressOnlyInternetGateway) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *EgressOnlyInternetGateway) error { + ctx := context.TODO() shared := fi.ValueOf(e.Shared) if shared { // Not terraform owned / managed @@ -182,8 +187,8 @@ func (_ *EgressOnlyInternetGateway) RenderTerraform(t *terraform.TerraformTarget if vpcID == "" { return fmt.Errorf("VPC ID is required when EgressOnlyInternetGateway is shared") } - request.Filters = []*ec2.Filter{awsup.NewEC2Filter("attachment.vpc-id", vpcID)} - igw, err := findEgressOnlyInternetGateway(t.Cloud.(awsup.AWSCloud), request) + request.Filters = []ec2types.Filter{awsup.NewEC2Filter("attachment.vpc-id", vpcID)} + igw, err := findEgressOnlyInternetGateway(ctx, t.Cloud.(awsup.AWSCloud), request) if err != nil { return err } diff --git a/upup/pkg/fi/cloudup/awstasks/egressonlyinternetgateway_test.go b/upup/pkg/fi/cloudup/awstasks/egressonlyinternetgateway_test.go index 120338e0d6566..bc294aaeb185f 100644 --- a/upup/pkg/fi/cloudup/awstasks/egressonlyinternetgateway_test.go +++ b/upup/pkg/fi/cloudup/awstasks/egressonlyinternetgateway_test.go @@ -22,7 +22,8 @@ import ( "testing" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/cloudmock/aws/mockec2" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" @@ -36,15 +37,15 @@ func TestSharedEgressOnlyInternetGatewayDoesNotRename(t *testing.T) { cloud.MockEC2 = c // Pre-create the vpc / subnet - vpc, err := c.CreateVpc(&ec2.CreateVpcInput{ + vpc, err := c.CreateVpc(ctx, &ec2.CreateVpcInput{ CidrBlock: aws.String("172.20.0.0/16"), }) if err != nil { t.Fatalf("error creating test VPC: %v", err) } - _, err = c.CreateTags(&ec2.CreateTagsInput{ - Resources: []*string{vpc.Vpc.VpcId}, - Tags: []*ec2.Tag{ + _, err = c.CreateTags(ctx, &ec2.CreateTagsInput{ + Resources: []string{aws.ToString(vpc.Vpc.VpcId)}, + Tags: []ec2types.Tag{ { Key: aws.String("Name"), Value: aws.String("ExistingVPC"), @@ -55,9 +56,9 @@ func TestSharedEgressOnlyInternetGatewayDoesNotRename(t *testing.T) { t.Fatalf("error tagging test vpc: %v", err) } - internetGateway, err := c.CreateEgressOnlyInternetGateway(&ec2.CreateEgressOnlyInternetGatewayInput{ + internetGateway, err := c.CreateEgressOnlyInternetGateway(ctx, &ec2.CreateEgressOnlyInternetGatewayInput{ VpcId: vpc.Vpc.VpcId, - TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeEgressOnlyInternetGateway, map[string]string{ + TagSpecifications: awsup.EC2TagSpecification(ec2types.ResourceTypeEgressOnlyInternetGateway, map[string]string{ "Name": "ExistingInternetGateway", }), }) @@ -108,12 +109,12 @@ func TestSharedEgressOnlyInternetGatewayDoesNotRename(t *testing.T) { if actual == nil { t.Fatalf("EgressOnlyInternetGateway created but then not found") } - expected := &ec2.EgressOnlyInternetGateway{ + expected := &ec2types.EgressOnlyInternetGateway{ EgressOnlyInternetGatewayId: aws.String("eigw-1"), Tags: buildTags(map[string]string{ "Name": "ExistingInternetGateway", }), - Attachments: []*ec2.InternetGatewayAttachment{ + Attachments: []ec2types.InternetGatewayAttachment{ { VpcId: vpc.Vpc.VpcId, }, diff --git a/upup/pkg/fi/cloudup/awstasks/elastic_ip.go b/upup/pkg/fi/cloudup/awstasks/elastic_ip.go index ec7dd028913f0..9fb3b12cc2a3c 100644 --- a/upup/pkg/fi/cloudup/awstasks/elastic_ip.go +++ b/upup/pkg/fi/cloudup/awstasks/elastic_ip.go @@ -17,10 +17,12 @@ limitations under the License. package awstasks import ( + "context" "fmt" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi/cloudup/terraformWriter" @@ -59,18 +61,18 @@ func (e *ElasticIP) CompareWithID() *string { } // Find returns the actual ElasticIP state, or nil if not found -func (e *ElasticIP) Find(context *fi.CloudupContext) (*ElasticIP, error) { - return e.find(context.T.Cloud.(awsup.AWSCloud)) +func (e *ElasticIP) Find(c *fi.CloudupContext) (*ElasticIP, error) { + return e.find(c.Context(), c.T.Cloud.(awsup.AWSCloud)) } // find will attempt to look up the elastic IP from AWS -func (e *ElasticIP) find(cloud awsup.AWSCloud) (*ElasticIP, error) { +func (e *ElasticIP) find(ctx context.Context, cloud awsup.AWSCloud) (*ElasticIP, error) { publicIP := e.PublicIP allocationID := e.ID // Find via RouteTable -> NatGateway -> ElasticIP if allocationID == nil && publicIP == nil && e.AssociatedNatGatewayRouteTable != nil { - ngw, err := findNatGatewayFromRouteTable(cloud, e.AssociatedNatGatewayRouteTable) + ngw, err := findNatGatewayFromRouteTable(ctx, cloud, e.AssociatedNatGatewayRouteTable) if err != nil { return nil, fmt.Errorf("error finding AssociatedNatGatewayRouteTable: %v", err) } @@ -96,7 +98,7 @@ func (e *ElasticIP) find(cloud awsup.AWSCloud) (*ElasticIP, error) { // Find via tag on subnet // TODO: Deprecated, because doesn't round-trip with terraform if allocationID == nil && publicIP == nil && e.TagOnSubnet != nil && e.TagOnSubnet.ID != nil { - var filters []*ec2.Filter + var filters []ec2types.Filter filters = append(filters, awsup.NewEC2Filter("key", "AssociatedElasticIp")) filters = append(filters, awsup.NewEC2Filter("resource-id", *e.TagOnSubnet.ID)) @@ -104,7 +106,7 @@ func (e *ElasticIP) find(cloud awsup.AWSCloud) (*ElasticIP, error) { Filters: filters, } - response, err := cloud.EC2().DescribeTags(request) + response, err := cloud.EC2().DescribeTags(ctx, request) if err != nil { return nil, fmt.Errorf("error listing tags: %v", err) } @@ -124,12 +126,12 @@ func (e *ElasticIP) find(cloud awsup.AWSCloud) (*ElasticIP, error) { if publicIP != nil || allocationID != nil { request := &ec2.DescribeAddressesInput{} if allocationID != nil { - request.AllocationIds = []*string{allocationID} + request.AllocationIds = []string{fi.ValueOf(allocationID)} } else if publicIP != nil { - request.Filters = []*ec2.Filter{awsup.NewEC2Filter("public-ip", *publicIP)} + request.Filters = []ec2types.Filter{awsup.NewEC2Filter("public-ip", *publicIP)} } - response, err := cloud.EC2().DescribeAddresses(request) + response, err := cloud.EC2().DescribeAddresses(ctx, request) if err != nil { return nil, fmt.Errorf("error listing ElasticIPs: %v", err) } @@ -150,20 +152,20 @@ func (e *ElasticIP) find(cloud awsup.AWSCloud) (*ElasticIP, error) { actual.AssociatedNatGatewayRouteTable = e.AssociatedNatGatewayRouteTable { - tags, err := cloud.EC2().DescribeTags(&ec2.DescribeTagsInput{ - Filters: []*ec2.Filter{ + tags, err := cloud.EC2().DescribeTags(ctx, &ec2.DescribeTagsInput{ + Filters: []ec2types.Filter{ { Name: aws.String("resource-id"), - Values: aws.StringSlice([]string{*a.AllocationId}), + Values: []string{aws.ToString(a.AllocationId)}, }, }, }) if err != nil { return nil, fmt.Errorf("error querying tags for ElasticIP: %v", err) } - var ec2Tags []*ec2.Tag + var ec2Tags []ec2types.Tag for _, t := range tags.Tags { - ec2Tags = append(ec2Tags, &ec2.Tag{ + ec2Tags = append(ec2Tags, ec2types.Tag{ Key: t.Key, Value: t.Value, }) @@ -220,6 +222,7 @@ func (_ *ElasticIP) CheckChanges(a, e, changes *ElasticIP) error { // RenderAWS is where we actually apply changes to AWS func (_ *ElasticIP) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *ElasticIP) error { + ctx := context.TODO() var publicIp *string var eipId *string @@ -228,11 +231,11 @@ func (_ *ElasticIP) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *ElasticIP) e klog.V(2).Infof("Creating ElasticIP for VPC") request := &ec2.AllocateAddressInput{ - TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeElasticIp, e.Tags), + TagSpecifications: awsup.EC2TagSpecification(ec2types.ResourceTypeElasticIp, e.Tags), } - request.Domain = aws.String(ec2.DomainTypeVpc) + request.Domain = ec2types.DomainTypeVpc - response, err := t.Cloud.EC2().AllocateAddress(request) + response, err := t.Cloud.EC2().AllocateAddress(ctx, request) if err != nil { return fmt.Errorf("error creating ElasticIP: %v", err) } diff --git a/upup/pkg/fi/cloudup/awstasks/elastic_ip_test.go b/upup/pkg/fi/cloudup/awstasks/elastic_ip_test.go index 84a7c7b8d4ffb..c4156e49d3c38 100644 --- a/upup/pkg/fi/cloudup/awstasks/elastic_ip_test.go +++ b/upup/pkg/fi/cloudup/awstasks/elastic_ip_test.go @@ -24,7 +24,7 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/util/pkg/vfs" "k8s.io/kops/cloudmock/aws/mockec2" @@ -88,11 +88,11 @@ func TestElasticIPCreate(t *testing.T) { t.Fatalf("Expected exactly one ElasticIP; found %v", c.Addresses) } - expected := &ec2.Address{ + expected := &ec2types.Address{ AllocationId: eip1.ID, - Domain: s("vpc"), + Domain: ec2types.DomainTypeVpc, PublicIp: s("192.0.2.1"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: s("Name"), Value: s("eip1"), diff --git a/upup/pkg/fi/cloudup/awstasks/internetgateway.go b/upup/pkg/fi/cloudup/awstasks/internetgateway.go index 74c1913d7e05d..2bf7e0d9b7eb8 100644 --- a/upup/pkg/fi/cloudup/awstasks/internetgateway.go +++ b/upup/pkg/fi/cloudup/awstasks/internetgateway.go @@ -17,9 +17,11 @@ limitations under the License. package awstasks import ( + "context" "fmt" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" @@ -47,8 +49,8 @@ func (e *InternetGateway) CompareWithID() *string { return e.ID } -func findInternetGateway(cloud awsup.AWSCloud, request *ec2.DescribeInternetGatewaysInput) (*ec2.InternetGateway, error) { - response, err := cloud.EC2().DescribeInternetGateways(request) +func findInternetGateway(ctx context.Context, cloud awsup.AWSCloud, request *ec2.DescribeInternetGatewaysInput) (*ec2types.InternetGateway, error) { + response, err := cloud.EC2().DescribeInternetGateways(ctx, request) if err != nil { return nil, fmt.Errorf("error listing InternetGateways: %v", err) } @@ -60,10 +62,11 @@ func findInternetGateway(cloud awsup.AWSCloud, request *ec2.DescribeInternetGate return nil, fmt.Errorf("found multiple InternetGateways matching tags") } igw := response.InternetGateways[0] - return igw, nil + return &igw, nil } func (e *InternetGateway) Find(c *fi.CloudupContext) (*InternetGateway, error) { + ctx := c.Context() cloud := c.T.Cloud.(awsup.AWSCloud) request := &ec2.DescribeInternetGatewaysInput{} @@ -74,16 +77,16 @@ func (e *InternetGateway) Find(c *fi.CloudupContext) (*InternetGateway, error) { return nil, fmt.Errorf("VPC ID is required when InternetGateway is shared") } - request.Filters = []*ec2.Filter{awsup.NewEC2Filter("attachment.vpc-id", *e.VPC.ID)} + request.Filters = []ec2types.Filter{awsup.NewEC2Filter("attachment.vpc-id", *e.VPC.ID)} } else { if e.ID != nil { - request.InternetGatewayIds = []*string{e.ID} + request.InternetGatewayIds = []string{fi.ValueOf(e.ID)} } else { request.Filters = cloud.BuildFilters(e.Name) } } - igw, err := findInternetGateway(cloud, request) + igw, err := findInternetGateway(ctx, cloud, request) if err != nil { return nil, err } @@ -136,6 +139,7 @@ func (s *InternetGateway) CheckChanges(a, e, changes *InternetGateway) error { } func (_ *InternetGateway) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *InternetGateway) error { + ctx := context.TODO() shared := fi.ValueOf(e.Shared) if shared { // Verify the InternetGateway was found and matches our required settings @@ -150,10 +154,10 @@ func (_ *InternetGateway) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Intern klog.V(2).Infof("Creating InternetGateway") request := &ec2.CreateInternetGatewayInput{ - TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeInternetGateway, e.Tags), + TagSpecifications: awsup.EC2TagSpecification(ec2types.ResourceTypeInternetGateway, e.Tags), } - response, err := t.Cloud.EC2().CreateInternetGateway(request) + response, err := t.Cloud.EC2().CreateInternetGateway(ctx, request) if err != nil { return fmt.Errorf("error creating InternetGateway: %v", err) } @@ -169,7 +173,7 @@ func (_ *InternetGateway) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Intern InternetGatewayId: e.ID, } - _, err := t.Cloud.EC2().AttachInternetGateway(attachRequest) + _, err := t.Cloud.EC2().AttachInternetGateway(ctx, attachRequest) if err != nil { return fmt.Errorf("error attaching InternetGateway to VPC: %v", err) } @@ -184,6 +188,7 @@ type terraformInternetGateway struct { } func (_ *InternetGateway) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *InternetGateway) error { + ctx := context.TODO() shared := fi.ValueOf(e.Shared) if shared { // Not terraform owned / managed @@ -195,8 +200,8 @@ func (_ *InternetGateway) RenderTerraform(t *terraform.TerraformTarget, a, e, ch if vpcID == "" { return fmt.Errorf("VPC ID is required when InternetGateway is shared") } - request.Filters = []*ec2.Filter{awsup.NewEC2Filter("attachment.vpc-id", vpcID)} - igw, err := findInternetGateway(t.Cloud.(awsup.AWSCloud), request) + request.Filters = []ec2types.Filter{awsup.NewEC2Filter("attachment.vpc-id", vpcID)} + igw, err := findInternetGateway(ctx, t.Cloud.(awsup.AWSCloud), request) if err != nil { return err } diff --git a/upup/pkg/fi/cloudup/awstasks/internetgateway_test.go b/upup/pkg/fi/cloudup/awstasks/internetgateway_test.go index 005571c69fe30..8042aa514e299 100644 --- a/upup/pkg/fi/cloudup/awstasks/internetgateway_test.go +++ b/upup/pkg/fi/cloudup/awstasks/internetgateway_test.go @@ -22,7 +22,8 @@ import ( "testing" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/cloudmock/aws/mockec2" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" @@ -36,15 +37,15 @@ func TestSharedInternetGatewayDoesNotRename(t *testing.T) { cloud.MockEC2 = c // Pre-create the vpc / subnet - vpc, err := c.CreateVpc(&ec2.CreateVpcInput{ + vpc, err := c.CreateVpc(ctx, &ec2.CreateVpcInput{ CidrBlock: aws.String("172.20.0.0/16"), }) if err != nil { t.Fatalf("error creating test VPC: %v", err) } - _, err = c.CreateTags(&ec2.CreateTagsInput{ - Resources: []*string{vpc.Vpc.VpcId}, - Tags: []*ec2.Tag{ + _, err = c.CreateTags(ctx, &ec2.CreateTagsInput{ + Resources: []string{aws.ToString(vpc.Vpc.VpcId)}, + Tags: []ec2types.Tag{ { Key: aws.String("Name"), Value: aws.String("ExistingVPC"), @@ -55,14 +56,14 @@ func TestSharedInternetGatewayDoesNotRename(t *testing.T) { t.Fatalf("error tagging test vpc: %v", err) } - internetGateway, err := c.CreateInternetGateway(&ec2.CreateInternetGatewayInput{}) + internetGateway, err := c.CreateInternetGateway(ctx, &ec2.CreateInternetGatewayInput{}) if err != nil { t.Fatalf("error creating test igw: %v", err) } - _, err = c.CreateTags(&ec2.CreateTagsInput{ - Resources: []*string{internetGateway.InternetGateway.InternetGatewayId}, - Tags: []*ec2.Tag{ + _, err = c.CreateTags(ctx, &ec2.CreateTagsInput{ + Resources: []string{aws.ToString(internetGateway.InternetGateway.InternetGatewayId)}, + Tags: []ec2types.Tag{ { Key: aws.String("Name"), Value: aws.String("ExistingInternetGateway"), @@ -73,7 +74,7 @@ func TestSharedInternetGatewayDoesNotRename(t *testing.T) { t.Fatalf("error tagging test igw: %v", err) } - _, err = c.AttachInternetGateway(&ec2.AttachInternetGatewayInput{ + _, err = c.AttachInternetGateway(ctx, &ec2.AttachInternetGatewayInput{ InternetGatewayId: internetGateway.InternetGateway.InternetGatewayId, VpcId: vpc.Vpc.VpcId, }) @@ -124,12 +125,12 @@ func TestSharedInternetGatewayDoesNotRename(t *testing.T) { if actual == nil { t.Fatalf("InternetGateway created but then not found") } - expected := &ec2.InternetGateway{ + expected := &ec2types.InternetGateway{ InternetGatewayId: aws.String("igw-1"), Tags: buildTags(map[string]string{ "Name": "ExistingInternetGateway", }), - Attachments: []*ec2.InternetGatewayAttachment{ + Attachments: []ec2types.InternetGatewayAttachment{ { VpcId: vpc.Vpc.VpcId, }, diff --git a/upup/pkg/fi/cloudup/awstasks/natgateway.go b/upup/pkg/fi/cloudup/awstasks/natgateway.go index c947655c1ed98..39a59f1e90d6d 100644 --- a/upup/pkg/fi/cloudup/awstasks/natgateway.go +++ b/upup/pkg/fi/cloudup/awstasks/natgateway.go @@ -17,10 +17,12 @@ limitations under the License. package awstasks import ( + "context" "fmt" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/klog/v2" raws "k8s.io/kops/pkg/resources/aws" @@ -59,20 +61,20 @@ func (e *NatGateway) CompareWithID() *string { } func (e *NatGateway) Find(c *fi.CloudupContext) (*NatGateway, error) { + ctx := c.Context() cloud := c.T.Cloud.(awsup.AWSCloud) - var ngw *ec2.NatGateway + var ngw *ec2types.NatGateway actual := &NatGateway{} if fi.ValueOf(e.ID) != "" { // We have an existing NGW, lets look up the EIP - var ngwIds []*string - ngwIds = append(ngwIds, e.ID) + ngwIds := []string{fi.ValueOf(e.ID)} request := &ec2.DescribeNatGatewaysInput{ NatGatewayIds: ngwIds, } - response, err := cloud.EC2().DescribeNatGateways(request) + response, err := cloud.EC2().DescribeNatGateways(ctx, request) if err != nil { return nil, fmt.Errorf("error listing Nat Gateways %v", err) } @@ -80,7 +82,7 @@ func (e *NatGateway) Find(c *fi.CloudupContext) (*NatGateway, error) { if len(response.NatGateways) != 1 { return nil, fmt.Errorf("found %d Nat Gateways with ID %q, expected 1", len(response.NatGateways), fi.ValueOf(e.ID)) } - ngw = response.NatGateways[0] + ngw = &response.NatGateways[0] if len(ngw.NatGatewayAddresses) != 1 { return nil, fmt.Errorf("found %d EIP Addresses for 1 NATGateway, expected 1", len(ngw.NatGatewayAddresses)) @@ -126,14 +128,15 @@ func (e *NatGateway) Find(c *fi.CloudupContext) (*NatGateway, error) { return actual, nil } -func (e *NatGateway) findNatGateway(c *fi.CloudupContext) (*ec2.NatGateway, error) { +func (e *NatGateway) findNatGateway(c *fi.CloudupContext) (*ec2types.NatGateway, error) { + ctx := c.Context() cloud := c.T.Cloud.(awsup.AWSCloud) id := e.ID // Find via route on private route table if id == nil && e.AssociatedRouteTable != nil { - ngw, err := findNatGatewayFromRouteTable(cloud, e.AssociatedRouteTable) + ngw, err := findNatGatewayFromRouteTable(ctx, cloud, e.AssociatedRouteTable) if err != nil { return nil, err } @@ -145,7 +148,7 @@ func (e *NatGateway) findNatGateway(c *fi.CloudupContext) (*ec2.NatGateway, erro // Find via tag on subnet // TODO: Obsolete - we can get from the route table instead if id == nil && e.Subnet != nil { - var filters []*ec2.Filter + var filters []ec2types.Filter filters = append(filters, awsup.NewEC2Filter("key", "AssociatedNatgateway")) if e.Subnet.ID == nil { klog.V(2).Infof("Unable to find subnet, bypassing Find() for NatGateway") @@ -157,7 +160,7 @@ func (e *NatGateway) findNatGateway(c *fi.CloudupContext) (*ec2.NatGateway, erro Filters: filters, } - response, err := cloud.EC2().DescribeTags(request) + response, err := cloud.EC2().DescribeTags(ctx, request) if err != nil { return nil, fmt.Errorf("error listing tags: %v", err) } @@ -175,35 +178,35 @@ func (e *NatGateway) findNatGateway(c *fi.CloudupContext) (*ec2.NatGateway, erro } if id != nil { - return findNatGatewayById(cloud, id) + return findNatGatewayById(ctx, cloud, fi.ValueOf(id)) } return nil, nil } -func findNatGatewayById(cloud awsup.AWSCloud, id *string) (*ec2.NatGateway, error) { +func findNatGatewayById(ctx context.Context, cloud awsup.AWSCloud, id string) (*ec2types.NatGateway, error) { request := &ec2.DescribeNatGatewaysInput{} - request.NatGatewayIds = []*string{id} - response, err := cloud.EC2().DescribeNatGateways(request) + request.NatGatewayIds = []string{id} + response, err := cloud.EC2().DescribeNatGateways(ctx, request) if err != nil { - return nil, fmt.Errorf("error listing NatGateway %q: %v", aws.ToString(id), err) + return nil, fmt.Errorf("error listing NatGateway %q: %v", id, err) } if response == nil || len(response.NatGateways) == 0 { - klog.V(2).Infof("Unable to find NatGateway %q", aws.ToString(id)) + klog.V(2).Infof("Unable to find NatGateway %q", id) return nil, nil } if len(response.NatGateways) != 1 { - return nil, fmt.Errorf("found multiple NatGateways with id %q", aws.ToString(id)) + return nil, fmt.Errorf("found multiple NatGateways with id %q", id) } - return response.NatGateways[0], nil + return &response.NatGateways[0], nil } -func findNatGatewayFromRouteTable(cloud awsup.AWSCloud, routeTable *RouteTable) (*ec2.NatGateway, error) { +func findNatGatewayFromRouteTable(ctx context.Context, cloud awsup.AWSCloud, routeTable *RouteTable) (*ec2types.NatGateway, error) { // Find via route on private route table if routeTable.ID != nil { klog.V(2).Infof("trying to match NatGateway via RouteTable %s", *routeTable.ID) - rt, err := findRouteTableByID(cloud, *routeTable.ID) + rt, err := findRouteTableByID(ctx, cloud, *routeTable.ID) if err != nil { return nil, fmt.Errorf("error finding associated RouteTable to NatGateway: %v", err) } @@ -225,14 +228,14 @@ func findNatGatewayFromRouteTable(cloud awsup.AWSCloud, routeTable *RouteTable) if !ok { return nil, fmt.Errorf("Could not find '%s' tag from route table", awsup.TagClusterName) } - filteredNatGateways := []*ec2.NatGateway{} + filteredNatGateways := []*ec2types.NatGateway{} for _, natGatewayID := range natGatewayIDs { - gw, err := findNatGatewayById(cloud, natGatewayID) + gw, err := findNatGatewayById(ctx, cloud, fi.ValueOf(natGatewayID)) if err != nil { return nil, err } - if raws.HasOwnedTag(ec2.ResourceTypeNatgateway+":"+fi.ValueOf(natGatewayID), gw.Tags, clusterName) { + if raws.HasOwnedTag(string(ec2types.ResourceTypeNatgateway)+":"+fi.ValueOf(natGatewayID), gw.Tags, clusterName) { filteredNatGateways = append(filteredNatGateways, gw) } } @@ -244,7 +247,7 @@ func findNatGatewayFromRouteTable(cloud awsup.AWSCloud, routeTable *RouteTable) return filteredNatGateways[0], nil } } else { - return findNatGatewayById(cloud, natGatewayIDs[0]) + return findNatGatewayById(ctx, cloud, fi.ValueOf(natGatewayIDs[0])) } } } @@ -297,6 +300,7 @@ func (e *NatGateway) Run(c *fi.CloudupContext) error { func (_ *NatGateway) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *NatGateway) error { // New NGW + ctx := context.TODO() var id *string if a == nil { @@ -308,11 +312,11 @@ func (_ *NatGateway) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *NatGateway) klog.V(2).Infof("Creating Nat Gateway") request := &ec2.CreateNatGatewayInput{ - TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeNatgateway, e.Tags), + TagSpecifications: awsup.EC2TagSpecification(ec2types.ResourceTypeNatgateway, e.Tags), } request.AllocationId = e.ElasticIP.ID request.SubnetId = e.Subnet.ID - response, err := t.Cloud.EC2().CreateNatGateway(request) + response, err := t.Cloud.EC2().CreateNatGateway(ctx, request) if err != nil { return fmt.Errorf("Error creating Nat Gateway: %v", err) } diff --git a/upup/pkg/fi/cloudup/awstasks/route.go b/upup/pkg/fi/cloudup/awstasks/route.go index 8a23480c3be75..4675b5768acc3 100644 --- a/upup/pkg/fi/cloudup/awstasks/route.go +++ b/upup/pkg/fi/cloudup/awstasks/route.go @@ -17,10 +17,12 @@ limitations under the License. package awstasks import ( + "context" "fmt" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" @@ -48,6 +50,7 @@ type Route struct { } func (e *Route) Find(c *fi.CloudupContext) (*Route, error) { + ctx := c.Context() cloud := c.T.Cloud.(awsup.AWSCloud) if e.RouteTable == nil || (e.CIDR == nil && e.IPv6CIDR == nil) { @@ -60,10 +63,10 @@ func (e *Route) Find(c *fi.CloudupContext) (*Route, error) { } request := &ec2.DescribeRouteTablesInput{ - RouteTableIds: []*string{e.RouteTable.ID}, + RouteTableIds: []string{fi.ValueOf(e.RouteTable.ID)}, } - response, err := cloud.EC2().DescribeRouteTables(request) + response, err := cloud.EC2().DescribeRouteTables(ctx, request) if err != nil { return nil, fmt.Errorf("error listing RouteTables: %v", err) } @@ -104,7 +107,7 @@ func (e *Route) Find(c *fi.CloudupContext) (*Route, error) { actual.VPCPeeringConnectionID = r.VpcPeeringConnectionId } - if aws.ToString(r.State) == "blackhole" { + if r.State == ec2types.RouteStateBlackhole { klog.V(2).Infof("found route is a blackhole route") // These should be nil anyway, but just in case... actual.Instance = nil @@ -184,6 +187,7 @@ func (s *Route) CheckChanges(a, e, changes *Route) error { } func (_ *Route) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Route) error { + ctx := context.TODO() if a == nil { request := &ec2.CreateRouteInput{} request.RouteTableId = checkNotNil(e.RouteTable.ID) @@ -216,7 +220,7 @@ func (_ *Route) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Route) error { klog.V(2).Infof("Creating Route with RouteTable:%q CIDR:%q IPv6CIDR:%q", aws.ToString(e.RouteTable.ID), aws.ToString(e.CIDR), aws.ToString(e.IPv6CIDR)) - response, err := t.Cloud.EC2().CreateRoute(request) + response, err := t.Cloud.EC2().CreateRoute(ctx, request) if err != nil { code := awsup.AWSErrorCode(err) message := awsup.AWSErrorMessage(err) @@ -259,7 +263,7 @@ func (_ *Route) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Route) error { klog.V(2).Infof("Updating Route with RouteTable:%q CIDR:%q", *e.RouteTable.ID, *e.CIDR) - if _, err := t.Cloud.EC2().ReplaceRoute(request); err != nil { + if _, err := t.Cloud.EC2().ReplaceRoute(ctx, request); err != nil { code := awsup.AWSErrorCode(err) message := awsup.AWSErrorMessage(err) if code == "InvalidNatGatewayID.NotFound" { diff --git a/upup/pkg/fi/cloudup/awstasks/routetable.go b/upup/pkg/fi/cloudup/awstasks/routetable.go index 3962ff10a6550..ec598691265a9 100644 --- a/upup/pkg/fi/cloudup/awstasks/routetable.go +++ b/upup/pkg/fi/cloudup/awstasks/routetable.go @@ -17,10 +17,12 @@ limitations under the License. package awstasks import ( + "context" "fmt" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" @@ -49,13 +51,14 @@ func (e *RouteTable) CompareWithID() *string { } func (e *RouteTable) Find(c *fi.CloudupContext) (*RouteTable, error) { + ctx := c.Context() cloud := c.T.Cloud.(awsup.AWSCloud) - var rt *ec2.RouteTable + var rt *ec2types.RouteTable var err error if e.ID != nil { - rt, err = findRouteTableByID(cloud, *e.ID) + rt, err = findRouteTableByID(ctx, cloud, *e.ID) if err != nil { return nil, err } @@ -63,7 +66,7 @@ func (e *RouteTable) Find(c *fi.CloudupContext) (*RouteTable, error) { // Try finding by name if rt == nil && e.Tags["Name"] != "" { - rt, err = findRouteTableByFilters(cloud, cloud.BuildFilters(e.Name)) + rt, err = findRouteTableByFilters(ctx, cloud, cloud.BuildFilters(e.Name)) if err != nil { return nil, err } @@ -71,17 +74,17 @@ func (e *RouteTable) Find(c *fi.CloudupContext) (*RouteTable, error) { // Try finding by shared cluster tag, along with role (so it isn't ambiguous) if rt == nil && e.Tags[awsup.TagNameKopsRole] != "" { - var filters []*ec2.Filter - filters = append(filters, &ec2.Filter{ + var filters []ec2types.Filter + filters = append(filters, ec2types.Filter{ Name: aws.String("tag-key"), - Values: aws.StringSlice([]string{"kubernetes.io/cluster/" + c.T.Cluster.Name}), + Values: []string{"kubernetes.io/cluster/" + c.T.Cluster.Name}, }) - filters = append(filters, &ec2.Filter{ + filters = append(filters, ec2types.Filter{ Name: aws.String("tag:" + awsup.TagNameKopsRole), - Values: aws.StringSlice([]string{e.Tags[awsup.TagNameKopsRole]}), + Values: []string{e.Tags[awsup.TagNameKopsRole]}, }) - rt, err = findRouteTableByFilters(cloud, filters) + rt, err = findRouteTableByFilters(ctx, cloud, filters) if err != nil { return nil, err } @@ -107,11 +110,11 @@ func (e *RouteTable) Find(c *fi.CloudupContext) (*RouteTable, error) { return actual, nil } -func findRouteTableByID(cloud awsup.AWSCloud, id string) (*ec2.RouteTable, error) { +func findRouteTableByID(ctx context.Context, cloud awsup.AWSCloud, id string) (*ec2types.RouteTable, error) { request := &ec2.DescribeRouteTablesInput{} - request.RouteTableIds = []*string{&id} + request.RouteTableIds = []string{id} - response, err := cloud.EC2().DescribeRouteTables(request) + response, err := cloud.EC2().DescribeRouteTables(ctx, request) if err != nil { return nil, fmt.Errorf("error listing RouteTables: %v", err) } @@ -124,14 +127,14 @@ func findRouteTableByID(cloud awsup.AWSCloud, id string) (*ec2.RouteTable, error } rt := response.RouteTables[0] - return rt, nil + return &rt, nil } -func findRouteTableByFilters(cloud awsup.AWSCloud, filters []*ec2.Filter) (*ec2.RouteTable, error) { +func findRouteTableByFilters(ctx context.Context, cloud awsup.AWSCloud, filters []ec2types.Filter) (*ec2types.RouteTable, error) { request := &ec2.DescribeRouteTablesInput{} request.Filters = filters - response, err := cloud.EC2().DescribeRouteTables(request) + response, err := cloud.EC2().DescribeRouteTables(ctx, request) if err != nil { return nil, fmt.Errorf("error listing RouteTables: %v", err) } @@ -143,7 +146,7 @@ func findRouteTableByFilters(cloud awsup.AWSCloud, filters []*ec2.Filter) (*ec2. return nil, fmt.Errorf("found multiple RouteTables matching tags") } rt := response.RouteTables[0] - return rt, nil + return &rt, nil } func (e *RouteTable) Run(c *fi.CloudupContext) error { @@ -165,6 +168,7 @@ func (s *RouteTable) CheckChanges(a, e, changes *RouteTable) error { } func (_ *RouteTable) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *RouteTable) error { + ctx := context.TODO() if a == nil { vpcID := e.VPC.ID if vpcID == nil { @@ -175,10 +179,10 @@ func (_ *RouteTable) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *RouteTable) request := &ec2.CreateRouteTableInput{ VpcId: vpcID, - TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeRouteTable, e.Tags), + TagSpecifications: awsup.EC2TagSpecification(ec2types.ResourceTypeRouteTable, e.Tags), } - response, err := t.Cloud.EC2().CreateRouteTable(request) + response, err := t.Cloud.EC2().CreateRouteTable(ctx, request) if err != nil { return fmt.Errorf("error creating RouteTable: %v", err) } diff --git a/upup/pkg/fi/cloudup/awstasks/routetableassociation.go b/upup/pkg/fi/cloudup/awstasks/routetableassociation.go index 861d8fdc42c14..43d5e9417a256 100644 --- a/upup/pkg/fi/cloudup/awstasks/routetableassociation.go +++ b/upup/pkg/fi/cloudup/awstasks/routetableassociation.go @@ -17,10 +17,12 @@ limitations under the License. package awstasks import ( + "context" "fmt" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" @@ -43,6 +45,7 @@ func (s *RouteTableAssociation) CompareWithID() *string { } func (e *RouteTableAssociation) Find(c *fi.CloudupContext) (*RouteTableAssociation, error) { + ctx := c.Context() cloud := c.T.Cloud.(awsup.AWSCloud) routeTableID := e.RouteTable.ID @@ -53,10 +56,10 @@ func (e *RouteTableAssociation) Find(c *fi.CloudupContext) (*RouteTableAssociati } request := &ec2.DescribeRouteTablesInput{ - RouteTableIds: []*string{routeTableID}, + RouteTableIds: []string{fi.ValueOf(routeTableID)}, } - response, err := cloud.EC2().DescribeRouteTables(request) + response, err := cloud.EC2().DescribeRouteTables(ctx, request) if err != nil { return nil, fmt.Errorf("error listing RouteTables: %v", err) } @@ -114,7 +117,8 @@ func (s *RouteTableAssociation) CheckChanges(a, e, changes *RouteTableAssociatio return nil } -func findExistingRouteTableForSubnet(cloud awsup.AWSCloud, subnet *Subnet) (*ec2.RouteTable, error) { +func findExistingRouteTableForSubnet(cloud awsup.AWSCloud, subnet *Subnet) (*ec2types.RouteTable, error) { + ctx := context.TODO() if subnet == nil { return nil, fmt.Errorf("subnet not set") } @@ -125,9 +129,9 @@ func findExistingRouteTableForSubnet(cloud awsup.AWSCloud, subnet *Subnet) (*ec2 subnetID := fi.ValueOf(subnet.ID) request := &ec2.DescribeRouteTablesInput{ - Filters: []*ec2.Filter{awsup.NewEC2Filter("association.subnet-id", subnetID)}, + Filters: []ec2types.Filter{awsup.NewEC2Filter("association.subnet-id", subnetID)}, } - response, err := cloud.EC2().DescribeRouteTables(request) + response, err := cloud.EC2().DescribeRouteTables(ctx, request) if err != nil { return nil, fmt.Errorf("error listing RouteTables for subnet %q: %v", subnetID, err) } @@ -139,10 +143,11 @@ func findExistingRouteTableForSubnet(cloud awsup.AWSCloud, subnet *Subnet) (*ec2 return nil, fmt.Errorf("found multiple RouteTables attached to subnet") } rt := response.RouteTables[0] - return rt, nil + return &rt, nil } func (_ *RouteTableAssociation) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *RouteTableAssociation) error { + ctx := context.TODO() if a == nil { // TODO: We might do better just to make the subnet the primary key here @@ -162,7 +167,7 @@ func (_ *RouteTableAssociation) RenderAWS(t *awsup.AWSAPITarget, a, e, changes * AssociationId: a.RouteTableAssociationId, } - _, err := t.Cloud.EC2().DisassociateRouteTable(request) + _, err := t.Cloud.EC2().DisassociateRouteTable(ctx, request) if err != nil { return fmt.Errorf("error disassociating existing RouteTable from subnet: %v", err) } @@ -175,7 +180,7 @@ func (_ *RouteTableAssociation) RenderAWS(t *awsup.AWSAPITarget, a, e, changes * RouteTableId: e.RouteTable.ID, } - response, err := t.Cloud.EC2().AssociateRouteTable(request) + response, err := t.Cloud.EC2().AssociateRouteTable(ctx, request) if err != nil { return fmt.Errorf("error creating RouteTableAssociation: %v", err) } diff --git a/upup/pkg/fi/cloudup/awstasks/securitygroup.go b/upup/pkg/fi/cloudup/awstasks/securitygroup.go index 872ca1c1745da..08e9852c567af 100644 --- a/upup/pkg/fi/cloudup/awstasks/securitygroup.go +++ b/upup/pkg/fi/cloudup/awstasks/securitygroup.go @@ -17,12 +17,14 @@ limitations under the License. package awstasks import ( + "context" "fmt" "strconv" "strings" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" @@ -96,13 +98,14 @@ func (e *SecurityGroup) Find(c *fi.CloudupContext) (*SecurityGroup, error) { return actual, nil } -func (e *SecurityGroup) findEc2(c *fi.CloudupContext) (*ec2.SecurityGroup, error) { +func (e *SecurityGroup) findEc2(c *fi.CloudupContext) (*ec2types.SecurityGroup, error) { + ctx := c.Context() cloud := c.T.Cloud.(awsup.AWSCloud) request := &ec2.DescribeSecurityGroupsInput{} if fi.ValueOf(e.ID) != "" { // Find by ID. - request.GroupIds = []*string{e.ID} + request.GroupIds = []string{fi.ValueOf(e.ID)} } else if fi.ValueOf(e.Name) != "" && e.VPC != nil && e.VPC.ID != nil { // Find by filters (name and VPC ID). filters := cloud.BuildFilters(e.Name) @@ -115,7 +118,7 @@ func (e *SecurityGroup) findEc2(c *fi.CloudupContext) (*ec2.SecurityGroup, error return nil, nil } - response, err := cloud.EC2().DescribeSecurityGroups(request) + response, err := cloud.EC2().DescribeSecurityGroups(ctx, request) if err != nil { return nil, fmt.Errorf("error listing SecurityGroups: %v", err) } @@ -127,7 +130,7 @@ func (e *SecurityGroup) findEc2(c *fi.CloudupContext) (*ec2.SecurityGroup, error return nil, fmt.Errorf("found multiple SecurityGroups matching tags") } sg := response.SecurityGroups[0] - return sg, nil + return &sg, nil } func (e *SecurityGroup) Run(c *fi.CloudupContext) error { @@ -157,6 +160,7 @@ func (_ *SecurityGroup) CheckChanges(a, e, changes *SecurityGroup) error { } func (_ *SecurityGroup) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *SecurityGroup) error { + ctx := context.TODO() shared := fi.ValueOf(e.Shared) if shared { // Do we want to do any verification of the security group? @@ -170,10 +174,10 @@ func (_ *SecurityGroup) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Security VpcId: e.VPC.ID, GroupName: e.Name, Description: e.Description, - TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeSecurityGroup, e.Tags), + TagSpecifications: awsup.EC2TagSpecification(ec2types.ResourceTypeSecurityGroup, e.Tags), } - response, err := t.Cloud.EC2().CreateSecurityGroup(request) + response, err := t.Cloud.EC2().CreateSecurityGroup(ctx, request) if err != nil { return fmt.Errorf("error creating SecurityGroup: %v", err) } @@ -225,18 +229,19 @@ func (e *SecurityGroup) TerraformLink() *terraformWriter.Literal { // deleteSecurityGroupRule tracks a securitygrouprule that we're going to delete // It implements fi.CloudupDeletion type deleteSecurityGroupRule struct { - rule *ec2.SecurityGroupRule + rule *ec2types.SecurityGroupRule } -func buildDeleteSecurityGroupRule(rule *ec2.SecurityGroupRule) *deleteSecurityGroupRule { +func buildDeleteSecurityGroupRule(rule ec2types.SecurityGroupRule) *deleteSecurityGroupRule { d := &deleteSecurityGroupRule{} - d.rule = rule + d.rule = &rule return d } var _ fi.CloudupDeletion = &deleteSecurityGroupRule{} func (d *deleteSecurityGroupRule) Delete(t fi.CloudupTarget) error { + ctx := context.TODO() klog.V(2).Infof("deleting security group permission: %v", fi.DebugAsJsonString(d.rule)) awsTarget, ok := t.(*awsup.AWSAPITarget) @@ -247,22 +252,22 @@ func (d *deleteSecurityGroupRule) Delete(t fi.CloudupTarget) error { if aws.ToBool(d.rule.IsEgress) { request := &ec2.RevokeSecurityGroupEgressInput{ GroupId: d.rule.GroupId, - SecurityGroupRuleIds: []*string{d.rule.SecurityGroupRuleId}, + SecurityGroupRuleIds: []string{fi.ValueOf(d.rule.SecurityGroupRuleId)}, } klog.V(2).Infof("Calling EC2 RevokeSecurityGroupEgress") - _, err := awsTarget.Cloud.EC2().RevokeSecurityGroupEgress(request) + _, err := awsTarget.Cloud.EC2().RevokeSecurityGroupEgress(ctx, request) if err != nil { return fmt.Errorf("error revoking SecurityGroupEgress: %v", err) } } else { request := &ec2.RevokeSecurityGroupIngressInput{ GroupId: d.rule.GroupId, - SecurityGroupRuleIds: []*string{d.rule.SecurityGroupRuleId}, + SecurityGroupRuleIds: []string{fi.ValueOf(d.rule.SecurityGroupRuleId)}, } klog.V(2).Infof("Calling EC2 RevokeSecurityGroupIngress") - _, err := awsTarget.Cloud.EC2().RevokeSecurityGroupIngress(request) + _, err := awsTarget.Cloud.EC2().RevokeSecurityGroupIngress(ctx, request) if err != nil { return fmt.Errorf("error revoking SecurityGroupIngress: %v", err) } @@ -278,10 +283,10 @@ func (d *deleteSecurityGroupRule) TaskName() string { func (d *deleteSecurityGroupRule) Item() string { s := fi.ValueOf(d.rule.GroupId) + ":" p := d.rule - if aws.ToInt64(p.FromPort) != 0 { - s += fmt.Sprintf(" port=%d", aws.ToInt64(p.FromPort)) - if aws.ToInt64(p.ToPort) != aws.ToInt64(p.FromPort) { - s += fmt.Sprintf("-%d", aws.ToInt64(p.ToPort)) + if aws.ToInt32(p.FromPort) != 0 { + s += fmt.Sprintf(" port=%d", aws.ToInt32(p.FromPort)) + if aws.ToInt32(p.ToPort) != aws.ToInt32(p.FromPort) { + s += fmt.Sprintf("-%d", aws.ToInt32(p.ToPort)) } } if aws.ToString(p.IpProtocol) != "-1" { @@ -303,6 +308,7 @@ func (d *deleteSecurityGroupRule) DeferDeletion() bool { } func (e *SecurityGroup) FindDeletions(c *fi.CloudupContext) ([]fi.CloudupDeletion, error) { + ctx := c.Context() var removals []fi.CloudupDeletion if len(e.RemoveExtraRules) == 0 { @@ -329,12 +335,12 @@ func (e *SecurityGroup) FindDeletions(c *fi.CloudupContext) ([]fi.CloudupDeletio cloud := c.T.Cloud.(awsup.AWSCloud) request := &ec2.DescribeSecurityGroupRulesInput{ - Filters: []*ec2.Filter{ + Filters: []ec2types.Filter{ awsup.NewEC2Filter("group-id", *e.ID), }, } - response, err := cloud.EC2().DescribeSecurityGroupRules(request) + response, err := cloud.EC2().DescribeSecurityGroupRules(ctx, request) if err != nil { return nil, err } @@ -345,14 +351,14 @@ func (e *SecurityGroup) FindDeletions(c *fi.CloudupContext) ([]fi.CloudupDeletio // (in the model, we typically consider only rules on port 22 and 443) match := false for _, rule := range rules { - if rule.Matches(permission) { + if rule.Matches(&permission) { klog.V(2).Infof("permission matches rule %s: %v", rule, permission) match = true break } } if !match { - klog.V(4).Infof("Ignoring security group permission %q (did not match removal rules)", permission) + klog.V(4).Infof("Ignoring security group permission %+v (did not match removal rules)", permission) continue } found := false @@ -367,7 +373,7 @@ func (e *SecurityGroup) FindDeletions(c *fi.CloudupContext) ([]fi.CloudupDeletio return nil, nil } - if er.matches(permission) { + if er.matches(&permission) { found = true } } @@ -381,7 +387,7 @@ func (e *SecurityGroup) FindDeletions(c *fi.CloudupContext) ([]fi.CloudupDeletio // RemovalRule is a rule that filters the permissions we should remove type RemovalRule interface { - Matches(permission *ec2.SecurityGroupRule) bool + Matches(permission *ec2types.SecurityGroupRule) bool } // ParseRemovalRule parses our removal rule DSL into a RemovalRule @@ -431,12 +437,12 @@ func (r *PortRemovalRule) String() string { return fi.DebugAsJsonString(r) } -func (r *PortRemovalRule) Matches(permission *ec2.SecurityGroupRule) bool { +func (r *PortRemovalRule) Matches(permission *ec2types.SecurityGroupRule) bool { // Check if port matches - if permission.FromPort == nil || *permission.FromPort != int64(r.FromPort) { + if permission.FromPort == nil || *permission.FromPort != int32(r.FromPort) { return false } - if permission.ToPort == nil || *permission.ToPort != int64(r.ToPort) { + if permission.ToPort == nil || *permission.ToPort != int32(r.ToPort) { return false } return true diff --git a/upup/pkg/fi/cloudup/awstasks/securitygroup_test.go b/upup/pkg/fi/cloudup/awstasks/securitygroup_test.go index db6c420422f34..caf195360d457 100644 --- a/upup/pkg/fi/cloudup/awstasks/securitygroup_test.go +++ b/upup/pkg/fi/cloudup/awstasks/securitygroup_test.go @@ -22,7 +22,7 @@ import ( "testing" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/cloudmock/aws/mockec2" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" @@ -66,40 +66,40 @@ func testParsesAsPort(t *testing.T, rule string, fromPort int, toPort int) { func TestPortRemovalRule(t *testing.T) { r := &PortRemovalRule{FromPort: 22, ToPort: 23} - testMatches(t, r, &ec2.SecurityGroupRule{FromPort: aws.Int64(22), ToPort: aws.Int64(23)}) + testMatches(t, r, &ec2types.SecurityGroupRule{FromPort: aws.Int32(22), ToPort: aws.Int32(23)}) - testNotMatches(t, r, &ec2.SecurityGroupRule{FromPort: aws.Int64(0), ToPort: aws.Int64(0)}) - testNotMatches(t, r, &ec2.SecurityGroupRule{FromPort: aws.Int64(22), ToPort: aws.Int64(22)}) - testNotMatches(t, r, &ec2.SecurityGroupRule{FromPort: aws.Int64(23), ToPort: aws.Int64(23)}) - testNotMatches(t, r, &ec2.SecurityGroupRule{FromPort: aws.Int64(20), ToPort: aws.Int64(23)}) - testNotMatches(t, r, &ec2.SecurityGroupRule{FromPort: aws.Int64(22), ToPort: aws.Int64(24)}) - testNotMatches(t, r, &ec2.SecurityGroupRule{ToPort: aws.Int64(23)}) - testNotMatches(t, r, &ec2.SecurityGroupRule{FromPort: aws.Int64(22)}) - testNotMatches(t, r, &ec2.SecurityGroupRule{}) + testNotMatches(t, r, &ec2types.SecurityGroupRule{FromPort: aws.Int32(0), ToPort: aws.Int32(0)}) + testNotMatches(t, r, &ec2types.SecurityGroupRule{FromPort: aws.Int32(22), ToPort: aws.Int32(22)}) + testNotMatches(t, r, &ec2types.SecurityGroupRule{FromPort: aws.Int32(23), ToPort: aws.Int32(23)}) + testNotMatches(t, r, &ec2types.SecurityGroupRule{FromPort: aws.Int32(20), ToPort: aws.Int32(23)}) + testNotMatches(t, r, &ec2types.SecurityGroupRule{FromPort: aws.Int32(22), ToPort: aws.Int32(24)}) + testNotMatches(t, r, &ec2types.SecurityGroupRule{ToPort: aws.Int32(23)}) + testNotMatches(t, r, &ec2types.SecurityGroupRule{FromPort: aws.Int32(22)}) + testNotMatches(t, r, &ec2types.SecurityGroupRule{}) r = &PortRemovalRule{FromPort: -1, ToPort: -1} - testMatches(t, r, &ec2.SecurityGroupRule{FromPort: aws.Int64(-1), ToPort: aws.Int64(-1)}) + testMatches(t, r, &ec2types.SecurityGroupRule{FromPort: aws.Int32(-1), ToPort: aws.Int32(-1)}) } func TestPortRemovalRule_Zero(t *testing.T) { r := &PortRemovalRule{FromPort: 0, ToPort: 0} - testMatches(t, r, &ec2.SecurityGroupRule{FromPort: aws.Int64(0), ToPort: aws.Int64(0)}) + testMatches(t, r, &ec2types.SecurityGroupRule{FromPort: aws.Int32(0), ToPort: aws.Int32(0)}) - testNotMatches(t, r, &ec2.SecurityGroupRule{FromPort: aws.Int64(0), ToPort: aws.Int64(20)}) - testNotMatches(t, r, &ec2.SecurityGroupRule{ToPort: aws.Int64(0)}) - testNotMatches(t, r, &ec2.SecurityGroupRule{FromPort: aws.Int64(0)}) - testNotMatches(t, r, &ec2.SecurityGroupRule{}) + testNotMatches(t, r, &ec2types.SecurityGroupRule{FromPort: aws.Int32(0), ToPort: aws.Int32(20)}) + testNotMatches(t, r, &ec2types.SecurityGroupRule{ToPort: aws.Int32(0)}) + testNotMatches(t, r, &ec2types.SecurityGroupRule{FromPort: aws.Int32(0)}) + testNotMatches(t, r, &ec2types.SecurityGroupRule{}) } -func testMatches(t *testing.T, rule *PortRemovalRule, permission *ec2.SecurityGroupRule) { +func testMatches(t *testing.T, rule *PortRemovalRule, permission *ec2types.SecurityGroupRule) { if !rule.Matches(permission) { - t.Fatalf("rule %q failed to match permission %q", rule, permission) + t.Fatalf("rule %+v failed to match permission %+v", rule, permission) } } -func testNotMatches(t *testing.T, rule *PortRemovalRule, permission *ec2.SecurityGroupRule) { +func testNotMatches(t *testing.T, rule *PortRemovalRule, permission *ec2types.SecurityGroupRule) { if rule.Matches(permission) { - t.Fatalf("rule %q unexpectedly matched permission %q", rule, permission) + t.Fatalf("rule %+v unexpectedly matched permission %+v", rule, permission) } } @@ -147,11 +147,11 @@ func TestSecurityGroupCreate(t *testing.T) { t.Fatalf("Expected exactly one SecurityGroup; found %v", c.SecurityGroups) } - expected := &ec2.SecurityGroup{ + expected := &ec2types.SecurityGroup{ Description: s("Description"), GroupId: sg1.ID, VpcId: vpc1.ID, - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String("Name"), Value: aws.String("sg1"), diff --git a/upup/pkg/fi/cloudup/awstasks/securitygrouprule.go b/upup/pkg/fi/cloudup/awstasks/securitygrouprule.go index 7e79ae6410959..c1f189b0047e8 100644 --- a/upup/pkg/fi/cloudup/awstasks/securitygrouprule.go +++ b/upup/pkg/fi/cloudup/awstasks/securitygrouprule.go @@ -17,11 +17,13 @@ limitations under the License. package awstasks import ( + "context" "fmt" "strings" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi" @@ -44,9 +46,9 @@ type SecurityGroupRule struct { Protocol *string // FromPort is the lower-bound (inclusive) of the port-range - FromPort *int64 + FromPort *int32 // ToPort is the upper-bound (inclusive) of the port-range - ToPort *int64 + ToPort *int32 SourceGroup *SecurityGroup Egress *bool @@ -55,6 +57,7 @@ type SecurityGroupRule struct { } func (e *SecurityGroupRule) Find(c *fi.CloudupContext) (*SecurityGroupRule, error) { + ctx := c.Context() cloud := c.T.Cloud.(awsup.AWSCloud) if e.SecurityGroup == nil || e.SecurityGroup.ID == nil { @@ -67,12 +70,12 @@ func (e *SecurityGroupRule) Find(c *fi.CloudupContext) (*SecurityGroupRule, erro } request := &ec2.DescribeSecurityGroupRulesInput{ - Filters: []*ec2.Filter{ + Filters: []ec2types.Filter{ awsup.NewEC2Filter("group-id", *e.SecurityGroup.ID), }, } - response, err := cloud.EC2().DescribeSecurityGroupRules(request) + response, err := cloud.EC2().DescribeSecurityGroupRules(ctx, request) if err != nil { return nil, fmt.Errorf("error listing SecurityGroup: %v", err) } @@ -81,11 +84,11 @@ func (e *SecurityGroupRule) Find(c *fi.CloudupContext) (*SecurityGroupRule, erro return nil, nil } - var foundRule *ec2.SecurityGroupRule + var foundRule *ec2types.SecurityGroupRule for _, rule := range response.SecurityGroupRules { - if e.matches(rule) { - foundRule = rule + if e.matches(&rule) { + foundRule = &rule break } } @@ -108,10 +111,10 @@ func (e *SecurityGroupRule) Find(c *fi.CloudupContext) (*SecurityGroupRule, erro } if fi.ValueOf(actual.Protocol) != "icmpv6" { - if fi.ValueOf(actual.FromPort) == int64(-1) { + if fi.ValueOf(actual.FromPort) == int32(-1) { actual.FromPort = nil } - if fi.ValueOf(actual.ToPort) == int64(-1) { + if fi.ValueOf(actual.ToPort) == int32(-1) { actual.ToPort = nil } } @@ -149,20 +152,20 @@ func (e *SecurityGroupRule) SetCidrOrPrefix(cidr string) { } } -func (e *SecurityGroupRule) matches(rule *ec2.SecurityGroupRule) bool { - matchFromPort := int64(-1) +func (e *SecurityGroupRule) matches(rule *ec2types.SecurityGroupRule) bool { + matchFromPort := int32(-1) if e.FromPort != nil { matchFromPort = *e.FromPort } - if aws.ToInt64(rule.FromPort) != matchFromPort { + if aws.ToInt32(rule.FromPort) != matchFromPort { return false } - matchToPort := int64(-1) + matchToPort := int32(-1) if e.ToPort != nil { matchToPort = *e.ToPort } - if aws.ToInt64(rule.ToPort) != matchToPort { + if aws.ToInt32(rule.ToPort) != matchToPort { return false } @@ -260,6 +263,7 @@ func (e *SecurityGroupRule) Description() string { } func (_ *SecurityGroupRule) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *SecurityGroupRule) error { + ctx := context.TODO() name := fi.ValueOf(e.Name) if a == nil { @@ -268,35 +272,35 @@ func (_ *SecurityGroupRule) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Secu protocol = aws.String("-1") } - ipPermission := &ec2.IpPermission{ + ipPermission := ec2types.IpPermission{ IpProtocol: protocol, FromPort: e.FromPort, ToPort: e.ToPort, } if e.SourceGroup != nil { - ipPermission.UserIdGroupPairs = []*ec2.UserIdGroupPair{ + ipPermission.UserIdGroupPairs = []ec2types.UserIdGroupPair{ { GroupId: e.SourceGroup.ID, }, } } else if e.IPv6CIDR != nil { IPv6CIDR := e.IPv6CIDR - ipPermission.Ipv6Ranges = []*ec2.Ipv6Range{ + ipPermission.Ipv6Ranges = []ec2types.Ipv6Range{ {CidrIpv6: IPv6CIDR}, } } else if e.CIDR != nil { CIDR := e.CIDR - ipPermission.IpRanges = []*ec2.IpRange{ + ipPermission.IpRanges = []ec2types.IpRange{ {CidrIp: CIDR}, } } else if e.PrefixList != nil { PrefixList := e.PrefixList - ipPermission.PrefixListIds = []*ec2.PrefixListId{ + ipPermission.PrefixListIds = []ec2types.PrefixListId{ {PrefixListId: PrefixList}, } } else { - ipPermission.IpRanges = []*ec2.IpRange{ + ipPermission.IpRanges = []ec2types.IpRange{ {CidrIp: aws.String("0.0.0.0/0")}, } } @@ -307,11 +311,11 @@ func (_ *SecurityGroupRule) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Secu request := &ec2.AuthorizeSecurityGroupEgressInput{ GroupId: e.SecurityGroup.ID, } - request.IpPermissions = []*ec2.IpPermission{ipPermission} - request.TagSpecifications = awsup.EC2TagSpecification(ec2.ResourceTypeSecurityGroupRule, e.Tags) + request.IpPermissions = []ec2types.IpPermission{ipPermission} + request.TagSpecifications = awsup.EC2TagSpecification(ec2types.ResourceTypeSecurityGroupRule, e.Tags) klog.V(2).Infof("%s: Calling EC2 AuthorizeSecurityGroupEgress (%s)", name, description) - _, err := t.Cloud.EC2().AuthorizeSecurityGroupEgress(request) + _, err := t.Cloud.EC2().AuthorizeSecurityGroupEgress(ctx, request) if err != nil { return fmt.Errorf("error creating SecurityGroupEgress: %v", err) } @@ -319,11 +323,11 @@ func (_ *SecurityGroupRule) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Secu request := &ec2.AuthorizeSecurityGroupIngressInput{ GroupId: e.SecurityGroup.ID, } - request.IpPermissions = []*ec2.IpPermission{ipPermission} - request.TagSpecifications = awsup.EC2TagSpecification(ec2.ResourceTypeSecurityGroupRule, e.Tags) + request.IpPermissions = []ec2types.IpPermission{ipPermission} + request.TagSpecifications = awsup.EC2TagSpecification(ec2types.ResourceTypeSecurityGroupRule, e.Tags) klog.V(2).Infof("%s: Calling EC2 AuthorizeSecurityGroupIngress (%s)", name, description) - _, err := t.Cloud.EC2().AuthorizeSecurityGroupIngress(request) + _, err := t.Cloud.EC2().AuthorizeSecurityGroupIngress(ctx, request) if err != nil { return fmt.Errorf("error creating SecurityGroupIngress: %v", err) } @@ -344,8 +348,8 @@ type terraformSecurityGroupIngress struct { SecurityGroup *terraformWriter.Literal `cty:"security_group_id"` SourceGroup *terraformWriter.Literal `cty:"source_security_group_id"` - FromPort *int64 `cty:"from_port"` - ToPort *int64 `cty:"to_port"` + FromPort *int32 `cty:"from_port"` + ToPort *int32 `cty:"to_port"` Protocol *string `cty:"protocol"` CIDRBlocks []string `cty:"cidr_blocks"` @@ -367,17 +371,17 @@ func (_ *SecurityGroupRule) RenderTerraform(t *terraform.TerraformTarget, a, e, if e.Protocol == nil { tf.Protocol = fi.PtrTo("-1") - tf.FromPort = fi.PtrTo(int64(0)) - tf.ToPort = fi.PtrTo(int64(0)) + tf.FromPort = fi.PtrTo(int32(0)) + tf.ToPort = fi.PtrTo(int32(0)) } if tf.FromPort == nil { // FromPort is required by tf - tf.FromPort = fi.PtrTo(int64(0)) + tf.FromPort = fi.PtrTo(int32(0)) } if tf.ToPort == nil { // ToPort is required by tf - tf.ToPort = fi.PtrTo(int64(65535)) + tf.ToPort = fi.PtrTo(int32(65535)) } if e.SourceGroup != nil { diff --git a/upup/pkg/fi/cloudup/awstasks/subnet.go b/upup/pkg/fi/cloudup/awstasks/subnet.go index 9dc4209b37f51..f2ab179c54712 100644 --- a/upup/pkg/fi/cloudup/awstasks/subnet.go +++ b/upup/pkg/fi/cloudup/awstasks/subnet.go @@ -17,11 +17,13 @@ limitations under the License. package awstasks import ( + "context" "fmt" "strings" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi" @@ -94,12 +96,12 @@ func (e *Subnet) Find(c *fi.CloudupContext) (*Subnet, error) { } for _, association := range subnet.Ipv6CidrBlockAssociationSet { - if association == nil || association.Ipv6CidrBlockState == nil { + if association.Ipv6CidrBlockState == nil { continue } - state := aws.ToString(association.Ipv6CidrBlockState.State) - if state != ec2.SubnetCidrBlockStateCodeAssociated && state != ec2.SubnetCidrBlockStateCodeAssociating { + state := association.Ipv6CidrBlockState.State + if state != ec2types.SubnetCidrBlockStateCodeAssociated && state != ec2types.SubnetCidrBlockStateCodeAssociating { continue } @@ -109,7 +111,7 @@ func (e *Subnet) Find(c *fi.CloudupContext) (*Subnet, error) { actual.AssignIPv6AddressOnCreation = subnet.AssignIpv6AddressOnCreation - actual.ResourceBasedNaming = fi.PtrTo(aws.ToString(subnet.PrivateDnsNameOptionsOnLaunch.HostnameType) == ec2.HostnameTypeResourceName) + actual.ResourceBasedNaming = fi.PtrTo(subnet.PrivateDnsNameOptionsOnLaunch.HostnameType == ec2types.HostnameTypeResourceName) if *actual.ResourceBasedNaming { if fi.ValueOf(actual.CIDR) != "" && !aws.ToBool(subnet.PrivateDnsNameOptionsOnLaunch.EnableResourceNameDnsARecord) { actual.ResourceBasedNaming = nil @@ -142,17 +144,17 @@ func (e *Subnet) Find(c *fi.CloudupContext) (*Subnet, error) { return actual, nil } -func (e *Subnet) findEc2Subnet(c *fi.CloudupContext) (*ec2.Subnet, error) { +func (e *Subnet) findEc2Subnet(c *fi.CloudupContext) (*ec2types.Subnet, error) { cloud := c.T.Cloud.(awsup.AWSCloud) request := &ec2.DescribeSubnetsInput{} if e.ID != nil { - request.SubnetIds = []*string{e.ID} + request.SubnetIds = []string{fi.ValueOf(e.ID)} } else { request.Filters = cloud.BuildFilters(e.Name) } - response, err := cloud.EC2().DescribeSubnets(request) + response, err := cloud.EC2().DescribeSubnets(c.Context(), request) if err != nil { return nil, fmt.Errorf("error listing Subnets: %v", err) } @@ -165,7 +167,7 @@ func (e *Subnet) findEc2Subnet(c *fi.CloudupContext) (*ec2.Subnet, error) { } subnet := response.Subnets[0] - return subnet, nil + return &subnet, nil } func (e *Subnet) Run(c *fi.CloudupContext) error { @@ -232,6 +234,7 @@ func (_ *Subnet) ShouldCreate(a, e, changes *Subnet) (bool, error) { } func (_ *Subnet) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Subnet) error { + ctx := context.TODO() shared := fi.ValueOf(e.Shared) if shared { // Verify the subnet was found @@ -268,14 +271,14 @@ func (_ *Subnet) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Subnet) error { Ipv6CidrBlock: e.IPv6CIDR, AvailabilityZone: e.AvailabilityZone, VpcId: e.VPC.ID, - TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeSubnet, e.Tags), + TagSpecifications: awsup.EC2TagSpecification(ec2types.ResourceTypeSubnet, e.Tags), } if e.CIDR == nil { request.Ipv6Native = aws.Bool(true) } - response, err := t.Cloud.EC2().CreateSubnet(request) + response, err := t.Cloud.EC2().CreateSubnet(ctx, request) if err != nil { return fmt.Errorf("error creating subnet: %v", err) } @@ -288,7 +291,7 @@ func (_ *Subnet) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Subnet) error { SubnetId: e.ID, } - _, err := t.Cloud.EC2().AssociateSubnetCidrBlock(request) + _, err := t.Cloud.EC2().AssociateSubnetCidrBlock(ctx, request) if err != nil { return fmt.Errorf("error associating subnet cidr block: %v", err) } @@ -298,24 +301,24 @@ func (_ *Subnet) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Subnet) error { if a == nil || changes.AssignIPv6AddressOnCreation != nil { request := &ec2.ModifySubnetAttributeInput{ SubnetId: e.ID, - AssignIpv6AddressOnCreation: &ec2.AttributeBooleanValue{Value: e.AssignIPv6AddressOnCreation}, + AssignIpv6AddressOnCreation: &ec2types.AttributeBooleanValue{Value: e.AssignIPv6AddressOnCreation}, } - _, err := t.Cloud.EC2().ModifySubnetAttribute(request) + _, err := t.Cloud.EC2().ModifySubnetAttribute(ctx, request) if err != nil { return fmt.Errorf("error modifying AssignIPv6AddressOnCreation: %w", err) } } if changes.ResourceBasedNaming != nil { - hostnameType := ec2.HostnameTypeIpName + hostnameType := ec2types.HostnameTypeIpName if *changes.ResourceBasedNaming { - hostnameType = ec2.HostnameTypeResourceName + hostnameType = ec2types.HostnameTypeResourceName } request := &ec2.ModifySubnetAttributeInput{ SubnetId: e.ID, - PrivateDnsHostnameTypeOnLaunch: &hostnameType, + PrivateDnsHostnameTypeOnLaunch: hostnameType, } - _, err := t.Cloud.EC2().ModifySubnetAttribute(request) + _, err := t.Cloud.EC2().ModifySubnetAttribute(ctx, request) if err != nil { return fmt.Errorf("error modifying hostname type: %w", err) } @@ -323,18 +326,18 @@ func (_ *Subnet) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Subnet) error { if fi.ValueOf(e.CIDR) == "" { request = &ec2.ModifySubnetAttributeInput{ SubnetId: e.ID, - EnableDns64: &ec2.AttributeBooleanValue{Value: aws.Bool(true)}, + EnableDns64: &ec2types.AttributeBooleanValue{Value: aws.Bool(true)}, } - _, err = t.Cloud.EC2().ModifySubnetAttribute(request) + _, err = t.Cloud.EC2().ModifySubnetAttribute(ctx, request) if err != nil { return fmt.Errorf("error enabling DNS64: %w", err) } } else { request = &ec2.ModifySubnetAttributeInput{ SubnetId: e.ID, - EnableResourceNameDnsARecordOnLaunch: &ec2.AttributeBooleanValue{Value: changes.ResourceBasedNaming}, + EnableResourceNameDnsARecordOnLaunch: &ec2types.AttributeBooleanValue{Value: changes.ResourceBasedNaming}, } - _, err = t.Cloud.EC2().ModifySubnetAttribute(request) + _, err = t.Cloud.EC2().ModifySubnetAttribute(ctx, request) if err != nil { return fmt.Errorf("error modifying A records: %w", err) } @@ -343,9 +346,9 @@ func (_ *Subnet) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Subnet) error { if fi.ValueOf(e.IPv6CIDR) != "" { request = &ec2.ModifySubnetAttributeInput{ SubnetId: e.ID, - EnableResourceNameDnsAAAARecordOnLaunch: &ec2.AttributeBooleanValue{Value: changes.ResourceBasedNaming}, + EnableResourceNameDnsAAAARecordOnLaunch: &ec2types.AttributeBooleanValue{Value: changes.ResourceBasedNaming}, } - _, err = t.Cloud.EC2().ModifySubnetAttribute(request) + _, err = t.Cloud.EC2().ModifySubnetAttribute(ctx, request) if err != nil { return fmt.Errorf("error modifying AAAA records: %w", err) } @@ -440,11 +443,11 @@ func (_ *Subnet) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *Su tf.AssignIPv6AddressOnCreation = fi.PtrTo(true) } if e.ResourceBasedNaming != nil { - hostnameType := ec2.HostnameTypeIpName + hostnameType := ec2types.HostnameTypeIpName if *e.ResourceBasedNaming { - hostnameType = ec2.HostnameTypeResourceName + hostnameType = ec2types.HostnameTypeResourceName } - tf.PrivateDNSHostnameTypeOnLaunch = fi.PtrTo(hostnameType) + tf.PrivateDNSHostnameTypeOnLaunch = fi.PtrTo(string(hostnameType)) if fi.ValueOf(e.CIDR) != "" { tf.EnableResourceNameDNSARecordOnLaunch = e.ResourceBasedNaming } @@ -487,13 +490,13 @@ func (e *Subnet) FindDeletions(c *fi.CloudupContext) ([]fi.CloudupDeletion, erro var removals []fi.CloudupDeletion for _, association := range subnet.Ipv6CidrBlockAssociationSet { // Skip when without state - if association == nil || association.Ipv6CidrBlockState == nil { + if association.Ipv6CidrBlockState == nil { continue } // Skip when already disassociated - state := aws.ToString(association.Ipv6CidrBlockState.State) - if state == ec2.SubnetCidrBlockStateCodeDisassociated || state == ec2.SubnetCidrBlockStateCodeDisassociating { + state := association.Ipv6CidrBlockState.State + if state == ec2types.SubnetCidrBlockStateCodeDisassociated || state == ec2types.SubnetCidrBlockStateCodeDisassociating { continue } @@ -521,6 +524,7 @@ type deleteSubnetIPv6CIDRBlock struct { var _ fi.CloudupDeletion = &deleteSubnetIPv6CIDRBlock{} func (d *deleteSubnetIPv6CIDRBlock) Delete(t fi.CloudupTarget) error { + ctx := context.TODO() awsTarget, ok := t.(*awsup.AWSAPITarget) if !ok { return fmt.Errorf("unexpected target type for deletion: %T", t) @@ -529,7 +533,7 @@ func (d *deleteSubnetIPv6CIDRBlock) Delete(t fi.CloudupTarget) error { request := &ec2.DisassociateSubnetCidrBlockInput{ AssociationId: d.associationID, } - _, err := awsTarget.Cloud.EC2().DisassociateSubnetCidrBlock(request) + _, err := awsTarget.Cloud.EC2().DisassociateSubnetCidrBlock(ctx, request) return err } diff --git a/upup/pkg/fi/cloudup/awstasks/subnet_test.go b/upup/pkg/fi/cloudup/awstasks/subnet_test.go index a8ad41820fee9..ae71c2da6c34c 100644 --- a/upup/pkg/fi/cloudup/awstasks/subnet_test.go +++ b/upup/pkg/fi/cloudup/awstasks/subnet_test.go @@ -23,7 +23,8 @@ import ( "testing" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/cloudmock/aws/mockec2" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" @@ -108,13 +109,13 @@ func TestSubnetCreate(t *testing.T) { t.Fatalf("Expected exactly one Subnet; found %v", c.SubnetIds()) } - expected := &ec2.Subnet{ + expected := &ec2types.Subnet{ AssignIpv6AddressOnCreation: aws.Bool(false), CidrBlock: aws.String("172.20.1.0/24"), - PrivateDnsNameOptionsOnLaunch: &ec2.PrivateDnsNameOptionsOnLaunch{ + PrivateDnsNameOptionsOnLaunch: &ec2types.PrivateDnsNameOptionsOnLaunch{ EnableResourceNameDnsAAAARecord: aws.Bool(false), EnableResourceNameDnsARecord: aws.Bool(true), - HostnameType: aws.String(ec2.HostnameTypeResourceName), + HostnameType: ec2types.HostnameTypeResourceName, }, SubnetId: aws.String("subnet-1"), VpcId: aws.String("vpc-1"), @@ -189,22 +190,22 @@ func TestSubnetCreateIPv6(t *testing.T) { t.Fatalf("Expected exactly one Subnet; found %v", c.SubnetIds()) } - expected := &ec2.Subnet{ + expected := &ec2types.Subnet{ AssignIpv6AddressOnCreation: aws.Bool(true), CidrBlock: aws.String("172.20.1.0/24"), - Ipv6CidrBlockAssociationSet: []*ec2.SubnetIpv6CidrBlockAssociation{ + Ipv6CidrBlockAssociationSet: []ec2types.SubnetIpv6CidrBlockAssociation{ { AssociationId: aws.String("subnet-cidr-assoc-ipv6-subnet-1"), Ipv6CidrBlock: aws.String("2001:db8:0:1::/64"), - Ipv6CidrBlockState: &ec2.SubnetCidrBlockState{ - State: aws.String(ec2.SubnetCidrBlockStateCodeAssociated), + Ipv6CidrBlockState: &ec2types.SubnetCidrBlockState{ + State: ec2types.SubnetCidrBlockStateCodeAssociated, }, }, }, - PrivateDnsNameOptionsOnLaunch: &ec2.PrivateDnsNameOptionsOnLaunch{ + PrivateDnsNameOptionsOnLaunch: &ec2types.PrivateDnsNameOptionsOnLaunch{ EnableResourceNameDnsAAAARecord: aws.Bool(true), EnableResourceNameDnsARecord: aws.Bool(true), - HostnameType: aws.String(ec2.HostnameTypeResourceName), + HostnameType: ec2types.HostnameTypeResourceName, }, SubnetId: aws.String("subnet-1"), VpcId: aws.String("vpc-1"), @@ -278,22 +279,22 @@ func TestSubnetCreateIPv6NetNum(t *testing.T) { t.Fatalf("Expected exactly one Subnet; found %v", c.SubnetIds()) } - expected := &ec2.Subnet{ + expected := &ec2types.Subnet{ AssignIpv6AddressOnCreation: aws.Bool(true), CidrBlock: aws.String("172.20.1.0/24"), - Ipv6CidrBlockAssociationSet: []*ec2.SubnetIpv6CidrBlockAssociation{ + Ipv6CidrBlockAssociationSet: []ec2types.SubnetIpv6CidrBlockAssociation{ { AssociationId: aws.String("subnet-cidr-assoc-ipv6-subnet-1"), Ipv6CidrBlock: aws.String("2001:db8:0:1::/64"), - Ipv6CidrBlockState: &ec2.SubnetCidrBlockState{ - State: aws.String(ec2.SubnetCidrBlockStateCodeAssociated), + Ipv6CidrBlockState: &ec2types.SubnetCidrBlockState{ + State: ec2types.SubnetCidrBlockStateCodeAssociated, }, }, }, - PrivateDnsNameOptionsOnLaunch: &ec2.PrivateDnsNameOptionsOnLaunch{ + PrivateDnsNameOptionsOnLaunch: &ec2types.PrivateDnsNameOptionsOnLaunch{ EnableResourceNameDnsAAAARecord: aws.Bool(false), EnableResourceNameDnsARecord: aws.Bool(false), - HostnameType: aws.String(ec2.HostnameTypeIpName), + HostnameType: ec2types.HostnameTypeIpName, }, SubnetId: aws.String("subnet-1"), VpcId: aws.String("vpc-1"), @@ -324,12 +325,12 @@ func TestSharedSubnetCreateDoesNotCreateNew(t *testing.T) { cloud.MockEC2 = c // Pre-create the vpc / subnet - vpc, err := c.CreateVpc(&ec2.CreateVpcInput{ + vpc, err := c.CreateVpc(ctx, &ec2.CreateVpcInput{ CidrBlock: aws.String("172.20.0.0/16"), - TagSpecifications: []*ec2.TagSpecification{ + TagSpecifications: []ec2types.TagSpecification{ { - ResourceType: aws.String(ec2.ResourceTypeVpc), - Tags: []*ec2.Tag{ + ResourceType: ec2types.ResourceTypeVpc, + Tags: []ec2types.Tag{ { Key: aws.String("Name"), Value: aws.String("ExistingVPC"), @@ -342,13 +343,13 @@ func TestSharedSubnetCreateDoesNotCreateNew(t *testing.T) { t.Fatalf("error creating test VPC: %v", err) } - subnet, err := c.CreateSubnet(&ec2.CreateSubnetInput{ + subnet, err := c.CreateSubnet(ctx, &ec2.CreateSubnetInput{ VpcId: vpc.Vpc.VpcId, CidrBlock: aws.String("172.20.1.0/24"), - TagSpecifications: []*ec2.TagSpecification{ + TagSpecifications: []ec2types.TagSpecification{ { - ResourceType: aws.String(ec2.ResourceTypeSubnet), - Tags: []*ec2.Tag{ + ResourceType: ec2types.ResourceTypeSubnet, + Tags: []ec2types.Tag{ { Key: aws.String("Name"), Value: aws.String("ExistingSubnet"), @@ -405,13 +406,13 @@ func TestSharedSubnetCreateDoesNotCreateNew(t *testing.T) { if actual == nil { t.Fatalf("Subnet created but then not found") } - expected := &ec2.Subnet{ + expected := &ec2types.Subnet{ AssignIpv6AddressOnCreation: aws.Bool(false), CidrBlock: aws.String("172.20.1.0/24"), - PrivateDnsNameOptionsOnLaunch: &ec2.PrivateDnsNameOptionsOnLaunch{ + PrivateDnsNameOptionsOnLaunch: &ec2types.PrivateDnsNameOptionsOnLaunch{ EnableResourceNameDnsAAAARecord: aws.Bool(false), EnableResourceNameDnsARecord: aws.Bool(false), - HostnameType: aws.String(ec2.HostnameTypeIpName), + HostnameType: ec2types.HostnameTypeIpName, }, SubnetId: aws.String("subnet-1"), VpcId: aws.String("vpc-1"), diff --git a/upup/pkg/fi/cloudup/awstasks/vpc.go b/upup/pkg/fi/cloudup/awstasks/vpc.go index ff0cdd5b614ca..5427ee08f2f57 100644 --- a/upup/pkg/fi/cloudup/awstasks/vpc.go +++ b/upup/pkg/fi/cloudup/awstasks/vpc.go @@ -17,10 +17,12 @@ limitations under the License. package awstasks import ( + "context" "fmt" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/klog/v2" "k8s.io/kops/pkg/featureflag" @@ -67,17 +69,18 @@ func (e *VPC) CompareWithID() *string { } func (e *VPC) Find(c *fi.CloudupContext) (*VPC, error) { + ctx := c.Context() cloud := c.T.Cloud.(awsup.AWSCloud) request := &ec2.DescribeVpcsInput{} if fi.ValueOf(e.ID) != "" { - request.VpcIds = []*string{e.ID} + request.VpcIds = []string{aws.ToString(e.ID)} } else { request.Filters = cloud.BuildFilters(e.Name) } - response, err := cloud.EC2().DescribeVpcs(request) + response, err := cloud.EC2().DescribeVpcs(ctx, request) if err != nil { return nil, fmt.Errorf("error listing VPCs: %v", err) } @@ -100,12 +103,12 @@ func (e *VPC) Find(c *fi.CloudupContext) (*VPC, error) { klog.V(4).Infof("found matching VPC %v", actual) for _, association := range vpc.Ipv6CidrBlockAssociationSet { - if association == nil || association.Ipv6CidrBlockState == nil { + if association.Ipv6CidrBlockState == nil { continue } - state := aws.ToString(association.Ipv6CidrBlockState.State) - if state != ec2.VpcCidrBlockStateCodeAssociated && state != ec2.VpcCidrBlockStateCodeAssociating { + state := association.Ipv6CidrBlockState.State + if state != ec2types.VpcCidrBlockStateCodeAssociated && state != ec2types.VpcCidrBlockStateCodeAssociating { continue } @@ -122,8 +125,8 @@ func (e *VPC) Find(c *fi.CloudupContext) (*VPC, error) { } if actual.ID != nil { - request := &ec2.DescribeVpcAttributeInput{VpcId: actual.ID, Attribute: aws.String(ec2.VpcAttributeNameEnableDnsSupport)} - response, err := cloud.EC2().DescribeVpcAttribute(request) + request := &ec2.DescribeVpcAttributeInput{VpcId: actual.ID, Attribute: ec2types.VpcAttributeNameEnableDnsSupport} + response, err := cloud.EC2().DescribeVpcAttribute(ctx, request) if err != nil { return nil, fmt.Errorf("error querying for dns support: %v", err) } @@ -131,8 +134,8 @@ func (e *VPC) Find(c *fi.CloudupContext) (*VPC, error) { } if actual.ID != nil { - request := &ec2.DescribeVpcAttributeInput{VpcId: actual.ID, Attribute: aws.String(ec2.VpcAttributeNameEnableDnsHostnames)} - response, err := cloud.EC2().DescribeVpcAttribute(request) + request := &ec2.DescribeVpcAttributeInput{VpcId: actual.ID, Attribute: ec2types.VpcAttributeNameEnableDnsHostnames} + response, err := cloud.EC2().DescribeVpcAttribute(ctx, request) if err != nil { return nil, fmt.Errorf("error querying for dns support: %v", err) } @@ -172,6 +175,7 @@ func (e *VPC) Run(c *fi.CloudupContext) error { } func (_ *VPC) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *VPC) error { + ctx := context.TODO() shared := fi.ValueOf(e.Shared) if shared { // Verify the VPC was found and matches our required settings @@ -194,10 +198,10 @@ func (_ *VPC) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *VPC) error { request := &ec2.CreateVpcInput{ CidrBlock: e.CIDR, - TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeVpc, e.Tags), + TagSpecifications: awsup.EC2TagSpecification(ec2types.ResourceTypeVpc, e.Tags), } - response, err := t.Cloud.EC2().CreateVpc(request) + response, err := t.Cloud.EC2().CreateVpc(ctx, request) if err != nil { return fmt.Errorf("error creating VPC: %v", err) } @@ -208,10 +212,10 @@ func (_ *VPC) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *VPC) error { if changes.EnableDNSSupport != nil { request := &ec2.ModifyVpcAttributeInput{ VpcId: e.ID, - EnableDnsSupport: &ec2.AttributeBooleanValue{Value: changes.EnableDNSSupport}, + EnableDnsSupport: &ec2types.AttributeBooleanValue{Value: changes.EnableDNSSupport}, } - _, err := t.Cloud.EC2().ModifyVpcAttribute(request) + _, err := t.Cloud.EC2().ModifyVpcAttribute(ctx, request) if err != nil { return fmt.Errorf("error modifying VPC attribute: %v", err) } @@ -220,10 +224,10 @@ func (_ *VPC) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *VPC) error { if changes.EnableDNSHostnames != nil { request := &ec2.ModifyVpcAttributeInput{ VpcId: e.ID, - EnableDnsHostnames: &ec2.AttributeBooleanValue{Value: changes.EnableDNSHostnames}, + EnableDnsHostnames: &ec2types.AttributeBooleanValue{Value: changes.EnableDNSHostnames}, } - _, err := t.Cloud.EC2().ModifyVpcAttribute(request) + _, err := t.Cloud.EC2().ModifyVpcAttribute(ctx, request) if err != nil { return fmt.Errorf("error modifying VPC attribute: %v", err) } @@ -239,10 +243,10 @@ func (e *VPC) FindDeletions(c *fi.CloudupContext) ([]fi.CloudupDeletion, error) var removals []fi.CloudupDeletion request := &ec2.DescribeVpcsInput{ - VpcIds: []*string{e.ID}, + VpcIds: []string{aws.ToString(e.ID)}, } cloud := c.T.Cloud.(awsup.AWSCloud) - response, err := cloud.EC2().DescribeVpcs(request) + response, err := cloud.EC2().DescribeVpcs(c.Context(), request) if err != nil { return nil, err } @@ -258,7 +262,7 @@ func (e *VPC) FindDeletions(c *fi.CloudupContext) ([]fi.CloudupDeletion, error) // We'll only delete CIDR associations that are not the primary association // and that have a state of "associated" if fi.ValueOf(association.CidrBlock) == fi.ValueOf(vpc.CidrBlock) || - association.CidrBlockState != nil && fi.ValueOf(association.CidrBlockState.State) != ec2.VpcCidrBlockStateCodeAssociated { + association.CidrBlockState != nil && association.CidrBlockState.State != ec2types.VpcCidrBlockStateCodeAssociated { continue } match := false @@ -373,6 +377,7 @@ type deleteVPCCIDRBlock struct { var _ fi.CloudupDeletion = &deleteVPCCIDRBlock{} func (d *deleteVPCCIDRBlock) Delete(t fi.CloudupTarget) error { + ctx := context.TODO() awsTarget, ok := t.(*awsup.AWSAPITarget) if !ok { return fmt.Errorf("unexpected target type for deletion: %T", t) @@ -380,7 +385,7 @@ func (d *deleteVPCCIDRBlock) Delete(t fi.CloudupTarget) error { request := &ec2.DisassociateVpcCidrBlockInput{ AssociationId: d.associationID, } - _, err := awsTarget.Cloud.EC2().DisassociateVpcCidrBlock(request) + _, err := awsTarget.Cloud.EC2().DisassociateVpcCidrBlock(ctx, request) return err } diff --git a/upup/pkg/fi/cloudup/awstasks/vpc_dhcpoptions_association.go b/upup/pkg/fi/cloudup/awstasks/vpc_dhcpoptions_association.go index cbc84896f9050..e59da655066a3 100644 --- a/upup/pkg/fi/cloudup/awstasks/vpc_dhcpoptions_association.go +++ b/upup/pkg/fi/cloudup/awstasks/vpc_dhcpoptions_association.go @@ -17,9 +17,10 @@ limitations under the License. package awstasks import ( + "context" "fmt" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" @@ -85,6 +86,7 @@ func (s *VPCDHCPOptionsAssociation) CheckChanges(a, e, changes *VPCDHCPOptionsAs } func (_ *VPCDHCPOptionsAssociation) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *VPCDHCPOptionsAssociation) error { + ctx := context.TODO() if changes.DHCPOptions != nil { klog.V(2).Infof("calling EC2 AssociateDhcpOptions") request := &ec2.AssociateDhcpOptionsInput{ @@ -92,7 +94,7 @@ func (_ *VPCDHCPOptionsAssociation) RenderAWS(t *awsup.AWSAPITarget, a, e, chang DhcpOptionsId: e.DHCPOptions.ID, } - _, err := t.Cloud.EC2().AssociateDhcpOptions(request) + _, err := t.Cloud.EC2().AssociateDhcpOptions(ctx, request) if err != nil { return fmt.Errorf("error creating VPCDHCPOptionsAssociation: %v", err) } diff --git a/upup/pkg/fi/cloudup/awstasks/vpc_test.go b/upup/pkg/fi/cloudup/awstasks/vpc_test.go index 4251a9c34ac14..0c32c2fb59d79 100644 --- a/upup/pkg/fi/cloudup/awstasks/vpc_test.go +++ b/upup/pkg/fi/cloudup/awstasks/vpc_test.go @@ -22,7 +22,8 @@ import ( "testing" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/cloudmock/aws/mockec2" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" @@ -62,7 +63,7 @@ func TestVPCCreate(t *testing.T) { t.Fatalf("Expected exactly one Vpc; found %v", c.Vpcs) } - expected := &ec2.Vpc{ + expected := &ec2types.Vpc{ CidrBlock: s("172.21.0.0/16"), IsDefault: fi.PtrTo(false), VpcId: vpc1.ID, @@ -86,10 +87,10 @@ func TestVPCCreate(t *testing.T) { } } -func buildTags(tags map[string]string) []*ec2.Tag { - var t []*ec2.Tag +func buildTags(tags map[string]string) []ec2types.Tag { + var t []ec2types.Tag for k, v := range tags { - t = append(t, &ec2.Tag{ + t = append(t, ec2types.Tag{ Key: aws.String(k), Value: aws.String(v), }) @@ -123,14 +124,15 @@ func Test4758(t *testing.T) { } func TestSharedVPCAdditionalCIDR(t *testing.T) { + ctx := context.Background() cloud := awsup.BuildMockAWSCloud("us-east-1", "abc") c := &mockec2.MockEC2{} c.CreateVpcWithId(&ec2.CreateVpcInput{ CidrBlock: s("172.21.0.0/16"), - TagSpecifications: []*ec2.TagSpecification{ + TagSpecifications: []ec2types.TagSpecification{ { - ResourceType: s(ec2.ResourceTypeVpc), - Tags: []*ec2.Tag{ + ResourceType: ec2types.ResourceTypeVpc, + Tags: []ec2types.Tag{ { Key: s("Name"), Value: s("vpc-1"), @@ -139,7 +141,7 @@ func TestSharedVPCAdditionalCIDR(t *testing.T) { }, }, }, "vpc-1") - c.AssociateVpcCidrBlock(&ec2.AssociateVpcCidrBlockInput{ + c.AssociateVpcCidrBlock(ctx, &ec2.AssociateVpcCidrBlockInput{ VpcId: s("vpc-1"), CidrBlock: s("172.22.0.0/16"), }) @@ -174,19 +176,19 @@ func TestSharedVPCAdditionalCIDR(t *testing.T) { t.Fatalf("Expected exactly one Vpc; found %v", c.Vpcs) } - expected := &ec2.Vpc{ + expected := &ec2types.Vpc{ CidrBlock: s("172.21.0.0/16"), IsDefault: fi.PtrTo(false), VpcId: vpc1.ID, Tags: buildTags(map[string]string{ "Name": "vpc-1", }), - CidrBlockAssociationSet: []*ec2.VpcCidrBlockAssociation{ + CidrBlockAssociationSet: []ec2types.VpcCidrBlockAssociation{ { AssociationId: s("vpc-1-0"), CidrBlock: s("172.22.0.0/16"), - CidrBlockState: &ec2.VpcCidrBlockState{ - State: s(ec2.VpcCidrBlockStateCodeAssociated), + CidrBlockState: &ec2types.VpcCidrBlockState{ + State: ec2types.VpcCidrBlockStateCodeAssociated, }, }, }, diff --git a/upup/pkg/fi/cloudup/awstasks/vpcamazonipv6cidrblock.go b/upup/pkg/fi/cloudup/awstasks/vpcamazonipv6cidrblock.go index c2f5d4c8e06b6..9c61cdf951a1b 100644 --- a/upup/pkg/fi/cloudup/awstasks/vpcamazonipv6cidrblock.go +++ b/upup/pkg/fi/cloudup/awstasks/vpcamazonipv6cidrblock.go @@ -17,10 +17,12 @@ limitations under the License. package awstasks import ( + "context" "fmt" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" "k8s.io/kops/upup/pkg/fi/cloudup/terraform" @@ -84,6 +86,7 @@ func (s *VPCAmazonIPv6CIDRBlock) CheckChanges(a, e, changes *VPCAmazonIPv6CIDRBl } func (_ *VPCAmazonIPv6CIDRBlock) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *VPCAmazonIPv6CIDRBlock) error { + ctx := context.TODO() shared := aws.ToBool(e.Shared) if shared && a == nil { // VPC not owned by kOps, no changes will be applied @@ -97,7 +100,7 @@ func (_ *VPCAmazonIPv6CIDRBlock) RenderAWS(t *awsup.AWSAPITarget, a, e, changes } // Response doesn't contain the new CIDR block - _, err := t.Cloud.EC2().AssociateVpcCidrBlock(request) + _, err := t.Cloud.EC2().AssociateVpcCidrBlock(ctx, request) if err != nil { return fmt.Errorf("error associating Amazon IPv6 provided CIDR block to VPC: %v", err) } @@ -119,12 +122,12 @@ func findVPCIPv6CIDR(cloud awsup.AWSCloud, vpcID *string) (*string, error) { var byoIPv6CidrBlock *string for _, association := range vpc.Ipv6CidrBlockAssociationSet { - if association == nil || association.Ipv6CidrBlockState == nil { + if association.Ipv6CidrBlockState == nil { continue } // Ipv6CidrBlock is available only when state is "associated" - if aws.ToString(association.Ipv6CidrBlockState.State) != ec2.VpcCidrBlockStateCodeAssociated { + if association.Ipv6CidrBlockState.State != ec2types.VpcCidrBlockStateCodeAssociated { continue } diff --git a/upup/pkg/fi/cloudup/awstasks/vpccidrblock.go b/upup/pkg/fi/cloudup/awstasks/vpccidrblock.go index 77c3c679c46f0..e2d02814e408b 100644 --- a/upup/pkg/fi/cloudup/awstasks/vpccidrblock.go +++ b/upup/pkg/fi/cloudup/awstasks/vpccidrblock.go @@ -17,10 +17,12 @@ limitations under the License. package awstasks import ( + "context" "fmt" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" "k8s.io/kops/upup/pkg/fi/cloudup/terraform" @@ -57,12 +59,12 @@ func (e *VPCCIDRBlock) Find(c *fi.CloudupContext) (*VPCCIDRBlock, error) { found := false if e.CIDRBlock != nil { for _, cba := range vpc.CidrBlockAssociationSet { - if cba == nil || cba.CidrBlockState == nil { + if cba.CidrBlockState == nil { continue } - state := aws.ToString(cba.CidrBlockState.State) - if state != ec2.VpcCidrBlockStateCodeAssociated && state != ec2.VpcCidrBlockStateCodeAssociating { + state := cba.CidrBlockState.State + if state != ec2types.VpcCidrBlockStateCodeAssociated && state != ec2types.VpcCidrBlockStateCodeAssociating { continue } @@ -116,6 +118,7 @@ func (s *VPCCIDRBlock) CheckChanges(a, e, changes *VPCCIDRBlock) error { } func (_ *VPCCIDRBlock) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *VPCCIDRBlock) error { + ctx := context.TODO() shared := aws.ToBool(e.Shared) if shared && a == nil { // VPC not owned by kOps, no changes will be applied @@ -129,7 +132,7 @@ func (_ *VPCCIDRBlock) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *VPCCIDRBl CidrBlock: e.CIDRBlock, } - _, err := t.Cloud.EC2().AssociateVpcCidrBlock(request) + _, err := t.Cloud.EC2().AssociateVpcCidrBlock(ctx, request) if err != nil { return fmt.Errorf("error associating AdditionalCIDR to VPC: %v", err) } diff --git a/upup/pkg/fi/cloudup/awsup/aws_cloud.go b/upup/pkg/fi/cloudup/awsup/aws_cloud.go index b22e1cbe9284a..ef6d32e2eff8f 100644 --- a/upup/pkg/fi/cloudup/awsup/aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/aws_cloud.go @@ -37,6 +37,8 @@ import ( stscredsv2 "github.com/aws/aws-sdk-go-v2/credentials/stscreds" "github.com/aws/aws-sdk-go-v2/service/autoscaling" autoscalingtypes "github.com/aws/aws-sdk-go-v2/service/autoscaling/types" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" elb "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing" elbtypes "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" elbv2 "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2" @@ -49,13 +51,12 @@ import ( "github.com/aws/aws-sdk-go/aws/credentials/stscreds" "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + ec2v1 "github.com/aws/aws-sdk-go/service/ec2" "k8s.io/klog/v2" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/sets" - k8s_aws "k8s.io/cloud-provider-aws/pkg/providers/v1" "k8s.io/kops/dnsprovider/pkg/dnsprovider" dnsproviderroute53 "k8s.io/kops/dnsprovider/pkg/dnsprovider/providers/aws/route53" @@ -165,13 +166,13 @@ type AWSCloud interface { DescribeELBTags(loadBalancerNames []string) (map[string][]elbtypes.Tag, error) // TODO: Remove, replace with awsup.ListELBV2LoadBalancers DescribeELBV2Tags(loadBalancerNames []string) (map[string][]elbv2types.Tag, error) - FindELBV2NetworkInterfacesByName(vpcID string, loadBalancerName string) ([]*ec2.NetworkInterface, error) + FindELBV2NetworkInterfacesByName(vpcID string, loadBalancerName string) ([]ec2types.NetworkInterface, error) // DescribeInstance is a helper that queries for the specified instance by id DescribeInstance(instanceID string) (*ec2.Instance, error) // DescribeVPC is a helper that queries for the specified vpc by id - DescribeVPC(vpcID string) (*ec2.Vpc, error) + DescribeVPC(vpcID string) (*ec2types.Vpc, error) DescribeAvailabilityZones() ([]*ec2.AvailabilityZone, error) // ResolveImage finds an AMI image based on the given name. @@ -1849,28 +1850,29 @@ func FindLatestELBV2ByNameTag(loadBalancers []*LoadBalancerInfo, findNameTag str return latest } -func (c *awsCloudImplementation) FindELBV2NetworkInterfacesByName(vpcID string, loadBalancerName string) ([]*ec2.NetworkInterface, error) { +func (c *awsCloudImplementation) FindELBV2NetworkInterfacesByName(vpcID string, loadBalancerName string) ([]ec2types.NetworkInterface, error) { return findELBV2NetworkInterfaces(c, vpcID, loadBalancerName) } -func findELBV2NetworkInterfaces(c AWSCloud, vpcID, lbName string) ([]*ec2.NetworkInterface, error) { +func findELBV2NetworkInterfaces(c AWSCloud, vpcID, lbName string) ([]ec2types.NetworkInterface, error) { klog.V(2).Infof("Listing all NLB network interfaces") + ctx := context.TODO() request := &ec2.DescribeNetworkInterfacesInput{ - Filters: []*ec2.Filter{ + Filters: []ec2types.Filter{ NewEC2Filter("vpc-id", vpcID), NewEC2Filter("interface-type", "network_load_balancer"), }, } - response, err := c.EC2().DescribeNetworkInterfaces(request) + response, err := c.EC2().DescribeNetworkInterfaces(ctx, request) if err != nil { return nil, fmt.Errorf("error describing network interfaces: %w", err) } - var found []*ec2.NetworkInterface + var found []ec2types.NetworkInterface for _, ni := range response.NetworkInterfaces { - if strings.HasPrefix(aws.StringValue(ni.Description), "ELB net/"+lbName+"/") { + if strings.HasPrefix(aws.ToString(ni.Description), "ELB net/"+lbName+"/") { found = append(found, ni) } } @@ -1958,17 +1960,18 @@ func (c *awsCloudImplementation) DescribeInstance(instanceID string) (*ec2.Insta } // DescribeVPC is a helper that queries for the specified vpc by id -func (c *awsCloudImplementation) DescribeVPC(vpcID string) (*ec2.Vpc, error) { +func (c *awsCloudImplementation) DescribeVPC(vpcID string) (*ec2types.Vpc, error) { return describeVPC(c, vpcID) } -func describeVPC(c AWSCloud, vpcID string) (*ec2.Vpc, error) { +func describeVPC(c AWSCloud, vpcID string) (*ec2types.Vpc, error) { klog.V(2).Infof("Calling DescribeVPC for VPC %q", vpcID) + ctx := context.TODO() request := &ec2.DescribeVpcsInput{ - VpcIds: []*string{&vpcID}, + VpcIds: []string{vpcID}, } - response, err := c.EC2().DescribeVpcs(request) + response, err := c.EC2().DescribeVpcs(ctx, request) if err != nil { return nil, fmt.Errorf("error listing VPCs: %v", err) } @@ -1980,7 +1983,7 @@ func describeVPC(c AWSCloud, vpcID string) (*ec2.Vpc, error) { } vpc := response.Vpcs[0] - return vpc, nil + return &vpc, nil } // ResolveImage finds an AMI image based on the given name. @@ -2184,6 +2187,7 @@ func (c *awsCloudImplementation) FindVPCInfo(vpcID string) (*fi.VPCInfo, error) } func findVPCInfo(c AWSCloud, vpcID string) (*fi.VPCInfo, error) { + ctx := context.TODO() vpc, err := c.DescribeVPC(vpcID) if err != nil { return nil, err @@ -2193,26 +2197,26 @@ func findVPCInfo(c AWSCloud, vpcID string) (*fi.VPCInfo, error) { } vpcInfo := &fi.VPCInfo{ - CIDR: aws.StringValue(vpc.CidrBlock), + CIDR: aws.ToString(vpc.CidrBlock), } // Find subnets in the VPC { klog.V(2).Infof("Calling DescribeSubnets for subnets in VPC %q", vpcID) request := &ec2.DescribeSubnetsInput{ - Filters: []*ec2.Filter{NewEC2Filter("vpc-id", vpcID)}, + Filters: []ec2types.Filter{NewEC2Filter("vpc-id", vpcID)}, } - response, err := c.EC2().DescribeSubnets(request) + response, err := c.EC2().DescribeSubnets(ctx, request) if err != nil { return nil, fmt.Errorf("error listing subnets in VPC %q: %v", vpcID, err) } if response != nil { for _, subnet := range response.Subnets { subnetInfo := &fi.SubnetInfo{ - ID: aws.StringValue(subnet.SubnetId), - CIDR: aws.StringValue(subnet.CidrBlock), - Zone: aws.StringValue(subnet.AvailabilityZone), + ID: aws.ToString(subnet.SubnetId), + CIDR: aws.ToString(subnet.CidrBlock), + Zone: aws.ToString(subnet.AvailabilityZone), } vpcInfo.Subnets = append(vpcInfo.Subnets, subnetInfo) diff --git a/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go b/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go index 02f2ef7ac4b0d..e212cab063bec 100644 --- a/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go @@ -209,7 +209,7 @@ func (c *MockAWSCloud) DescribeELBV2Tags(loadBalancerArns []string) (map[string] return describeELBV2Tags(c, loadBalancerArns) } -func (c *MockAWSCloud) FindELBV2NetworkInterfacesByName(vpcID, loadBalancerName string) ([]*ec2.NetworkInterface, error) { +func (c *MockAWSCloud) FindELBV2NetworkInterfacesByName(vpcID, loadBalancerName string) ([]ec2types.NetworkInterface, error) { return nil, nil } @@ -217,7 +217,7 @@ func (c *MockAWSCloud) DescribeInstance(instanceID string) (*ec2.Instance, error return nil, fmt.Errorf("MockAWSCloud DescribeInstance not implemented") } -func (c *MockAWSCloud) DescribeVPC(vpcID string) (*ec2.Vpc, error) { +func (c *MockAWSCloud) DescribeVPC(vpcID string) (*ec2types.Vpc, error) { return describeVPC(c, vpcID) } diff --git a/upup/pkg/fi/cloudup/new_cluster.go b/upup/pkg/fi/cloudup/new_cluster.go index b7a6eed304184..788243dd42f44 100644 --- a/upup/pkg/fi/cloudup/new_cluster.go +++ b/upup/pkg/fi/cloudup/new_cluster.go @@ -535,14 +535,15 @@ func NewCluster(opt *NewClusterOptions, clientset simple.Clientset) (*NewCluster } func setupVPC(opt *NewClusterOptions, cluster *api.Cluster, cloud fi.Cloud) error { + ctx := context.TODO() cluster.Spec.Networking.NetworkID = opt.NetworkID switch cluster.Spec.GetCloudProvider() { case api.CloudProviderAWS: if cluster.Spec.Networking.NetworkID == "" && len(opt.SubnetIDs) > 0 { awsCloud := cloud.(awsup.AWSCloud) - res, err := awsCloud.EC2().DescribeSubnets(&ec2.DescribeSubnetsInput{ - SubnetIds: []*string{aws.String(opt.SubnetIDs[0])}, + res, err := awsCloud.EC2().DescribeSubnets(ctx, &ec2.DescribeSubnetsInput{ + SubnetIds: []string{opt.SubnetIDs[0]}, }) if err != nil { return fmt.Errorf("error describing subnet %s: %v", opt.SubnetIDs[0], err) diff --git a/upup/pkg/fi/nodeup/nodetasks/prefix.go b/upup/pkg/fi/nodeup/nodetasks/prefix.go index b735d0bf26db6..eb8986796e087 100644 --- a/upup/pkg/fi/nodeup/nodetasks/prefix.go +++ b/upup/pkg/fi/nodeup/nodetasks/prefix.go @@ -28,7 +28,7 @@ import ( awsconfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" "k8s.io/klog/v2" "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/upup/pkg/fi" @@ -96,14 +96,14 @@ func (_ *Prefix) RenderLocal(t *local.LocalTarget, a, e, changes *Prefix) error return err } - response, err := t.Cloud.(awsup.AWSCloud).EC2().AssignIpv6Addresses(&ec2.AssignIpv6AddressesInput{ - Ipv6PrefixCount: fi.PtrTo(int64(1)), + response, err := t.Cloud.(awsup.AWSCloud).EC2().AssignIpv6Addresses(ctx, &ec2.AssignIpv6AddressesInput{ + Ipv6PrefixCount: fi.PtrTo(int32(1)), NetworkInterfaceId: fi.PtrTo(interfaceId), }) if err != nil { return fmt.Errorf("failed to assign prefix: %w", err) } - klog.V(2).Infof("assigned prefix to primary network interface: %q", fi.ValueOf(response.AssignedIpv6Prefixes[0])) + klog.V(2).Infof("assigned prefix to primary network interface: %q", response.AssignedIpv6Prefixes[0]) return nil } From dd3d64943f489966f986951a13c54f680fd5f8fb Mon Sep 17 00:00:00 2001 From: Peter Rifel Date: Tue, 9 Apr 2024 20:21:12 -0500 Subject: [PATCH 03/14] Migrate remaining EC2 resource types to aws-sdk-go-v2 --- cloudmock/aws/mockautoscaling/ec2shim.go | 13 +- cloudmock/aws/mockec2/api.go | 21 ++- cloudmock/aws/mockec2/convenience.go | 2 +- cloudmock/aws/mockec2/images.go | 60 ++------- cloudmock/aws/mockec2/instances.go | 40 ++---- cloudmock/aws/mockec2/keypairs.go | 69 +++------- cloudmock/aws/mockec2/launch_templates.go | 92 +++++-------- cloudmock/aws/mockec2/tags.go | 108 ++++++--------- cloudmock/aws/mockec2/volumes.go | 119 +++-------------- pkg/apis/kops/validation/aws.go | 21 +-- pkg/apis/kops/validation/aws_test.go | 14 +- pkg/apis/kops/validation/instancegroup.go | 5 +- pkg/apis/kops/validation/validation.go | 2 +- pkg/model/awsmodel/autoscalinggroup.go | 106 ++++++++------- pkg/model/master_volumes.go | 32 ++--- pkg/resources/aws/aws.go | 98 ++++++++------ pkg/resources/aws/aws_test.go | 39 +++--- pkg/resources/aws/filters.go | 14 +- pkg/resources/aws/tags.go | 10 +- .../cloudup/awstasks/block_device_mappings.go | 62 ++++----- .../cloudup/awstasks/classic_load_balancer.go | 7 +- upup/pkg/fi/cloudup/awstasks/ebsvolume.go | 44 +++--- upup/pkg/fi/cloudup/awstasks/helper.go | 3 +- upup/pkg/fi/cloudup/awstasks/instance.go | 40 +++--- .../pkg/fi/cloudup/awstasks/launchtemplate.go | 27 ++-- .../awstasks/launchtemplate_target_api.go | 125 ++++++++++-------- .../launchtemplate_target_terraform.go | 37 +++--- .../launchtemplate_target_terraform_test.go | 34 ++--- upup/pkg/fi/cloudup/awstasks/sshkey.go | 27 ++-- upup/pkg/fi/cloudup/awstasks/tags.go | 8 +- .../fi/cloudup/spotinsttasks/elastigroup.go | 30 ++--- .../fi/cloudup/spotinsttasks/launch_spec.go | 17 +-- upup/pkg/fi/cloudup/spotinsttasks/ocean.go | 16 +++ 33 files changed, 593 insertions(+), 749 deletions(-) diff --git a/cloudmock/aws/mockautoscaling/ec2shim.go b/cloudmock/aws/mockautoscaling/ec2shim.go index 09d8630431ace..71f67568f5563 100644 --- a/cloudmock/aws/mockautoscaling/ec2shim.go +++ b/cloudmock/aws/mockautoscaling/ec2shim.go @@ -21,30 +21,29 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/autoscaling" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "k8s.io/kops/util/pkg/awsinterfaces" ) type ec2Shim struct { - ec2iface.EC2API + awsinterfaces.EC2API mockAutoscaling *MockAutoscaling } -func (m *MockAutoscaling) GetEC2Shim(e ec2iface.EC2API) ec2iface.EC2API { +func (m *MockAutoscaling) GetEC2Shim(e awsinterfaces.EC2API) awsinterfaces.EC2API { return &ec2Shim{ EC2API: e, mockAutoscaling: m, } } -func (e *ec2Shim) TerminateInstances(input *ec2.TerminateInstancesInput) (*ec2.TerminateInstancesOutput, error) { - ctx := context.TODO() +func (e *ec2Shim) TerminateInstances(ctx context.Context, input *ec2.TerminateInstancesInput, optFns ...func(*ec2.Options)) (*ec2.TerminateInstancesOutput, error) { if input.DryRun != nil && *input.DryRun { return &ec2.TerminateInstancesOutput{}, nil } for _, id := range input.InstanceIds { request := &autoscaling.TerminateInstanceInAutoScalingGroupInput{ - InstanceId: id, + InstanceId: aws.String(id), ShouldDecrementDesiredCapacity: aws.Bool(false), } if _, err := e.mockAutoscaling.TerminateInstanceInAutoScalingGroup(ctx, request); err != nil { diff --git a/cloudmock/aws/mockec2/api.go b/cloudmock/aws/mockec2/api.go index d562a6d9ec525..a6bc83a54494f 100644 --- a/cloudmock/aws/mockec2/api.go +++ b/cloudmock/aws/mockec2/api.go @@ -20,15 +20,14 @@ import ( "fmt" "sync" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/aws" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "k8s.io/kops/util/pkg/awsinterfaces" ) type MockEC2 struct { // Stub out interface - ec2iface.EC2API + awsinterfaces.EC2API mutex sync.Mutex @@ -39,7 +38,7 @@ type MockEC2 struct { DhcpOptions map[string]*ec2types.DhcpOptions - Images []*ec2.Image + Images []*ec2types.Image securityGroupNumber int SecurityGroups map[string]*ec2types.SecurityGroup @@ -47,11 +46,11 @@ type MockEC2 struct { subnets map[string]*subnetInfo - Volumes map[string]*ec2.Volume + Volumes map[string]*ec2types.Volume - KeyPairs map[string]*ec2.KeyPairInfo + KeyPairs map[string]*ec2types.KeyPairInfo - Tags []*ec2.TagDescription + Tags []*ec2types.TagDescription Vpcs map[string]*vpcInfo @@ -67,13 +66,13 @@ type MockEC2 struct { ids map[string]*idAllocator } -var _ ec2iface.EC2API = &MockEC2{} +var _ awsinterfaces.EC2API = &MockEC2{} func (m *MockEC2) All() map[string]interface{} { all := make(map[string]interface{}) for _, o := range m.Addresses { - all[aws.StringValue(o.AllocationId)] = o + all[aws.ToString(o.AllocationId)] = o } for id, o := range m.RouteTables { all[id] = o @@ -82,7 +81,7 @@ func (m *MockEC2) All() map[string]interface{} { all[id] = o } for _, o := range m.Images { - all[aws.StringValue(o.ImageId)] = o + all[aws.ToString(o.ImageId)] = o } for id, o := range m.SecurityGroups { all[id] = o diff --git a/cloudmock/aws/mockec2/convenience.go b/cloudmock/aws/mockec2/convenience.go index 78465eaa74e1d..eadf15c28d953 100644 --- a/cloudmock/aws/mockec2/convenience.go +++ b/cloudmock/aws/mockec2/convenience.go @@ -16,7 +16,7 @@ limitations under the License. package mockec2 -import "github.com/aws/aws-sdk-go/aws" +import "github.com/aws/aws-sdk-go-v2/aws" // s is a helper that builds a *string from a string value func s(v string) *string { diff --git a/cloudmock/aws/mockec2/images.go b/cloudmock/aws/mockec2/images.go index ae3054af9d2d5..c1814bf19f265 100644 --- a/cloudmock/aws/mockec2/images.go +++ b/cloudmock/aws/mockec2/images.go @@ -17,79 +17,45 @@ limitations under the License. package mockec2 import ( + "context" "fmt" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) -func (m *MockEC2) DescribeImageAttributeRequest(*ec2.DescribeImageAttributeInput) (*request.Request, *ec2.DescribeImageAttributeOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeImageAttributeWithContext(aws.Context, *ec2.DescribeImageAttributeInput, ...request.Option) (*ec2.DescribeImageAttributeOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeImageAttribute(*ec2.DescribeImageAttributeInput) (*ec2.DescribeImageAttributeOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeImagesRequest(*ec2.DescribeImagesInput) (*request.Request, *ec2.DescribeImagesOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeImagesWithContext(aws.Context, *ec2.DescribeImagesInput, ...request.Option) (*ec2.DescribeImagesOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeImagesPagesWithContext(ctx aws.Context, request *ec2.DescribeImagesInput, callback func(output *ec2.DescribeImagesOutput, b bool) bool, options ...request.Option) error { +func (m *MockEC2) DescribeImages(ctx context.Context, request *ec2.DescribeImagesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeImagesOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DescribeImagesPages: %v", request) - var images []*ec2.Image + var images []ec2types.Image for _, image := range m.Images { matches, err := m.imageMatchesFilter(image, request.Filters) if err != nil { - return err + return nil, err } if !matches { continue } copy := *image - copy.Tags = m.getTags(ec2.ResourceTypeImage, *image.ImageId) - images = append(images, ©) + copy.Tags = m.getTags(ec2types.ResourceTypeImage, *image.ImageId) + images = append(images, copy) } response := &ec2.DescribeImagesOutput{ Images: images, } - - callback(response, false) - - return nil -} - -func (m *MockEC2) DescribeImportImageTasksRequest(*ec2.DescribeImportImageTasksInput) (*request.Request, *ec2.DescribeImportImageTasksOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeImportImageTasksWithContext(aws.Context, *ec2.DescribeImportImageTasksInput, ...request.Option) (*ec2.DescribeImportImageTasksOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeImportImageTasks(*ec2.DescribeImportImageTasksInput) (*ec2.DescribeImportImageTasksOutput, error) { - panic("Not implemented") + return response, nil } -func (m *MockEC2) imageMatchesFilter(image *ec2.Image, filters []*ec2.Filter) (bool, error) { +func (m *MockEC2) imageMatchesFilter(image *ec2types.Image, filters []ec2types.Filter) (bool, error) { allFiltersMatch := true for _, filter := range filters { match := false @@ -97,14 +63,14 @@ func (m *MockEC2) imageMatchesFilter(image *ec2.Image, filters []*ec2.Filter) (b case "name": for _, v := range filter.Values { - if aws.StringValue(image.Name) == *v { + if aws.ToString(image.Name) == v { match = true } } default: if strings.HasPrefix(*filter.Name, "tag:") { - match = m.hasTag(ec2.ResourceTypeImage, *image.ImageId, filter) + match = m.hasTag(ec2types.ResourceTypeImage, *image.ImageId, filter) } else { return false, fmt.Errorf("unknown filter name: %q", *filter.Name) } diff --git a/cloudmock/aws/mockec2/instances.go b/cloudmock/aws/mockec2/instances.go index 8df203eac94d3..262596bee92be 100644 --- a/cloudmock/aws/mockec2/instances.go +++ b/cloudmock/aws/mockec2/instances.go @@ -17,49 +17,27 @@ limitations under the License. package mockec2 import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) -func (m *MockEC2) DescribeInstances(*ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) { +func (m *MockEC2) DescribeInstances(ctx context.Context, request *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) { klog.Warningf("MockEc2::DescribeInstances is stub-implemented") return &ec2.DescribeInstancesOutput{}, nil } -func (m *MockEC2) DescribeInstancesWithContext(aws.Context, *ec2.DescribeInstancesInput, ...request.Option) (*ec2.DescribeInstancesOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeInstancesRequest(*ec2.DescribeInstancesInput) (*request.Request, *ec2.DescribeInstancesOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeInstancesPages(request *ec2.DescribeInstancesInput, callback func(*ec2.DescribeInstancesOutput, bool) bool) error { - // For the mock, we just send everything in one page - page, err := m.DescribeInstances(request) - if err != nil { - return err - } - - callback(page, false) - - return nil -} - -func (m *MockEC2) DescribeInstancesPagesWithContext(aws.Context, *ec2.DescribeInstancesInput, func(*ec2.DescribeInstancesOutput, bool) bool, ...request.Option) error { - panic("Not implemented") -} - -func (m *MockEC2) DescribeInstanceTypes(*ec2.DescribeInstanceTypesInput) (*ec2.DescribeInstanceTypesOutput, error) { +func (m *MockEC2) DescribeInstanceTypes(ctx context.Context, request *ec2.DescribeInstanceTypesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstanceTypesOutput, error) { klog.Warningf("MockEc2::DescribeInstanceTypes is stub-implemented") return &ec2.DescribeInstanceTypesOutput{}, nil } -func (m *MockEC2) GetInstanceTypesFromInstanceRequirements(input *ec2.GetInstanceTypesFromInstanceRequirementsInput) (*ec2.GetInstanceTypesFromInstanceRequirementsOutput, error) { +func (m *MockEC2) GetInstanceTypesFromInstanceRequirements(ctx context.Context, request *ec2.GetInstanceTypesFromInstanceRequirementsInput, optFns ...func(*ec2.Options)) (*ec2.GetInstanceTypesFromInstanceRequirementsOutput, error) { return &ec2.GetInstanceTypesFromInstanceRequirementsOutput{ - InstanceTypes: []*ec2.InstanceTypeInfoFromInstanceRequirements{ + InstanceTypes: []ec2types.InstanceTypeInfoFromInstanceRequirements{ { InstanceType: aws.String("c5.large"), }, diff --git a/cloudmock/aws/mockec2/keypairs.go b/cloudmock/aws/mockec2/keypairs.go index 3db54c7fc8204..a0802c883b126 100644 --- a/cloudmock/aws/mockec2/keypairs.go +++ b/cloudmock/aws/mockec2/keypairs.go @@ -17,33 +17,18 @@ limitations under the License. package mockec2 import ( + "context" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/pkg/pki" ) -func (m *MockEC2) DescribeKeyPairsRequest(*ec2.DescribeKeyPairsInput) (*request.Request, *ec2.DescribeKeyPairsOutput) { - panic("MockEC2 DescribeKeyPairsRequest not implemented") -} - -func (m *MockEC2) DescribeKeyPairsWithContext(aws.Context, *ec2.DescribeKeyPairsInput, ...request.Option) (*ec2.DescribeKeyPairsOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) ImportKeyPairRequest(*ec2.ImportKeyPairInput) (*request.Request, *ec2.ImportKeyPairOutput) { - panic("MockEC2 ImportKeyPairRequest not implemented") -} - -func (m *MockEC2) ImportKeyPairWithContext(aws.Context, *ec2.ImportKeyPairInput, ...request.Option) (*ec2.ImportKeyPairOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) ImportKeyPair(request *ec2.ImportKeyPairInput) (*ec2.ImportKeyPairOutput, error) { +func (m *MockEC2) ImportKeyPair(ctx context.Context, request *ec2.ImportKeyPairInput, optFns ...func(*ec2.Options)) (*ec2.ImportKeyPairOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -57,13 +42,13 @@ func (m *MockEC2) ImportKeyPair(request *ec2.ImportKeyPairInput) (*ec2.ImportKey n := len(m.KeyPairs) + 1 id := fmt.Sprintf("key-%d", n) - kp := &ec2.KeyPairInfo{ + kp := &ec2types.KeyPairInfo{ KeyFingerprint: aws.String(fp), KeyName: request.KeyName, KeyPairId: aws.String(id), } if m.KeyPairs == nil { - m.KeyPairs = make(map[string]*ec2.KeyPairInfo) + m.KeyPairs = make(map[string]*ec2types.KeyPairInfo) } m.KeyPairs[id] = kp response := &ec2.ImportKeyPairOutput{ @@ -71,30 +56,18 @@ func (m *MockEC2) ImportKeyPair(request *ec2.ImportKeyPairInput) (*ec2.ImportKey KeyName: kp.KeyName, } - m.addTags(id, tagSpecificationsToTags(request.TagSpecifications, ec2.ResourceTypeKeyPair)...) + m.addTags(id, tagSpecificationsToTags(request.TagSpecifications, ec2types.ResourceTypeKeyPair)...) return response, nil } -func (m *MockEC2) CreateKeyPairRequest(*ec2.CreateKeyPairInput) (*request.Request, *ec2.CreateKeyPairOutput) { - panic("MockEC2 CreateKeyPairRequest not implemented") -} - -func (m *MockEC2) CreateKeyPairWithContext(aws.Context, *ec2.CreateKeyPairInput, ...request.Option) (*ec2.CreateKeyPairOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) CreateKeyPair(*ec2.CreateKeyPairInput) (*ec2.CreateKeyPairOutput, error) { - panic("MockEC2 CreateKeyPair not implemented") -} - -func (m *MockEC2) DescribeKeyPairs(request *ec2.DescribeKeyPairsInput) (*ec2.DescribeKeyPairsOutput, error) { +func (m *MockEC2) DescribeKeyPairs(ctx context.Context, request *ec2.DescribeKeyPairsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeKeyPairsOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DescribeKeyPairs: %v", request) - var keypairs []*ec2.KeyPairInfo + var keypairs []ec2types.KeyPairInfo for _, keypair := range m.KeyPairs { allFiltersMatch := true @@ -102,7 +75,7 @@ func (m *MockEC2) DescribeKeyPairs(request *ec2.DescribeKeyPairsInput) (*ec2.Des if len(request.KeyNames) != 0 { match := false for _, keyname := range request.KeyNames { - if aws.StringValue(keyname) == aws.StringValue(keypair.KeyName) { + if keyname == aws.ToString(keypair.KeyName) { match = true } } @@ -117,7 +90,7 @@ func (m *MockEC2) DescribeKeyPairs(request *ec2.DescribeKeyPairsInput) (*ec2.Des case "key-name": for _, v := range filter.Values { - if aws.StringValue(keypair.KeyName) == aws.StringValue(v) { + if aws.ToString(keypair.KeyName) == v { match = true } } @@ -136,8 +109,8 @@ func (m *MockEC2) DescribeKeyPairs(request *ec2.DescribeKeyPairsInput) (*ec2.Des } copy := *keypair - copy.Tags = m.getTags(ec2.ResourceTypeKeyPair, *copy.KeyPairId) - keypairs = append(keypairs, ©) + copy.Tags = m.getTags(ec2types.ResourceTypeKeyPair, *copy.KeyPairId) + keypairs = append(keypairs, copy) } response := &ec2.DescribeKeyPairsOutput{ @@ -147,16 +120,16 @@ func (m *MockEC2) DescribeKeyPairs(request *ec2.DescribeKeyPairsInput) (*ec2.Des return response, nil } -func (m *MockEC2) DeleteKeyPair(request *ec2.DeleteKeyPairInput) (*ec2.DeleteKeyPairOutput, error) { +func (m *MockEC2) DeleteKeyPair(ctx context.Context, request *ec2.DeleteKeyPairInput, optFns ...func(*ec2.Options)) (*ec2.DeleteKeyPairOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DeleteKeyPair: %v", request) - keyID := aws.StringValue(request.KeyPairId) + keyID := aws.ToString(request.KeyPairId) found := false for id, kp := range m.KeyPairs { - if aws.StringValue(kp.KeyPairId) == keyID { + if aws.ToString(kp.KeyPairId) == keyID { found = true delete(m.KeyPairs, id) } @@ -167,11 +140,3 @@ func (m *MockEC2) DeleteKeyPair(request *ec2.DeleteKeyPairInput) (*ec2.DeleteKey return &ec2.DeleteKeyPairOutput{}, nil } - -func (m *MockEC2) DeleteKeyPairWithContext(aws.Context, *ec2.DeleteKeyPairInput, ...request.Option) (*ec2.DeleteKeyPairOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteKeyPairRequest(*ec2.DeleteKeyPairInput) (*request.Request, *ec2.DeleteKeyPairOutput) { - panic("Not implemented") -} diff --git a/cloudmock/aws/mockec2/launch_templates.go b/cloudmock/aws/mockec2/launch_templates.go index 4a24c3e1ab58a..f09538dbb92c8 100644 --- a/cloudmock/aws/mockec2/launch_templates.go +++ b/cloudmock/aws/mockec2/launch_templates.go @@ -21,37 +21,20 @@ import ( "fmt" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) type launchTemplateInfo struct { - data *ec2.ResponseLaunchTemplateData + data *ec2types.ResponseLaunchTemplateData name *string version int } -// DescribeLaunchTemplatesPages mocks the describing the launch templates -func (m *MockEC2) DescribeLaunchTemplatesPages(request *ec2.DescribeLaunchTemplatesInput, callback func(*ec2.DescribeLaunchTemplatesOutput, bool) bool) error { - page, err := m.DescribeLaunchTemplates(request) - if err != nil { - return err - } - - callback(page, false) - - return nil -} - -// DescribeLaunchTemplatesPagesWithContext mocks the describing the launch templates -func (m *MockEC2) DescribeLaunchTemplatesPagesWithContext(ctx context.Context, request *ec2.DescribeLaunchTemplatesInput, callback func(*ec2.DescribeLaunchTemplatesOutput, bool) bool, option ...request.Option) error { - return m.DescribeLaunchTemplatesPages(request, callback) -} - // DescribeLaunchTemplates mocks the describing the launch templates -func (m *MockEC2) DescribeLaunchTemplates(request *ec2.DescribeLaunchTemplatesInput) (*ec2.DescribeLaunchTemplatesOutput, error) { +func (m *MockEC2) DescribeLaunchTemplates(ctx context.Context, request *ec2.DescribeLaunchTemplatesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeLaunchTemplatesOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -64,12 +47,12 @@ func (m *MockEC2) DescribeLaunchTemplates(request *ec2.DescribeLaunchTemplatesIn } for id, ltInfo := range m.LaunchTemplates { - launchTemplatetName := aws.StringValue(ltInfo.name) + launchTemplatetName := aws.ToString(ltInfo.name) allFiltersMatch := true for _, filter := range request.Filters { - filterName := aws.StringValue(filter.Name) - filterValue := aws.StringValue(filter.Values[0]) + filterName := aws.ToString(filter.Name) + filterValue := filter.Values[0] filterMatches := false if filterName == "tag:Name" && filterValue == launchTemplatetName { @@ -86,7 +69,7 @@ func (m *MockEC2) DescribeLaunchTemplates(request *ec2.DescribeLaunchTemplatesIn } if allFiltersMatch { - o.LaunchTemplates = append(o.LaunchTemplates, &ec2.LaunchTemplate{ + o.LaunchTemplates = append(o.LaunchTemplates, ec2types.LaunchTemplate{ LaunchTemplateName: aws.String(launchTemplatetName), LaunchTemplateId: aws.String(id), }) @@ -97,7 +80,7 @@ func (m *MockEC2) DescribeLaunchTemplates(request *ec2.DescribeLaunchTemplatesIn } // DescribeLaunchTemplateVersions mocks the retrieval of launch template versions - we don't use this at the moment so we can just return the template -func (m *MockEC2) DescribeLaunchTemplateVersions(request *ec2.DescribeLaunchTemplateVersionsInput) (*ec2.DescribeLaunchTemplateVersionsOutput, error) { +func (m *MockEC2) DescribeLaunchTemplateVersions(ctx context.Context, request *ec2.DescribeLaunchTemplateVersionsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeLaunchTemplateVersionsOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -110,10 +93,10 @@ func (m *MockEC2) DescribeLaunchTemplateVersions(request *ec2.DescribeLaunchTemp } for id, ltInfo := range m.LaunchTemplates { - if aws.StringValue(ltInfo.name) != aws.StringValue(request.LaunchTemplateName) { + if aws.ToString(ltInfo.name) != aws.ToString(request.LaunchTemplateName) { continue } - o.LaunchTemplateVersions = append(o.LaunchTemplateVersions, &ec2.LaunchTemplateVersion{ + o.LaunchTemplateVersions = append(o.LaunchTemplateVersions, ec2types.LaunchTemplateVersion{ DefaultVersion: aws.Bool(true), LaunchTemplateId: aws.String(id), LaunchTemplateData: ltInfo.data, @@ -123,13 +106,8 @@ func (m *MockEC2) DescribeLaunchTemplateVersions(request *ec2.DescribeLaunchTemp return o, nil } -// DescribeLaunchTemplateVersionsWithContext mocks the retrieval of launch template versions - we don't use this at the moment so we can just return the template -func (m *MockEC2) DescribeLaunchTemplateVersionsWithContext(ctx context.Context, request *ec2.DescribeLaunchTemplateVersionsInput, option ...request.Option) (*ec2.DescribeLaunchTemplateVersionsOutput, error) { - return m.DescribeLaunchTemplateVersions(request) -} - // CreateLaunchTemplate mocks the ec2 create launch template -func (m *MockEC2) CreateLaunchTemplate(request *ec2.CreateLaunchTemplateInput) (*ec2.CreateLaunchTemplateOutput, error) { +func (m *MockEC2) CreateLaunchTemplate(ctx context.Context, request *ec2.CreateLaunchTemplateInput, optFns ...func(*ec2.Options)) (*ec2.CreateLaunchTemplateOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -150,16 +128,16 @@ func (m *MockEC2) CreateLaunchTemplate(request *ec2.CreateLaunchTemplateInput) ( name: request.LaunchTemplateName, version: 1, } - m.addTags(id, tagSpecificationsToTags(request.TagSpecifications, ec2.ResourceTypeLaunchTemplate)...) + m.addTags(id, tagSpecificationsToTags(request.TagSpecifications, ec2types.ResourceTypeLaunchTemplate)...) return &ec2.CreateLaunchTemplateOutput{ - LaunchTemplate: &ec2.LaunchTemplate{ + LaunchTemplate: &ec2types.LaunchTemplate{ LaunchTemplateId: aws.String(id), }, }, nil } -func (m *MockEC2) CreateLaunchTemplateVersion(request *ec2.CreateLaunchTemplateVersionInput) (*ec2.CreateLaunchTemplateVersionOutput, error) { +func (m *MockEC2) CreateLaunchTemplateVersion(ctx context.Context, request *ec2.CreateLaunchTemplateVersionInput, optFns ...func(*ec2.Options)) (*ec2.CreateLaunchTemplateVersionOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -170,7 +148,7 @@ func (m *MockEC2) CreateLaunchTemplateVersion(request *ec2.CreateLaunchTemplateV var ltVersion int var ltID string for id, ltInfo := range m.LaunchTemplates { - if aws.StringValue(ltInfo.name) == aws.StringValue(name) { + if aws.ToString(ltInfo.name) == aws.ToString(name) { found = true ltInfo.data = responseLaunchTemplateData(request.LaunchTemplateData) ltInfo.version++ @@ -182,7 +160,7 @@ func (m *MockEC2) CreateLaunchTemplateVersion(request *ec2.CreateLaunchTemplateV return nil, nil // TODO: error } return &ec2.CreateLaunchTemplateVersionOutput{ - LaunchTemplateVersion: &ec2.LaunchTemplateVersion{ + LaunchTemplateVersion: &ec2types.LaunchTemplateVersion{ VersionNumber: aws.Int64(int64(ltVersion)), LaunchTemplateId: <ID, }, @@ -190,7 +168,7 @@ func (m *MockEC2) CreateLaunchTemplateVersion(request *ec2.CreateLaunchTemplateV } // DeleteLaunchTemplate mocks the deletion of a launch template -func (m *MockEC2) DeleteLaunchTemplate(request *ec2.DeleteLaunchTemplateInput) (*ec2.DeleteLaunchTemplateOutput, error) { +func (m *MockEC2) DeleteLaunchTemplate(ctx context.Context, request *ec2.DeleteLaunchTemplateInput, optFns ...func(*ec2.Options)) (*ec2.DeleteLaunchTemplateOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -202,7 +180,7 @@ func (m *MockEC2) DeleteLaunchTemplate(request *ec2.DeleteLaunchTemplateInput) ( return o, nil } for id := range m.LaunchTemplates { - if id == aws.StringValue(request.LaunchTemplateId) { + if id == aws.ToString(request.LaunchTemplateId) { delete(m.LaunchTemplates, id) } } @@ -210,12 +188,12 @@ func (m *MockEC2) DeleteLaunchTemplate(request *ec2.DeleteLaunchTemplateInput) ( return o, nil } -func (m *MockEC2) ModifyLaunchTemplate(*ec2.ModifyLaunchTemplateInput) (*ec2.ModifyLaunchTemplateOutput, error) { +func (m *MockEC2) ModifyLaunchTemplate(ctx context.Context, request *ec2.ModifyLaunchTemplateInput, optFns ...func(*ec2.Options)) (*ec2.ModifyLaunchTemplateOutput, error) { return &ec2.ModifyLaunchTemplateOutput{}, nil } -func responseLaunchTemplateData(req *ec2.RequestLaunchTemplateData) *ec2.ResponseLaunchTemplateData { - resp := &ec2.ResponseLaunchTemplateData{ +func responseLaunchTemplateData(req *ec2types.RequestLaunchTemplateData) *ec2types.ResponseLaunchTemplateData { + resp := &ec2types.ResponseLaunchTemplateData{ DisableApiTermination: req.DisableApiTermination, EbsOptimized: req.EbsOptimized, ImageId: req.ImageId, @@ -227,26 +205,26 @@ func responseLaunchTemplateData(req *ec2.RequestLaunchTemplateData) *ec2.Respons } if req.MetadataOptions != nil { - resp.MetadataOptions = &ec2.LaunchTemplateInstanceMetadataOptions{ + resp.MetadataOptions = &ec2types.LaunchTemplateInstanceMetadataOptions{ HttpTokens: req.MetadataOptions.HttpTokens, HttpPutResponseHopLimit: req.MetadataOptions.HttpPutResponseHopLimit, HttpProtocolIpv6: req.MetadataOptions.HttpProtocolIpv6, } } if req.Monitoring != nil { - resp.Monitoring = &ec2.LaunchTemplatesMonitoring{Enabled: req.Monitoring.Enabled} + resp.Monitoring = &ec2types.LaunchTemplatesMonitoring{Enabled: req.Monitoring.Enabled} } if req.CpuOptions != nil { - resp.CpuOptions = &ec2.LaunchTemplateCpuOptions{ + resp.CpuOptions = &ec2types.LaunchTemplateCpuOptions{ CoreCount: req.CpuOptions.CoreCount, ThreadsPerCore: req.CpuOptions.ThreadsPerCore, } } if len(req.BlockDeviceMappings) > 0 { for _, x := range req.BlockDeviceMappings { - var ebs *ec2.LaunchTemplateEbsBlockDevice + var ebs *ec2types.LaunchTemplateEbsBlockDevice if x.Ebs != nil { - ebs = &ec2.LaunchTemplateEbsBlockDevice{ + ebs = &ec2types.LaunchTemplateEbsBlockDevice{ DeleteOnTermination: x.Ebs.DeleteOnTermination, Encrypted: x.Ebs.Encrypted, Iops: x.Ebs.Iops, @@ -257,7 +235,7 @@ func responseLaunchTemplateData(req *ec2.RequestLaunchTemplateData) *ec2.Respons VolumeType: x.Ebs.VolumeType, } } - resp.BlockDeviceMappings = append(resp.BlockDeviceMappings, &ec2.LaunchTemplateBlockDeviceMapping{ + resp.BlockDeviceMappings = append(resp.BlockDeviceMappings, ec2types.LaunchTemplateBlockDeviceMapping{ DeviceName: x.DeviceName, Ebs: ebs, NoDevice: x.NoDevice, @@ -266,18 +244,18 @@ func responseLaunchTemplateData(req *ec2.RequestLaunchTemplateData) *ec2.Respons } } if req.CreditSpecification != nil { - resp.CreditSpecification = &ec2.CreditSpecification{CpuCredits: req.CreditSpecification.CpuCredits} + resp.CreditSpecification = &ec2types.CreditSpecification{CpuCredits: req.CreditSpecification.CpuCredits} } if req.IamInstanceProfile != nil { - resp.IamInstanceProfile = &ec2.LaunchTemplateIamInstanceProfileSpecification{ + resp.IamInstanceProfile = &ec2types.LaunchTemplateIamInstanceProfileSpecification{ Arn: req.IamInstanceProfile.Arn, Name: req.IamInstanceProfile.Name, } } if req.InstanceMarketOptions != nil { - resp.InstanceMarketOptions = &ec2.LaunchTemplateInstanceMarketOptions{ + resp.InstanceMarketOptions = &ec2types.LaunchTemplateInstanceMarketOptions{ MarketType: req.InstanceMarketOptions.MarketType, - SpotOptions: &ec2.LaunchTemplateSpotMarketOptions{ + SpotOptions: &ec2types.LaunchTemplateSpotMarketOptions{ BlockDurationMinutes: req.InstanceMarketOptions.SpotOptions.BlockDurationMinutes, InstanceInterruptionBehavior: req.InstanceMarketOptions.SpotOptions.InstanceInterruptionBehavior, MaxPrice: req.InstanceMarketOptions.SpotOptions.MaxPrice, @@ -288,7 +266,7 @@ func responseLaunchTemplateData(req *ec2.RequestLaunchTemplateData) *ec2.Respons } if len(req.NetworkInterfaces) > 0 { for _, x := range req.NetworkInterfaces { - resp.NetworkInterfaces = append(resp.NetworkInterfaces, &ec2.LaunchTemplateInstanceNetworkInterfaceSpecification{ + resp.NetworkInterfaces = append(resp.NetworkInterfaces, ec2types.LaunchTemplateInstanceNetworkInterfaceSpecification{ AssociatePublicIpAddress: x.AssociatePublicIpAddress, DeleteOnTermination: x.DeleteOnTermination, Description: x.Description, @@ -305,7 +283,7 @@ func responseLaunchTemplateData(req *ec2.RequestLaunchTemplateData) *ec2.Respons } if len(req.TagSpecifications) > 0 { for _, x := range req.TagSpecifications { - resp.TagSpecifications = append(resp.TagSpecifications, &ec2.LaunchTemplateTagSpecification{ + resp.TagSpecifications = append(resp.TagSpecifications, ec2types.LaunchTemplateTagSpecification{ ResourceType: x.ResourceType, Tags: x.Tags, }) diff --git a/cloudmock/aws/mockec2/tags.go b/cloudmock/aws/mockec2/tags.go index 5bcf146b453ee..de646a9778346 100644 --- a/cloudmock/aws/mockec2/tags.go +++ b/cloudmock/aws/mockec2/tags.go @@ -17,87 +17,71 @@ limitations under the License. package mockec2 import ( + "context" "fmt" "sort" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) -func (m *MockEC2) CreateTagsRequest(*ec2.CreateTagsInput) (*request.Request, *ec2.CreateTagsOutput) { - panic("Not implemented") -} - -func (m *MockEC2) CreateTagsWithContext(aws.Context, *ec2.CreateTagsInput, ...request.Option) (*ec2.CreateTagsOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) CreateTags(request *ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) { +func (m *MockEC2) CreateTags(ctx context.Context, request *ec2.CreateTagsInput, optFns ...func(*ec2.Options)) (*ec2.CreateTagsOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("CreateTags %v", request) for _, v := range request.Resources { - resourceId := *v - m.addTags(resourceId, request.Tags...) + m.addTags(v, request.Tags...) } response := &ec2.CreateTagsOutput{} return response, nil } -func (m *MockEC2) addTags(resourceId string, tags ...*ec2.Tag) { - resourceType := "" +func (m *MockEC2) addTags(resourceId string, tags ...ec2types.Tag) { + var resourceType ec2types.ResourceType if strings.HasPrefix(resourceId, "subnet-") { - resourceType = ec2.ResourceTypeSubnet + resourceType = ec2types.ResourceTypeSubnet } else if strings.HasPrefix(resourceId, "vpc-") { - resourceType = ec2.ResourceTypeVpc + resourceType = ec2types.ResourceTypeVpc } else if strings.HasPrefix(resourceId, "sg-") { - resourceType = ec2.ResourceTypeSecurityGroup + resourceType = ec2types.ResourceTypeSecurityGroup } else if strings.HasPrefix(resourceId, "vol-") { - resourceType = ec2.ResourceTypeVolume + resourceType = ec2types.ResourceTypeVolume } else if strings.HasPrefix(resourceId, "igw-") { - resourceType = ec2.ResourceTypeInternetGateway + resourceType = ec2types.ResourceTypeInternetGateway } else if strings.HasPrefix(resourceId, "eigw-") { - resourceType = ec2.ResourceTypeEgressOnlyInternetGateway + resourceType = ec2types.ResourceTypeEgressOnlyInternetGateway } else if strings.HasPrefix(resourceId, "nat-") { - resourceType = ec2.ResourceTypeNatgateway + resourceType = ec2types.ResourceTypeNatgateway } else if strings.HasPrefix(resourceId, "dopt-") { - resourceType = ec2.ResourceTypeDhcpOptions + resourceType = ec2types.ResourceTypeDhcpOptions } else if strings.HasPrefix(resourceId, "rtb-") { - resourceType = ec2.ResourceTypeRouteTable + resourceType = ec2types.ResourceTypeRouteTable } else if strings.HasPrefix(resourceId, "eipalloc-") { - resourceType = ec2.ResourceTypeElasticIp + resourceType = ec2types.ResourceTypeElasticIp } else if strings.HasPrefix(resourceId, "lt-") { - resourceType = ec2.ResourceTypeLaunchTemplate + resourceType = ec2types.ResourceTypeLaunchTemplate } else if strings.HasPrefix(resourceId, "key-") { - resourceType = ec2.ResourceTypeKeyPair + resourceType = ec2types.ResourceTypeKeyPair } else { klog.Fatalf("Unknown resource-type in create tags: %v", resourceId) } for _, tag := range tags { - t := &ec2.TagDescription{ + t := &ec2types.TagDescription{ Key: tag.Key, Value: tag.Value, ResourceId: s(resourceId), - ResourceType: s(resourceType), + ResourceType: resourceType, } m.Tags = append(m.Tags, t) } } -func (m *MockEC2) DescribeTagsRequest(*ec2.DescribeTagsInput) (*request.Request, *ec2.DescribeTagsOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeTagsWithContext(aws.Context, *ec2.DescribeTagsInput, ...request.Option) (*ec2.DescribeTagsOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) hasTag(resourceType string, resourceId string, filter *ec2.Filter) bool { +func (m *MockEC2) hasTag(resourceType ec2types.ResourceType, resourceId string, filter ec2types.Filter) bool { name := *filter.Name if strings.HasPrefix(name, "tag:") { tagKey := name[4:] @@ -106,7 +90,7 @@ func (m *MockEC2) hasTag(resourceType string, resourceId string, filter *ec2.Fil if *tag.ResourceId != resourceId { continue } - if *tag.ResourceType != resourceType { + if tag.ResourceType != resourceType { continue } if *tag.Key != tagKey { @@ -114,7 +98,7 @@ func (m *MockEC2) hasTag(resourceType string, resourceId string, filter *ec2.Fil } for _, v := range filter.Values { - if *tag.Value == *v { + if *tag.Value == v { return true } } @@ -124,11 +108,11 @@ func (m *MockEC2) hasTag(resourceType string, resourceId string, filter *ec2.Fil if *tag.ResourceId != resourceId { continue } - if *tag.ResourceType != resourceType { + if tag.ResourceType != resourceType { continue } for _, v := range filter.Values { - if *tag.Key == *v { + if *tag.Key == v { return true } } @@ -139,17 +123,17 @@ func (m *MockEC2) hasTag(resourceType string, resourceId string, filter *ec2.Fil return false } -func (m *MockEC2) getTags(resourceType string, resourceId string) []*ec2.Tag { - var tags []*ec2.Tag +func (m *MockEC2) getTags(resourceType ec2types.ResourceType, resourceId string) []ec2types.Tag { + var tags []ec2types.Tag for _, tag := range m.Tags { if *tag.ResourceId != resourceId { continue } - if *tag.ResourceType != resourceType { + if tag.ResourceType != resourceType { continue } - t := &ec2.Tag{ + t := ec2types.Tag{ Key: tag.Key, Value: tag.Value, } @@ -158,13 +142,13 @@ func (m *MockEC2) getTags(resourceType string, resourceId string) []*ec2.Tag { return tags } -func (m *MockEC2) DescribeTags(request *ec2.DescribeTagsInput) (*ec2.DescribeTagsOutput, error) { +func (m *MockEC2) DescribeTags(ctx context.Context, request *ec2.DescribeTagsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeTagsOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DescribeTags %v", request) - var tags []*ec2.TagDescription + var tags []ec2types.TagDescription for _, tag := range m.Tags { allFiltersMatch := true @@ -173,14 +157,14 @@ func (m *MockEC2) DescribeTags(request *ec2.DescribeTagsInput) (*ec2.DescribeTag switch *filter.Name { case "key": for _, v := range filter.Values { - if *v == *tag.Key { + if v == *tag.Key { match = true } } case "resource-id": for _, v := range filter.Values { - if *v == *tag.ResourceId { + if v == *tag.ResourceId { match = true } } @@ -200,7 +184,7 @@ func (m *MockEC2) DescribeTags(request *ec2.DescribeTagsInput) (*ec2.DescribeTag } copy := *tag - tags = append(tags, ©) + tags = append(tags, copy) } response := &ec2.DescribeTagsOutput{ @@ -210,29 +194,19 @@ func (m *MockEC2) DescribeTags(request *ec2.DescribeTagsInput) (*ec2.DescribeTag return response, nil } -func (m *MockEC2) DescribeTagsPages(*ec2.DescribeTagsInput, func(*ec2.DescribeTagsOutput, bool) bool) error { - panic("Not implemented") -} - -func (m *MockEC2) DescribeTagsPagesWithContext(aws.Context, *ec2.DescribeTagsInput, func(*ec2.DescribeTagsOutput, bool) bool, ...request.Option) error { - panic("Not implemented") -} - // SortTags sorts the slice of tags by Key -func SortTags(tags []*ec2.Tag) { +func SortTags(tags []ec2types.Tag) { keys := make([]string, len(tags)) for i := range tags { - if tags[i] != nil { - keys[i] = aws.StringValue(tags[i].Key) - } + keys[i] = aws.ToString(tags[i].Key) } sort.SliceStable(tags, func(i, j int) bool { return keys[i] < keys[j] }) } -func tagSpecificationsToTags(specifications []*ec2.TagSpecification, resourceType string) []*ec2.Tag { - tags := make([]*ec2.Tag, 0) +func tagSpecificationsToTags(specifications []ec2types.TagSpecification, resourceType ec2types.ResourceType) []ec2types.Tag { + tags := make([]ec2types.Tag, 0) for _, specification := range specifications { - if aws.StringValue(specification.ResourceType) != resourceType { + if specification.ResourceType != resourceType { continue } tags = append(tags, specification.Tags...) diff --git a/cloudmock/aws/mockec2/volumes.go b/cloudmock/aws/mockec2/volumes.go index fccfa7b56c3ea..6e5323aea14fb 100644 --- a/cloudmock/aws/mockec2/volumes.go +++ b/cloudmock/aws/mockec2/volumes.go @@ -17,16 +17,17 @@ limitations under the License. package mockec2 import ( + "context" "fmt" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) -func (m *MockEC2) CreateVolume(request *ec2.CreateVolumeInput) (*ec2.Volume, error) { +func (m *MockEC2) CreateVolume(ctx context.Context, request *ec2.CreateVolumeInput, optFns ...func(*ec2.Options)) (*ec2.CreateVolumeOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -39,7 +40,7 @@ func (m *MockEC2) CreateVolume(request *ec2.CreateVolumeInput) (*ec2.Volume, err n := len(m.Volumes) + 1 id := fmt.Sprintf("vol-%d", n) - volume := &ec2.Volume{ + volume := &ec2types.Volume{ VolumeId: s(id), AvailabilityZone: request.AvailabilityZone, Encrypted: request.Encrypted, @@ -52,14 +53,14 @@ func (m *MockEC2) CreateVolume(request *ec2.CreateVolumeInput) (*ec2.Volume, err } if m.Volumes == nil { - m.Volumes = make(map[string]*ec2.Volume) + m.Volumes = make(map[string]*ec2types.Volume) } m.Volumes[*volume.VolumeId] = volume - m.addTags(id, tagSpecificationsToTags(request.TagSpecifications, ec2.ResourceTypeVolume)...) + m.addTags(id, tagSpecificationsToTags(request.TagSpecifications, ec2types.ResourceTypeVolume)...) copy := *volume - copy.Tags = m.getTags(ec2.ResourceTypeVolume, *volume.VolumeId) + copy.Tags = m.getTags(ec2types.ResourceTypeVolume, *volume.VolumeId) // TODO: a few fields // // Information about the volume attachments. // Attachments []*VolumeAttachment `locationName:"attachmentSet" locationNameList:"item" type:"list"` @@ -70,58 +71,12 @@ func (m *MockEC2) CreateVolume(request *ec2.CreateVolumeInput) (*ec2.Volume, err // // The volume state. // State *string `locationName:"status" type:"string" enum:"VolumeState"` - return ©, nil + return &ec2.CreateVolumeOutput{ + VolumeId: copy.VolumeId, + }, nil } -func (m *MockEC2) CreateVolumeWithContext(aws.Context, *ec2.CreateVolumeInput, ...request.Option) (*ec2.Volume, error) { - panic("Not implemented") -} - -func (m *MockEC2) CreateVolumeRequest(*ec2.CreateVolumeInput) (*request.Request, *ec2.Volume) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeVolumeAttributeRequest(*ec2.DescribeVolumeAttributeInput) (*request.Request, *ec2.DescribeVolumeAttributeOutput) { - panic("MockEC2 DescribeVolumeAttributeRequest not implemented") -} - -func (m *MockEC2) DescribeVolumeAttributeWithContext(aws.Context, *ec2.DescribeVolumeAttributeInput, ...request.Option) (*ec2.DescribeVolumeAttributeOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeVolumeAttribute(*ec2.DescribeVolumeAttributeInput) (*ec2.DescribeVolumeAttributeOutput, error) { - panic("MockEC2 DescribeVolumeAttribute not implemented") -} - -func (m *MockEC2) DescribeVolumeStatusRequest(*ec2.DescribeVolumeStatusInput) (*request.Request, *ec2.DescribeVolumeStatusOutput) { - panic("MockEC2 DescribeVolumeStatusRequest not implemented") -} - -func (m *MockEC2) DescribeVolumeStatusWithContext(aws.Context, *ec2.DescribeVolumeStatusInput, ...request.Option) (*ec2.DescribeVolumeStatusOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeVolumeStatus(*ec2.DescribeVolumeStatusInput) (*ec2.DescribeVolumeStatusOutput, error) { - panic("MockEC2 DescribeVolumeStatus not implemented") -} - -func (m *MockEC2) DescribeVolumeStatusPages(*ec2.DescribeVolumeStatusInput, func(*ec2.DescribeVolumeStatusOutput, bool) bool) error { - panic("MockEC2 DescribeVolumeStatusPages not implemented") -} - -func (m *MockEC2) DescribeVolumeStatusPagesWithContext(aws.Context, *ec2.DescribeVolumeStatusInput, func(*ec2.DescribeVolumeStatusOutput, bool) bool, ...request.Option) error { - panic("Not implemented") -} - -func (m *MockEC2) DescribeVolumesRequest(*ec2.DescribeVolumesInput) (*request.Request, *ec2.DescribeVolumesOutput) { - panic("MockEC2 DescribeVolumesRequest not implemented") -} - -func (m *MockEC2) DescribeVolumesWithContext(aws.Context, *ec2.DescribeVolumesInput, ...request.Option) (*ec2.DescribeVolumesOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeVolumes(request *ec2.DescribeVolumesInput) (*ec2.DescribeVolumesOutput, error) { +func (m *MockEC2) DescribeVolumes(ctx context.Context, request *ec2.DescribeVolumesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeVolumesOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -131,7 +86,7 @@ func (m *MockEC2) DescribeVolumes(request *ec2.DescribeVolumesInput) (*ec2.Descr klog.Fatalf("VolumeIds") } - var volumes []*ec2.Volume + var volumes []ec2types.Volume for _, volume := range m.Volumes { allFiltersMatch := true @@ -140,7 +95,7 @@ func (m *MockEC2) DescribeVolumes(request *ec2.DescribeVolumesInput) (*ec2.Descr switch *filter.Name { default: if strings.HasPrefix(*filter.Name, "tag:") { - match = m.hasTag(ec2.ResourceTypeVolume, *volume.VolumeId, filter) + match = m.hasTag(ec2types.ResourceTypeVolume, *volume.VolumeId, filter) } else { return nil, fmt.Errorf("unknown filter name: %q", *filter.Name) } @@ -157,8 +112,8 @@ func (m *MockEC2) DescribeVolumes(request *ec2.DescribeVolumesInput) (*ec2.Descr } copy := *volume - copy.Tags = m.getTags(ec2.ResourceTypeVolume, *volume.VolumeId) - volumes = append(volumes, ©) + copy.Tags = m.getTags(ec2types.ResourceTypeVolume, *volume.VolumeId) + volumes = append(volumes, copy) } response := &ec2.DescribeVolumesOutput{ @@ -168,41 +123,13 @@ func (m *MockEC2) DescribeVolumes(request *ec2.DescribeVolumesInput) (*ec2.Descr return response, nil } -func (m *MockEC2) DescribeVolumesPages(request *ec2.DescribeVolumesInput, callback func(*ec2.DescribeVolumesOutput, bool) bool) error { - // For the mock, we just send everything in one page - page, err := m.DescribeVolumes(request) - if err != nil { - return err - } - - callback(page, false) - - return nil -} - -func (m *MockEC2) DescribeVolumesPagesWithContext(aws.Context, *ec2.DescribeVolumesInput, func(*ec2.DescribeVolumesOutput, bool) bool, ...request.Option) error { - panic("Not implemented") -} - -func (m *MockEC2) DescribeVolumesModifications(*ec2.DescribeVolumesModificationsInput) (*ec2.DescribeVolumesModificationsOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeVolumesModificationsWithContext(aws.Context, *ec2.DescribeVolumesModificationsInput, ...request.Option) (*ec2.DescribeVolumesModificationsOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DescribeVolumesModificationsRequest(*ec2.DescribeVolumesModificationsInput) (*request.Request, *ec2.DescribeVolumesModificationsOutput) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteVolume(request *ec2.DeleteVolumeInput) (*ec2.DeleteVolumeOutput, error) { +func (m *MockEC2) DeleteVolume(ctx context.Context, request *ec2.DeleteVolumeInput, optFns ...func(*ec2.Options)) (*ec2.DeleteVolumeOutput, error) { m.mutex.Lock() defer m.mutex.Unlock() klog.Infof("DeleteVolume: %v", request) - id := aws.StringValue(request.VolumeId) + id := aws.ToString(request.VolumeId) o := m.Volumes[id] if o == nil { return nil, fmt.Errorf("Volume %q not found", id) @@ -211,11 +138,3 @@ func (m *MockEC2) DeleteVolume(request *ec2.DeleteVolumeInput) (*ec2.DeleteVolum return &ec2.DeleteVolumeOutput{}, nil } - -func (m *MockEC2) DeleteVolumeWithContext(aws.Context, *ec2.DeleteVolumeInput, ...request.Option) (*ec2.DeleteVolumeOutput, error) { - panic("Not implemented") -} - -func (m *MockEC2) DeleteVolumeRequest(*ec2.DeleteVolumeInput) (*request.Request, *ec2.DeleteVolumeOutput) { - panic("Not implemented") -} diff --git a/pkg/apis/kops/validation/aws.go b/pkg/apis/kops/validation/aws.go index 7448776ce9f92..1a9cfdf52074a 100644 --- a/pkg/apis/kops/validation/aws.go +++ b/pkg/apis/kops/validation/aws.go @@ -23,7 +23,8 @@ import ( "strings" "github.com/aws/aws-sdk-go-v2/aws/arn" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation/field" @@ -186,7 +187,7 @@ func awsValidateInstanceTypeAndImage(instanceTypeFieldPath *field.Path, imageFie return append(allErrs, field.Invalid(imageFieldPath, image, fmt.Sprintf("specified image %q is invalid: %s", image, err))) } - imageArch := fi.ValueOf(imageInfo.Architecture) + imageArch := string(imageInfo.Architecture) // Spotinst uses the instance type field to keep a "," separated list of instance types for _, instanceType := range strings.Split(instanceTypes, ",") { @@ -199,15 +200,17 @@ func awsValidateInstanceTypeAndImage(instanceTypeFieldPath *field.Path, imageFie found := false if machineInfo != nil && machineInfo.ProcessorInfo != nil { for _, machineArch := range machineInfo.ProcessorInfo.SupportedArchitectures { - if imageArch == fi.ValueOf(machineArch) { + if imageArch == string(machineArch) { found = true } } } if !found { - var machineArch []string + machineArch := make([]string, 0) if machineInfo != nil && machineInfo.ProcessorInfo != nil && machineInfo.ProcessorInfo.SupportedArchitectures != nil { - machineArch = fi.StringSliceValue(machineInfo.ProcessorInfo.SupportedArchitectures) + for _, arch := range machineInfo.ProcessorInfo.SupportedArchitectures { + machineArch = append(machineArch, string(arch)) + } } allErrs = append(allErrs, field.Invalid(instanceTypeFieldPath, instanceTypes, fmt.Sprintf("machine type architecture %q does not match image architecture %q", strings.Join(machineArch, ","), imageArch))) @@ -230,8 +233,8 @@ func awsValidateSpotDurationInMinute(fieldPath *field.Path, ig *kops.InstanceGro func awsValidateInstanceInterruptionBehavior(fieldPath *field.Path, ig *kops.InstanceGroup) field.ErrorList { allErrs := field.ErrorList{} if ig.Spec.InstanceInterruptionBehavior != nil { - instanceInterruptionBehavior := *ig.Spec.InstanceInterruptionBehavior - allErrs = append(allErrs, IsValidValue(fieldPath, &instanceInterruptionBehavior, ec2.InstanceInterruptionBehavior_Values())...) + instanceInterruptionBehavior := ec2types.InstanceInterruptionBehavior(*ig.Spec.InstanceInterruptionBehavior) + allErrs = append(allErrs, IsValidValue(fieldPath, &instanceInterruptionBehavior, ec2types.InstanceInterruptionBehavior("").Values())...) } return allErrs } @@ -240,7 +243,7 @@ func awsValidateInstanceInterruptionBehavior(fieldPath *field.Path, ig *kops.Ins func awsValidateMixedInstancesPolicy(path *field.Path, spec *kops.MixedInstancesPolicySpec, ig *kops.InstanceGroup, cloud awsup.AWSCloud) field.ErrorList { var errs field.ErrorList - mainMachineTypeInfo, err := awsup.GetMachineTypeInfo(cloud, ig.Spec.MachineType) + mainMachineTypeInfo, err := awsup.GetMachineTypeInfo(cloud, ec2types.InstanceType(ig.Spec.MachineType)) if err != nil { errs = append(errs, field.Invalid(field.NewPath("spec", "machineType"), ig.Spec.MachineType, fmt.Sprintf("machine type specified is invalid: %q", ig.Spec.MachineType))) return errs @@ -254,7 +257,7 @@ func awsValidateMixedInstancesPolicy(path *field.Path, spec *kops.MixedInstances errs = append(errs, awsValidateInstanceTypeAndImage(path.Child("instances").Index(i), path.Child("image"), instanceTypes, ig.Spec.Image, cloud)...) for _, instanceType := range strings.Split(instanceTypes, ",") { - machineTypeInfo, err := awsup.GetMachineTypeInfo(cloud, instanceType) + machineTypeInfo, err := awsup.GetMachineTypeInfo(cloud, ec2types.InstanceType(instanceType)) if err != nil { errs = append(errs, field.Invalid(field.NewPath("spec", "machineType"), ig.Spec.MachineType, fmt.Sprintf("machine type specified is invalid: %q", ig.Spec.MachineType))) return errs diff --git a/pkg/apis/kops/validation/aws_test.go b/pkg/apis/kops/validation/aws_test.go index 9d71055542aaa..0ebb749b2e805 100644 --- a/pkg/apis/kops/validation/aws_test.go +++ b/pkg/apis/kops/validation/aws_test.go @@ -21,7 +21,7 @@ import ( "time" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/kops/cloudmock/aws/mockec2" @@ -238,13 +238,13 @@ func TestValidateInstanceGroupSpec(t *testing.T) { mockEC2 := &mockec2.MockEC2{} cloud.MockEC2 = mockEC2 - mockEC2.Images = append(mockEC2.Images, &ec2.Image{ + mockEC2.Images = append(mockEC2.Images, &ec2types.Image{ CreationDate: aws.String("2016-10-21T20:07:19.000Z"), ImageId: aws.String("ami-073c8c0760395aab8"), Name: aws.String("focal"), OwnerId: aws.String(awsup.WellKnownAccountUbuntu), RootDeviceName: aws.String("/dev/xvda"), - Architecture: aws.String("x86_64"), + Architecture: ec2types.ArchitectureValuesX8664, }) for _, g := range grid { @@ -339,13 +339,13 @@ func TestMixedInstancePolicies(t *testing.T) { mockEC2 := &mockec2.MockEC2{} cloud.MockEC2 = mockEC2 - mockEC2.Images = append(mockEC2.Images, &ec2.Image{ + mockEC2.Images = append(mockEC2.Images, &ec2types.Image{ CreationDate: aws.String("2016-10-21T20:07:19.000Z"), ImageId: aws.String("ami-073c8c0760395aab8"), Name: aws.String("focal"), OwnerId: aws.String(awsup.WellKnownAccountUbuntu), RootDeviceName: aws.String("/dev/xvda"), - Architecture: aws.String("x86_64"), + Architecture: ec2types.ArchitectureValuesX8664, }) for _, g := range grid { @@ -367,13 +367,13 @@ func TestInstanceMetadataOptions(t *testing.T) { mockEC2 := &mockec2.MockEC2{} cloud.MockEC2 = mockEC2 - mockEC2.Images = append(mockEC2.Images, &ec2.Image{ + mockEC2.Images = append(mockEC2.Images, &ec2types.Image{ CreationDate: aws.String("2016-10-21T20:07:19.000Z"), ImageId: aws.String("ami-073c8c0760395aab8"), Name: aws.String("focal"), OwnerId: aws.String(awsup.WellKnownAccountUbuntu), RootDeviceName: aws.String("/dev/xvda"), - Architecture: aws.String("x86_64"), + Architecture: ec2types.ArchitectureValuesX8664, }) tests := []struct { diff --git a/pkg/apis/kops/validation/instancegroup.go b/pkg/apis/kops/validation/instancegroup.go index aa22074948816..0e3b4534a0a98 100644 --- a/pkg/apis/kops/validation/instancegroup.go +++ b/pkg/apis/kops/validation/instancegroup.go @@ -23,7 +23,7 @@ import ( "k8s.io/kops/pkg/nodeidentity/aws" "github.com/aws/aws-sdk-go-v2/aws/arn" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation/field" @@ -67,7 +67,8 @@ func ValidateInstanceGroup(g *kops.InstanceGroup, cloud fi.Cloud, strict bool) f } if g.Spec.Tenancy != "" { - allErrs = append(allErrs, IsValidValue(field.NewPath("spec", "tenancy"), &g.Spec.Tenancy, ec2.Tenancy_Values())...) + tenancy := ec2types.Tenancy(g.Spec.Tenancy) + allErrs = append(allErrs, IsValidValue(field.NewPath("spec", "tenancy"), &tenancy, ec2types.Tenancy("").Values())...) } if strict && g.Spec.Manager == kops.InstanceManagerCloudGroup { diff --git a/pkg/apis/kops/validation/validation.go b/pkg/apis/kops/validation/validation.go index 99c7188985e60..593ac88854e37 100644 --- a/pkg/apis/kops/validation/validation.go +++ b/pkg/apis/kops/validation/validation.go @@ -25,7 +25,7 @@ import ( "regexp" "strings" - "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go-v2/aws/arn" "github.com/blang/semver/v4" "golang.org/x/net/ipv4" "golang.org/x/net/ipv6" diff --git a/pkg/model/awsmodel/autoscalinggroup.go b/pkg/model/awsmodel/autoscalinggroup.go index aa33229b45d47..1a37ddd30c916 100644 --- a/pkg/model/awsmodel/autoscalinggroup.go +++ b/pkg/model/awsmodel/autoscalinggroup.go @@ -22,7 +22,7 @@ import ( "strings" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/klog/v2" @@ -37,7 +37,7 @@ import ( const ( // DefaultVolumeType is the default volume type - DefaultVolumeType = ec2.VolumeTypeGp3 + DefaultVolumeType = ec2types.VolumeTypeGp3 // DefaultVolumeIonIops is the default volume IOPS when volume type is io1 or io2 DefaultVolumeIonIops = 100 // DefaultVolumeGp3Iops is the default volume IOPS when volume type is gp3 @@ -147,7 +147,7 @@ func (b *AutoscalingGroupModelBuilder) buildLaunchTemplateTask(c *fi.CloudupMode if err != nil { return nil, err } - var rootVolumeType string + var rootVolumeType ec2types.VolumeType rootVolumeEncryption := DefaultVolumeEncryption rootVolumeKmsKey := "" @@ -156,7 +156,7 @@ func (b *AutoscalingGroupModelBuilder) buildLaunchTemplateTask(c *fi.CloudupMode rootVolumeSize = fi.ValueOf(ig.Spec.RootVolume.Size) } - rootVolumeType = fi.ValueOf(ig.Spec.RootVolume.Type) + rootVolumeType = ec2types.VolumeType(fi.ValueOf(ig.Spec.RootVolume.Type)) if ig.Spec.RootVolume.Encryption != nil { rootVolumeEncryption = fi.ValueOf(ig.Spec.RootVolume.Encryption) @@ -186,33 +186,37 @@ func (b *AutoscalingGroupModelBuilder) buildLaunchTemplateTask(c *fi.CloudupMode } lt := &awstasks.LaunchTemplate{ - Name: fi.PtrTo(name), - Lifecycle: b.Lifecycle, - CPUCredits: fi.PtrTo(fi.ValueOf(ig.Spec.CPUCredits)), - HTTPPutResponseHopLimit: fi.PtrTo(int64(1)), - HTTPTokens: fi.PtrTo(ec2.LaunchTemplateHttpTokensStateRequired), - HTTPProtocolIPv6: fi.PtrTo(ec2.LaunchTemplateInstanceMetadataProtocolIpv6Disabled), - IAMInstanceProfile: link, - ImageID: fi.PtrTo(ig.Spec.Image), - InstanceInterruptionBehavior: ig.Spec.InstanceInterruptionBehavior, - InstanceMonitoring: fi.PtrTo(false), - IPv6AddressCount: fi.PtrTo(int64(0)), - RootVolumeIops: fi.PtrTo(int64(0)), - RootVolumeSize: fi.PtrTo(int64(rootVolumeSize)), - RootVolumeType: fi.PtrTo(rootVolumeType), - RootVolumeEncryption: fi.PtrTo(rootVolumeEncryption), - RootVolumeKmsKey: fi.PtrTo(rootVolumeKmsKey), - SecurityGroups: securityGroups, - Tags: tags, - UserData: userData, + Name: fi.PtrTo(name), + Lifecycle: b.Lifecycle, + CPUCredits: fi.PtrTo(fi.ValueOf(ig.Spec.CPUCredits)), + HTTPPutResponseHopLimit: fi.PtrTo(int32(1)), + HTTPTokens: fi.PtrTo(ec2types.LaunchTemplateHttpTokensStateRequired), + HTTPProtocolIPv6: fi.PtrTo(ec2types.LaunchTemplateInstanceMetadataProtocolIpv6Disabled), + IAMInstanceProfile: link, + ImageID: fi.PtrTo(ig.Spec.Image), + InstanceMonitoring: fi.PtrTo(false), + IPv6AddressCount: fi.PtrTo(int32(0)), + RootVolumeIops: fi.PtrTo(int32(0)), + RootVolumeSize: fi.PtrTo(int32(rootVolumeSize)), + RootVolumeType: rootVolumeType, + RootVolumeEncryption: fi.PtrTo(rootVolumeEncryption), + RootVolumeKmsKey: fi.PtrTo(rootVolumeKmsKey), + SecurityGroups: securityGroups, + Tags: tags, + UserData: userData, + } + if ig.Spec.InstanceInterruptionBehavior != nil { + lt.InstanceInterruptionBehavior = fi.PtrTo(ec2types.InstanceInterruptionBehavior(fi.ValueOf(ig.Spec.InstanceInterruptionBehavior))) } if ig.Spec.RootVolume != nil { - lt.RootVolumeIops = fi.PtrTo(int64(fi.ValueOf(ig.Spec.RootVolume.IOPS))) + if ig.Spec.RootVolume.IOPS != nil { + lt.RootVolumeIops = fi.PtrTo(int32(fi.ValueOf(ig.Spec.RootVolume.IOPS))) + } lt.RootVolumeOptimization = ig.Spec.RootVolume.Optimization } if ig.Spec.Manager == kops.InstanceManagerCloudGroup { - lt.InstanceType = fi.PtrTo(strings.Split(ig.Spec.MachineType, ",")[0]) + lt.InstanceType = fi.PtrTo(ec2types.InstanceType(strings.Split(ig.Spec.MachineType, ",")[0])) } { @@ -240,8 +244,8 @@ func (b *AutoscalingGroupModelBuilder) buildLaunchTemplateTask(c *fi.CloudupMode continue } if clusterSubnet.IPv6CIDR != "" { - lt.IPv6AddressCount = fi.PtrTo(int64(1)) - lt.HTTPProtocolIPv6 = fi.PtrTo(ec2.LaunchTemplateInstanceMetadataProtocolIpv6Enabled) + lt.IPv6AddressCount = fi.PtrTo(int32(1)) + lt.HTTPProtocolIPv6 = fi.PtrTo(ec2types.LaunchTemplateInstanceMetadataProtocolIpv6Enabled) } } } @@ -251,20 +255,21 @@ func (b *AutoscalingGroupModelBuilder) buildLaunchTemplateTask(c *fi.CloudupMode for i := range ig.Spec.Volumes { x := &ig.Spec.Volumes[i] if x.Type == "" { - x.Type = DefaultVolumeType + x.Type = string(DefaultVolumeType) } - if x.Type == ec2.VolumeTypeIo1 || x.Type == ec2.VolumeTypeIo2 { + switch ec2types.VolumeType(x.Type) { + case ec2types.VolumeTypeIo1, ec2types.VolumeTypeIo2: if x.IOPS == nil { x.IOPS = fi.PtrTo(int64(DefaultVolumeIonIops)) } - } else if x.Type == ec2.VolumeTypeGp3 { + case ec2types.VolumeTypeGp3: if x.IOPS == nil { x.IOPS = fi.PtrTo(int64(DefaultVolumeGp3Iops)) } if x.Throughput == nil { x.Throughput = fi.PtrTo(int64(DefaultVolumeGp3Throughput)) } - } else { + default: x.IOPS = nil } deleteOnTermination := DefaultVolumeDeleteOnTermination @@ -275,16 +280,21 @@ func (b *AutoscalingGroupModelBuilder) buildLaunchTemplateTask(c *fi.CloudupMode if x.Encrypted != nil { encryption = fi.ValueOf(x.Encrypted) } - lt.BlockDeviceMappings = append(lt.BlockDeviceMappings, &awstasks.BlockDeviceMapping{ + bdm := &awstasks.BlockDeviceMapping{ DeviceName: fi.PtrTo(x.Device), EbsDeleteOnTermination: fi.PtrTo(deleteOnTermination), EbsEncrypted: fi.PtrTo(encryption), EbsKmsKey: x.Key, - EbsVolumeIops: x.IOPS, - EbsVolumeSize: fi.PtrTo(x.Size), - EbsVolumeThroughput: x.Throughput, - EbsVolumeType: fi.PtrTo(x.Type), - }) + EbsVolumeSize: fi.PtrTo(int32(x.Size)), + EbsVolumeType: ec2types.VolumeType(x.Type), + } + if x.IOPS != nil { + bdm.EbsVolumeIops = fi.PtrTo(int32(fi.ValueOf(x.IOPS))) + } + if x.Throughput != nil { + bdm.EbsVolumeThroughput = fi.PtrTo(int32(fi.ValueOf(x.Throughput))) + } + lt.BlockDeviceMappings = append(lt.BlockDeviceMappings, bdm) } if ig.Spec.DetailedInstanceMonitoring != nil { @@ -292,27 +302,27 @@ func (b *AutoscalingGroupModelBuilder) buildLaunchTemplateTask(c *fi.CloudupMode } if ig.Spec.InstanceMetadata != nil && ig.Spec.InstanceMetadata.HTTPPutResponseHopLimit != nil { - lt.HTTPPutResponseHopLimit = ig.Spec.InstanceMetadata.HTTPPutResponseHopLimit + lt.HTTPPutResponseHopLimit = fi.PtrTo(int32(fi.ValueOf(ig.Spec.InstanceMetadata.HTTPPutResponseHopLimit))) } if ig.Spec.InstanceMetadata != nil && ig.Spec.InstanceMetadata.HTTPTokens != nil { - lt.HTTPTokens = ig.Spec.InstanceMetadata.HTTPTokens + lt.HTTPTokens = fi.PtrTo(ec2types.LaunchTemplateHttpTokensState(fi.ValueOf(ig.Spec.InstanceMetadata.HTTPTokens))) } else if b.IsKubernetesLT("1.27") { - lt.HTTPTokens = fi.PtrTo(ec2.LaunchTemplateHttpTokensStateOptional) + lt.HTTPTokens = fi.PtrTo(ec2types.LaunchTemplateHttpTokensStateOptional) } - if rootVolumeType == ec2.VolumeTypeIo1 || rootVolumeType == ec2.VolumeTypeIo2 { + if rootVolumeType == ec2types.VolumeTypeIo1 || rootVolumeType == ec2types.VolumeTypeIo2 { if ig.Spec.RootVolume == nil || fi.ValueOf(ig.Spec.RootVolume.IOPS) < 100 { - lt.RootVolumeIops = fi.PtrTo(int64(DefaultVolumeIonIops)) + lt.RootVolumeIops = fi.PtrTo(int32(DefaultVolumeIonIops)) } - } else if rootVolumeType == ec2.VolumeTypeGp3 { + } else if rootVolumeType == ec2types.VolumeTypeGp3 { if ig.Spec.RootVolume == nil || fi.ValueOf(ig.Spec.RootVolume.IOPS) < 3000 { - lt.RootVolumeIops = fi.PtrTo(int64(DefaultVolumeGp3Iops)) + lt.RootVolumeIops = fi.PtrTo(int32(DefaultVolumeGp3Iops)) } if ig.Spec.RootVolume == nil || fi.ValueOf(ig.Spec.RootVolume.Throughput) < 125 { - lt.RootVolumeThroughput = fi.PtrTo(int64(DefaultVolumeGp3Throughput)) + lt.RootVolumeThroughput = fi.PtrTo(int32(DefaultVolumeGp3Throughput)) } else { - lt.RootVolumeThroughput = fi.PtrTo(int64(fi.ValueOf(ig.Spec.RootVolume.Throughput))) + lt.RootVolumeThroughput = fi.PtrTo(int32(fi.ValueOf(ig.Spec.RootVolume.Throughput))) } } else { lt.RootVolumeIops = nil @@ -334,11 +344,11 @@ func (b *AutoscalingGroupModelBuilder) buildLaunchTemplateTask(c *fi.CloudupMode lt.SpotPrice = fi.PtrTo("") } if ig.Spec.SpotDurationInMinutes != nil { - lt.SpotDurationInMinutes = ig.Spec.SpotDurationInMinutes + lt.SpotDurationInMinutes = fi.PtrTo(int32(fi.ValueOf(ig.Spec.SpotDurationInMinutes))) } if ig.Spec.Tenancy != "" { - lt.Tenancy = fi.PtrTo(ig.Spec.Tenancy) + lt.Tenancy = fi.PtrTo(ec2types.Tenancy(ig.Spec.Tenancy)) } return lt, nil diff --git a/pkg/model/master_volumes.go b/pkg/model/master_volumes.go index d2a26112d2abd..38c6f89087452 100644 --- a/pkg/model/master_volumes.go +++ b/pkg/model/master_volumes.go @@ -21,7 +21,7 @@ import ( "sort" "strings" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/scaleway/scaleway-sdk-go/api/instance/v1" "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/apis/kops/model" @@ -44,7 +44,7 @@ import ( const ( DefaultEtcdVolumeSize = 20 - DefaultAWSEtcdVolumeType = ec2.VolumeTypeGp3 + DefaultAWSEtcdVolumeType = "gp3" DefaultAWSEtcdVolumeIonIops = 100 DefaultAWSEtcdVolumeGp3Iops = 3000 DefaultAWSEtcdVolumeGp3Throughput = 125 @@ -138,12 +138,12 @@ func (b *MasterVolumeBuilder) addAWSVolume(c *fi.CloudupModelBuilderContext, nam } volumeIops := fi.ValueOf(m.VolumeIOPS) volumeThroughput := fi.ValueOf(m.VolumeThroughput) - switch volumeType { - case ec2.VolumeTypeIo1, ec2.VolumeTypeIo2: + switch ec2types.VolumeType(volumeType) { + case ec2types.VolumeTypeIo1, ec2types.VolumeTypeIo2: if volumeIops < 100 { volumeIops = DefaultAWSEtcdVolumeIonIops } - case ec2.VolumeTypeGp3: + case ec2types.VolumeTypeGp3: if volumeIops < 3000 { volumeIops = DefaultAWSEtcdVolumeGp3Iops } @@ -181,18 +181,18 @@ func (b *MasterVolumeBuilder) addAWSVolume(c *fi.CloudupModelBuilderContext, nam Lifecycle: b.Lifecycle, AvailabilityZone: fi.PtrTo(zone), - SizeGB: fi.PtrTo(int64(volumeSize)), - VolumeType: fi.PtrTo(volumeType), + SizeGB: fi.PtrTo(int32(volumeSize)), + VolumeType: ec2types.VolumeType(volumeType), KmsKeyId: m.KmsKeyID, Encrypted: fi.PtrTo(encrypted), Tags: tags, } - switch volumeType { - case ec2.VolumeTypeGp3: - t.VolumeThroughput = fi.PtrTo(int64(volumeThroughput)) + switch ec2types.VolumeType(volumeType) { + case ec2types.VolumeTypeGp3: + t.VolumeThroughput = fi.PtrTo(int32(volumeThroughput)) fallthrough - case ec2.VolumeTypeIo1, ec2.VolumeTypeIo2: - t.VolumeIops = fi.PtrTo(int64(volumeIops)) + case ec2types.VolumeTypeIo1, ec2types.VolumeTypeIo2: + t.VolumeIops = fi.PtrTo(int32(volumeIops)) } c.AddTask(t) @@ -203,16 +203,16 @@ func (b *MasterVolumeBuilder) addAWSVolume(c *fi.CloudupModelBuilderContext, nam func validateAWSVolume(name, volumeType string, volumeSize, volumeIops, volumeThroughput int32) error { volumeIopsSizeRatio := float64(volumeIops) / float64(volumeSize) volumeThroughputIopsRatio := float64(volumeThroughput) / float64(volumeIops) - switch volumeType { - case ec2.VolumeTypeIo1: + switch ec2types.VolumeType(volumeType) { + case ec2types.VolumeTypeIo1: if volumeIopsSizeRatio > 50.0 { return fmt.Errorf("volumeIops to volumeSize ratio must be lower than 50. For %s ratio is %.02f", name, volumeIopsSizeRatio) } - case ec2.VolumeTypeIo2: + case ec2types.VolumeTypeIo2: if volumeIopsSizeRatio > 500.0 { return fmt.Errorf("volumeIops to volumeSize ratio must be lower than 500. For %s ratio is %.02f", name, volumeIopsSizeRatio) } - case ec2.VolumeTypeGp3: + case ec2types.VolumeTypeGp3: if volumeIops > 3000 && volumeIopsSizeRatio > 500.0 { return fmt.Errorf("volumeIops to volumeSize ratio must be lower than 500. For %s ratio is %.02f", name, volumeIopsSizeRatio) } diff --git a/pkg/resources/aws/aws.go b/pkg/resources/aws/aws.go index 9371ee716d5a3..c1096839c4096 100644 --- a/pkg/resources/aws/aws.go +++ b/pkg/resources/aws/aws.go @@ -210,11 +210,11 @@ func ListResourcesAWS(cloud awsup.AWSCloud, clusterInfo resources.ClusterInfo) ( return resourceTrackers, nil } -func BuildEC2Filters(cloud fi.Cloud) []*ec2.Filter { +func BuildEC2Filters(cloud fi.Cloud) []ec2types.Filter { awsCloud := cloud.(awsup.AWSCloud) tags := awsCloud.Tags() - var filters []*ec2.Filter + var filters []ec2types.Filter for k, v := range tags { filter := awsup.NewEC2Filter("tag:"+k, v) filters = append(filters, filter) @@ -324,11 +324,12 @@ func matchesIAMTags(tags map[string]string, actual []iamtypes.Tag) bool { } func DeleteInstances(cloud fi.Cloud, t []*resources.Resource) error { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) - var ids []*string + var ids []string for i, instance := range t { - ids = append(ids, &instance.ID) + ids = append(ids, instance.ID) if len(ids) < 100 && i < len(t)-1 { continue } @@ -337,8 +338,8 @@ func DeleteInstances(cloud fi.Cloud, t []*resources.Resource) error { request := &ec2.TerminateInstancesInput{ InstanceIds: ids, } - ids = []*string{} - _, err := c.EC2().TerminateInstances(request) + ids = []string{} + _, err := c.EC2().TerminateInstances(ctx, request) if err != nil { if awsup.AWSErrorCode(err) == "InvalidInstanceID.NotFound" { klog.V(2).Infof("Got InvalidInstanceID.NotFound error terminating instances; will treat as already terminated") @@ -351,27 +352,33 @@ func DeleteInstances(cloud fi.Cloud, t []*resources.Resource) error { } func ListInstances(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resource, error) { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) klog.V(2).Infof("Querying EC2 instances") filters := BuildEC2Filters(cloud) filters = append(filters, awsup.NewEC2Filter("vpc-id", vpcID)) - filters = append(filters, awsup.NewEC2Filter("instance-state-name", ec2.InstanceStateNameRunning)) + filters = append(filters, awsup.NewEC2Filter("instance-state-name", string(ec2types.InstanceStateNameRunning))) request := &ec2.DescribeInstancesInput{ Filters: filters, } var resourceTrackers []*resources.Resource - err := c.EC2().DescribeInstancesPages(request, func(p *ec2.DescribeInstancesOutput, lastPage bool) bool { - for _, reservation := range p.Reservations { + paginator := ec2.NewDescribeInstancesPaginator(c.EC2(), request) + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("error describing instances: %v", err) + } + for _, reservation := range page.Reservations { for _, instance := range reservation.Instances { id := aws.ToString(instance.InstanceId) resourceTracker := &resources.Resource{ Name: FindName(instance.Tags), ID: id, - Type: ec2.ResourceTypeInstance, + Type: string(ec2types.ResourceTypeInstance), GroupDeleter: DeleteInstances, GroupKey: fi.ValueOf(instance.SubnetId), Dumper: DumpInstance, @@ -397,10 +404,6 @@ func ListInstances(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Reso resourceTrackers = append(resourceTrackers, resourceTracker) } } - return true - }) - if err != nil { - return nil, fmt.Errorf("error describing instances: %v", err) } return resourceTrackers, nil @@ -456,7 +459,7 @@ func (s *dumpState) getImageInfo(imageID string) (*imageInfo, error) { return info, nil } -func guessSSHUser(image *ec2.Image) string { +func guessSSHUser(image *ec2types.Image) string { owner := aws.ToString(image.OwnerId) switch owner { case awsup.WellKnownAccountAmazonLinux2, awsup.WellKnownAccountRedhat: @@ -482,11 +485,11 @@ func guessSSHUser(image *ec2.Image) string { func DumpInstance(op *resources.DumpOperation, r *resources.Resource) error { data := make(map[string]interface{}) data["id"] = r.ID - data["type"] = ec2.ResourceTypeInstance + data["type"] = ec2types.ResourceTypeInstance data["raw"] = r.Obj op.Dump.Resources = append(op.Dump.Resources, data) - ec2Instance := r.Obj.(*ec2.Instance) + ec2Instance := r.Obj.(*ec2types.Instance) i := &resources.Instance{ Name: r.ID, } @@ -536,6 +539,7 @@ func DumpInstance(op *resources.DumpOperation, r *resources.Resource) error { } func DeleteVolume(cloud fi.Cloud, r *resources.Resource) error { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) id := r.ID @@ -544,7 +548,7 @@ func DeleteVolume(cloud fi.Cloud, r *resources.Resource) error { request := &ec2.DeleteVolumeInput{ VolumeId: &id, } - _, err := c.EC2().DeleteVolume(request) + _, err := c.EC2().DeleteVolume(ctx, request) if err != nil { if awsup.AWSErrorCode(err) == "InvalidVolume.NotFound" { klog.V(2).Infof("Got InvalidVolume.NotFound error deleting Volume %q; will treat as already-deleted", id) @@ -559,6 +563,7 @@ func DeleteVolume(cloud fi.Cloud, r *resources.Resource) error { } func ListVolumes(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resource, error) { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) volumes, err := DescribeVolumes(cloud) @@ -587,7 +592,7 @@ func ListVolumes(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resour ID: id, Type: "volume", Deleter: DeleteVolume, - Shared: HasSharedTag(ec2.ResourceTypeVolume+":"+id, volume.Tags, clusterName), + Shared: HasSharedTag(string(ec2types.ResourceTypeVolume)+":"+id, volume.Tags, clusterName), } var blocks []string @@ -614,7 +619,7 @@ func ListVolumes(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resour if len(elasticIPs) != 0 { klog.V(2).Infof("Querying EC2 Elastic IPs") request := &ec2.DescribeAddressesInput{} - response, err := c.EC2().DescribeAddresses(request) + response, err := c.EC2().DescribeAddresses(ctx, request) if err != nil { return nil, fmt.Errorf("error describing addresses: %v", err) } @@ -632,28 +637,31 @@ func ListVolumes(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resour return resourceTrackers, nil } -func DescribeVolumes(cloud fi.Cloud) ([]*ec2.Volume, error) { +func DescribeVolumes(cloud fi.Cloud) ([]ec2types.Volume, error) { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) - var volumes []*ec2.Volume + var volumes []ec2types.Volume klog.V(2).Infof("Listing EC2 Volumes") request := &ec2.DescribeVolumesInput{ Filters: BuildEC2Filters(c), } - err := c.EC2().DescribeVolumesPages(request, func(p *ec2.DescribeVolumesOutput, lastPage bool) bool { - volumes = append(volumes, p.Volumes...) - return true - }) - if err != nil { - return nil, fmt.Errorf("error describing volumes: %v", err) + paginator := ec2.NewDescribeVolumesPaginator(c.EC2(), request) + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("error describing volumes: %v", err) + } + volumes = append(volumes, page.Volumes...) } return volumes, nil } func DeleteKeypair(cloud fi.Cloud, r *resources.Resource) error { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) id := r.ID @@ -662,7 +670,7 @@ func DeleteKeypair(cloud fi.Cloud, r *resources.Resource) error { request := &ec2.DeleteKeyPairInput{ KeyPairId: &id, } - _, err := c.EC2().DeleteKeyPair(request) + _, err := c.EC2().DeleteKeyPair(ctx, request) if err != nil { return fmt.Errorf("error deleting KeyPair %q: %v", id, err) } @@ -670,6 +678,7 @@ func DeleteKeypair(cloud fi.Cloud, r *resources.Resource) error { } func ListKeypairs(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resource, error) { + ctx := context.TODO() if !strings.Contains(clusterName, ".") { klog.Infof("cluster %q is legacy (kube-up) cluster; won't delete keypairs", clusterName) return nil, nil @@ -684,7 +693,7 @@ func ListKeypairs(cloud fi.Cloud, vpcID, clusterName string) ([]*resources.Resou // TODO: We need to match both the name and a prefix // TODO: usee 'Filters: []*ec2.Filter{awsup.NewEC2Filter("key-name", keypairName)},' request := &ec2.DescribeKeyPairsInput{} - response, err := c.EC2().DescribeKeyPairs(request) + response, err := c.EC2().DescribeKeyPairs(ctx, request) if err != nil { return nil, fmt.Errorf("error listing KeyPairs: %v", err) } @@ -1275,22 +1284,28 @@ func ListAutoScalingGroups(cloud fi.Cloud, vpcID, clusterName string) ([]*resour // FindAutoScalingLaunchTemplates finds any launch templates owned by the cluster (by tag). func FindAutoScalingLaunchTemplates(cloud fi.Cloud, clusterName string) ([]*resources.Resource, error) { + ctx := context.TODO() c := cloud.(awsup.AWSCloud) klog.V(2).Infof("Finding all AutoScaling LaunchTemplates owned by the cluster") input := &ec2.DescribeLaunchTemplatesInput{ - Filters: []*ec2.Filter{ + Filters: []ec2types.Filter{ { Name: aws.String("tag:kubernetes.io/cluster/" + clusterName), - Values: []*string{aws.String("owned")}, + Values: []string{"owned"}, }, }, } var list []*resources.Resource - err := c.EC2().DescribeLaunchTemplatesPages(input, func(p *ec2.DescribeLaunchTemplatesOutput, lastPage bool) (shouldContinue bool) { - for _, lt := range p.LaunchTemplates { + paginator := ec2.NewDescribeLaunchTemplatesPaginator(c.EC2(), input) + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("error listing AutoScaling LaunchTemplates: %v", err) + } + for _, lt := range page.LaunchTemplates { list = append(list, &resources.Resource{ Name: aws.ToString(lt.LaunchTemplateName), ID: aws.ToString(lt.LaunchTemplateId), @@ -1298,10 +1313,6 @@ func FindAutoScalingLaunchTemplates(cloud fi.Cloud, clusterName string) ([]*reso Deleter: DeleteAutoScalingGroupLaunchTemplate, }) } - return true - }) - if err != nil { - return nil, fmt.Errorf("error listing AutoScaling LaunchTemplates: %v", err) } return list, nil @@ -1411,13 +1422,14 @@ func FindNatGateways(cloud fi.Cloud, routeTables map[string]*resources.Resource, // DeleteAutoScalingGroupLaunchTemplate deletes func DeleteAutoScalingGroupLaunchTemplate(cloud fi.Cloud, r *resources.Resource) error { + ctx := context.TODO() c, ok := cloud.(awsup.AWSCloud) if !ok { return errors.New("expected a aws.Cloud provider") } klog.V(2).Infof("Deleting EC2 LaunchTemplate %q", r.ID) - if _, err := c.EC2().DeleteLaunchTemplate(&ec2.DeleteLaunchTemplateInput{ + if _, err := c.EC2().DeleteLaunchTemplate(ctx, &ec2.DeleteLaunchTemplateInput{ LaunchTemplateId: fi.PtrTo(r.ID), }); err != nil { return fmt.Errorf("error deleting ec2 LaunchTemplate %q: %v", r.ID, err) @@ -2151,7 +2163,7 @@ func ListSpotinstResources(cloud fi.Cloud, vpcID, clusterName string) ([]*resour return spotinst.ListResources(cloud.(awsup.AWSCloud).Spotinst(), clusterName) } -func FindName(tags []*ec2.Tag) string { +func FindName(tags []ec2types.Tag) string { if name, found := awsup.FindEC2Tag(tags, "Name"); found { return name } @@ -2180,16 +2192,16 @@ func FindELBV2Name(tags []elbv2types.Tag) string { } // HasSharedTag looks for the shared tag indicating that the cluster does not own the resource -func HasSharedTag(description string, tags []*ec2.Tag, clusterName string) bool { +func HasSharedTag(description string, tags []ec2types.Tag, clusterName string) bool { tagKey := "kubernetes.io/cluster/" + clusterName - var found *ec2.Tag + var found *ec2types.Tag for _, tag := range tags { if aws.ToString(tag.Key) != tagKey { continue } - found = tag + found = &tag } if found == nil { diff --git a/pkg/resources/aws/aws_test.go b/pkg/resources/aws/aws_test.go index 60cae0a93d147..c28cf43e01454 100644 --- a/pkg/resources/aws/aws_test.go +++ b/pkg/resources/aws/aws_test.go @@ -17,14 +17,16 @@ limitations under the License. package aws import ( + "context" "reflect" "sort" "testing" "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" elbtypes "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" - "github.com/aws/aws-sdk-go/service/ec2" "k8s.io/kops/cloudmock/aws/mockec2" "k8s.io/kops/cloudmock/aws/mockiam" "k8s.io/kops/pkg/resources" @@ -42,16 +44,16 @@ func TestAddUntaggedRouteTables(t *testing.T) { cloud.MockEC2 = c // Matches by vpc id - c.AddRouteTable(&ec2.RouteTable{ + c.AddRouteTable(&ec2types.RouteTable{ VpcId: aws.String("vpc-1234"), RouteTableId: aws.String("rtb-1234"), }) // Skips main route tables - c.AddRouteTable(&ec2.RouteTable{ + c.AddRouteTable(&ec2types.RouteTable{ VpcId: aws.String("vpc-1234"), RouteTableId: aws.String("rtb-1234main"), - Associations: []*ec2.RouteTableAssociation{ + Associations: []ec2types.RouteTableAssociation{ { Main: aws.Bool(true), }, @@ -59,10 +61,10 @@ func TestAddUntaggedRouteTables(t *testing.T) { }) // Skips route table tagged with other cluster - c.AddRouteTable(&ec2.RouteTable{ + c.AddRouteTable(&ec2types.RouteTable{ VpcId: aws.String("vpc-1234"), RouteTableId: aws.String("rtb-1234notmain"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String(awsup.TagClusterName), Value: aws.String("other.example.com"), @@ -71,7 +73,7 @@ func TestAddUntaggedRouteTables(t *testing.T) { }) // Ignores non-matching vpcs - c.AddRouteTable(&ec2.RouteTable{ + c.AddRouteTable(&ec2types.RouteTable{ VpcId: aws.String("vpc-5555"), RouteTableId: aws.String("rtb-5555"), }) @@ -243,10 +245,10 @@ func TestListRouteTables(t *testing.T) { c := &mockec2.MockEC2{} cloud.MockEC2 = c - c.AddRouteTable(&ec2.RouteTable{ + c.AddRouteTable(&ec2types.RouteTable{ VpcId: aws.String("vpc-1234"), RouteTableId: aws.String("rtb-shared"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String("KubernetesCluster"), Value: aws.String(clusterName), @@ -257,10 +259,10 @@ func TestListRouteTables(t *testing.T) { }, }, }) - c.AddRouteTable(&ec2.RouteTable{ + c.AddRouteTable(&ec2types.RouteTable{ VpcId: aws.String("vpc-1234"), RouteTableId: aws.String("rtb-owned"), - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String("KubernetesCluster"), Value: aws.String(clusterName), @@ -287,6 +289,7 @@ func TestListRouteTables(t *testing.T) { } func TestSharedVolume(t *testing.T) { + ctx := context.Background() cloud := awsup.BuildMockAWSCloud("us-east-1", "abc") clusterName := "me.example.com" ownershipTagKey := "kubernetes.io/cluster/" + clusterName @@ -294,11 +297,11 @@ func TestSharedVolume(t *testing.T) { c := &mockec2.MockEC2{} cloud.MockEC2 = c - sharedVolume, err := c.CreateVolume(&ec2.CreateVolumeInput{ - TagSpecifications: []*ec2.TagSpecification{ + sharedVolume, err := c.CreateVolume(ctx, &ec2.CreateVolumeInput{ + TagSpecifications: []ec2types.TagSpecification{ { - ResourceType: aws.String(ec2.ResourceTypeVolume), - Tags: []*ec2.Tag{ + ResourceType: ec2types.ResourceTypeVolume, + Tags: []ec2types.Tag{ { Key: aws.String(ownershipTagKey), Value: aws.String("shared"), @@ -311,10 +314,10 @@ func TestSharedVolume(t *testing.T) { t.Fatalf("error creating volume: %v", err) } - ownedVolume, err := c.CreateVolume(&ec2.CreateVolumeInput{ - TagSpecifications: []*ec2.TagSpecification{ + ownedVolume, err := c.CreateVolume(ctx, &ec2.CreateVolumeInput{ + TagSpecifications: []ec2types.TagSpecification{ { - Tags: []*ec2.Tag{ + Tags: []ec2types.Tag{ { Key: aws.String(ownershipTagKey), Value: aws.String("owned"), diff --git a/pkg/resources/aws/filters.go b/pkg/resources/aws/filters.go index 4a2b0c51474cd..875c07260731f 100644 --- a/pkg/resources/aws/filters.go +++ b/pkg/resources/aws/filters.go @@ -18,22 +18,22 @@ package aws import ( "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" ) // buildEc2FiltersForCluster returns the set of filters we must use to find all resources -func buildEC2FiltersForCluster(clusterName string) [][]*ec2.Filter { - var filterSets [][]*ec2.Filter +func buildEC2FiltersForCluster(clusterName string) [][]ec2types.Filter { + var filterSets [][]ec2types.Filter // TODO: We could look for tag-key on the old & new tags, and then post-filter (we do this in k/k cloudprovider) - filterSets = append(filterSets, []*ec2.Filter{ - {Name: aws.String("tag:" + awsup.TagClusterName), Values: aws.StringSlice([]string{clusterName})}, + filterSets = append(filterSets, []ec2types.Filter{ + {Name: aws.String("tag:" + awsup.TagClusterName), Values: []string{clusterName}}, }) - filterSets = append(filterSets, []*ec2.Filter{ - {Name: aws.String("tag-key"), Values: aws.StringSlice([]string{"kubernetes.io/cluster/" + clusterName})}, + filterSets = append(filterSets, []ec2types.Filter{ + {Name: aws.String("tag-key"), Values: []string{"kubernetes.io/cluster/" + clusterName}}, }) return filterSets diff --git a/pkg/resources/aws/tags.go b/pkg/resources/aws/tags.go index 69cd379108979..4e3b0d915e3af 100644 --- a/pkg/resources/aws/tags.go +++ b/pkg/resources/aws/tags.go @@ -18,22 +18,22 @@ package aws import ( "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" ) // HasOwnedTag looks for the new tag indicating that the cluster does owns the resource, or the legacy tag -func HasOwnedTag(description string, tags []*ec2.Tag, clusterName string) bool { +func HasOwnedTag(description string, tags []ec2types.Tag, clusterName string) bool { tagKey := "kubernetes.io/cluster/" + clusterName - var found *ec2.Tag + var found *ec2types.Tag for _, tag := range tags { if aws.ToString(tag.Key) != tagKey { continue } - found = tag + found = &tag } if found != nil { @@ -56,7 +56,7 @@ func HasOwnedTag(description string, tags []*ec2.Tag, clusterName string) bool { continue } - found = tag + found = &tag } if found != nil { diff --git a/upup/pkg/fi/cloudup/awstasks/block_device_mappings.go b/upup/pkg/fi/cloudup/awstasks/block_device_mappings.go index ce4b9b4874901..54de981a03569 100644 --- a/upup/pkg/fi/cloudup/awstasks/block_device_mappings.go +++ b/upup/pkg/fi/cloudup/awstasks/block_device_mappings.go @@ -20,8 +20,8 @@ import ( "k8s.io/kops/upup/pkg/fi" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/autoscaling" - "github.com/aws/aws-sdk-go/service/ec2" + autoscalingtypes "github.com/aws/aws-sdk-go-v2/service/autoscaling/types" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" ) // BlockDeviceMapping defines the specification for a device mapping @@ -35,19 +35,19 @@ type BlockDeviceMapping struct { // EbsKmsKey is the encryption key identifier for the volume EbsKmsKey *string // EbsVolumeIops is the provisioned iops for the volume - EbsVolumeIops *int64 + EbsVolumeIops *int32 // EbsVolumeThroughput is the throughput for the volume - EbsVolumeThroughput *int64 + EbsVolumeThroughput *int32 // EbsVolumeSize is the size of the volume - EbsVolumeSize *int64 + EbsVolumeSize *int32 // EbsVolumeType is the aws volume type - EbsVolumeType *string + EbsVolumeType ec2types.VolumeType // VirtualName is the device name VirtualName *string } // BlockDeviceMappingFromEC2 converts a e2c block mapping to internal block device mapping -func BlockDeviceMappingFromEC2(i *ec2.BlockDeviceMapping) (string, *BlockDeviceMapping) { +func BlockDeviceMappingFromEC2(i ec2types.BlockDeviceMapping) (string, *BlockDeviceMapping) { o := &BlockDeviceMapping{ DeviceName: i.DeviceName, VirtualName: i.VirtualName, @@ -66,23 +66,23 @@ func BlockDeviceMappingFromEC2(i *ec2.BlockDeviceMapping) (string, *BlockDeviceM } // ToEC2 creates and returns an ec2 block mapping -func (i *BlockDeviceMapping) ToEC2(deviceName string) *ec2.BlockDeviceMapping { - o := &ec2.BlockDeviceMapping{ +func (i *BlockDeviceMapping) ToEC2(deviceName string) ec2types.BlockDeviceMapping { + o := ec2types.BlockDeviceMapping{ DeviceName: aws.String(deviceName), VirtualName: i.VirtualName, } - if i.EbsDeleteOnTermination != nil || i.EbsVolumeSize != nil || i.EbsVolumeType != nil || i.EbsEncrypted != nil { - o.Ebs = &ec2.EbsBlockDevice{ + if i.EbsDeleteOnTermination != nil || i.EbsVolumeSize != nil || len(i.EbsVolumeType) > 0 || i.EbsEncrypted != nil { + o.Ebs = &ec2types.EbsBlockDevice{ DeleteOnTermination: i.EbsDeleteOnTermination, Encrypted: i.EbsEncrypted, VolumeSize: i.EbsVolumeSize, VolumeType: i.EbsVolumeType, } - switch fi.ValueOf(i.EbsVolumeType) { - case ec2.VolumeTypeGp3: + switch i.EbsVolumeType { + case ec2types.VolumeTypeGp3: o.Ebs.Throughput = i.EbsVolumeThroughput fallthrough - case ec2.VolumeTypeIo1, ec2.VolumeTypeIo2: + case ec2types.VolumeTypeIo1, ec2types.VolumeTypeIo2: o.Ebs.Iops = i.EbsVolumeIops } if fi.ValueOf(o.Ebs.Encrypted) { @@ -94,7 +94,7 @@ func (i *BlockDeviceMapping) ToEC2(deviceName string) *ec2.BlockDeviceMapping { } // BlockDeviceMappingFromAutoscaling converts an autoscaling block mapping to internal spec -func BlockDeviceMappingFromAutoscaling(i *autoscaling.BlockDeviceMapping) (string, *BlockDeviceMapping) { +func BlockDeviceMappingFromAutoscaling(i *autoscalingtypes.BlockDeviceMapping) (string, *BlockDeviceMapping) { o := &BlockDeviceMapping{ DeviceName: i.DeviceName, VirtualName: i.VirtualName, @@ -103,9 +103,9 @@ func BlockDeviceMappingFromAutoscaling(i *autoscaling.BlockDeviceMapping) (strin o.EbsDeleteOnTermination = i.Ebs.DeleteOnTermination o.EbsEncrypted = i.Ebs.Encrypted o.EbsVolumeSize = i.Ebs.VolumeSize - o.EbsVolumeType = i.Ebs.VolumeType + o.EbsVolumeType = ec2types.VolumeType(fi.ValueOf(i.Ebs.VolumeType)) - if fi.ValueOf(o.EbsVolumeType) == ec2.VolumeTypeIo1 || fi.ValueOf(o.EbsVolumeType) == ec2.VolumeTypeIo2 { + if o.EbsVolumeType == ec2types.VolumeTypeIo1 || o.EbsVolumeType == ec2types.VolumeTypeIo2 { o.EbsVolumeIops = i.Ebs.Iops } } @@ -114,19 +114,19 @@ func BlockDeviceMappingFromAutoscaling(i *autoscaling.BlockDeviceMapping) (strin } // ToAutoscaling converts the internal block mapping to autoscaling -func (i *BlockDeviceMapping) ToAutoscaling(deviceName string) *autoscaling.BlockDeviceMapping { - o := &autoscaling.BlockDeviceMapping{ +func (i *BlockDeviceMapping) ToAutoscaling(deviceName string) *autoscalingtypes.BlockDeviceMapping { + o := &autoscalingtypes.BlockDeviceMapping{ DeviceName: aws.String(deviceName), VirtualName: i.VirtualName, } - if i.EbsDeleteOnTermination != nil || i.EbsVolumeSize != nil || i.EbsVolumeType != nil { - o.Ebs = &autoscaling.Ebs{ + if i.EbsDeleteOnTermination != nil || i.EbsVolumeSize != nil || len(i.EbsVolumeType) > 0 { + o.Ebs = &autoscalingtypes.Ebs{ DeleteOnTermination: i.EbsDeleteOnTermination, Encrypted: i.EbsEncrypted, VolumeSize: i.EbsVolumeSize, - VolumeType: i.EbsVolumeType, + VolumeType: fi.PtrTo(string(i.EbsVolumeType)), } - if fi.ValueOf(o.Ebs.VolumeType) == ec2.VolumeTypeIo1 || fi.ValueOf(o.Ebs.VolumeType) == ec2.VolumeTypeIo2 { + if ec2types.VolumeType(fi.ValueOf(o.Ebs.VolumeType)) == ec2types.VolumeTypeIo1 || ec2types.VolumeType(fi.ValueOf(o.Ebs.VolumeType)) == ec2types.VolumeTypeIo2 { o.Ebs.Iops = i.EbsVolumeIops } } @@ -135,7 +135,7 @@ func (i *BlockDeviceMapping) ToAutoscaling(deviceName string) *autoscaling.Block } // BlockDeviceMappingFromLaunchTemplateBootDeviceRequest coverts the launch template device mappings to an interval block device mapping -func BlockDeviceMappingFromLaunchTemplateBootDeviceRequest(i *ec2.LaunchTemplateBlockDeviceMapping) (string, *BlockDeviceMapping) { +func BlockDeviceMappingFromLaunchTemplateBootDeviceRequest(i ec2types.LaunchTemplateBlockDeviceMapping) (string, *BlockDeviceMapping) { o := &BlockDeviceMapping{ DeviceName: i.DeviceName, VirtualName: i.VirtualName, @@ -154,24 +154,24 @@ func BlockDeviceMappingFromLaunchTemplateBootDeviceRequest(i *ec2.LaunchTemplate } // ToLaunchTemplateBootDeviceRequest coverts in the internal block device mapping to a launch template request -func (i *BlockDeviceMapping) ToLaunchTemplateBootDeviceRequest(deviceName string) *ec2.LaunchTemplateBlockDeviceMappingRequest { - o := &ec2.LaunchTemplateBlockDeviceMappingRequest{ +func (i *BlockDeviceMapping) ToLaunchTemplateBootDeviceRequest(deviceName string) ec2types.LaunchTemplateBlockDeviceMappingRequest { + o := ec2types.LaunchTemplateBlockDeviceMappingRequest{ DeviceName: aws.String(deviceName), VirtualName: i.VirtualName, } - if i.EbsDeleteOnTermination != nil || i.EbsVolumeSize != nil || i.EbsVolumeType != nil || i.EbsEncrypted != nil { - o.Ebs = &ec2.LaunchTemplateEbsBlockDeviceRequest{ + if i.EbsDeleteOnTermination != nil || i.EbsVolumeSize != nil || len(i.EbsVolumeType) > 0 || i.EbsEncrypted != nil { + o.Ebs = &ec2types.LaunchTemplateEbsBlockDeviceRequest{ DeleteOnTermination: i.EbsDeleteOnTermination, Encrypted: i.EbsEncrypted, VolumeSize: i.EbsVolumeSize, VolumeType: i.EbsVolumeType, } } - switch fi.ValueOf(i.EbsVolumeType) { - case ec2.VolumeTypeGp3: + switch i.EbsVolumeType { + case ec2types.VolumeTypeGp3: o.Ebs.Throughput = i.EbsVolumeThroughput fallthrough - case ec2.VolumeTypeIo1, ec2.VolumeTypeIo2: + case ec2types.VolumeTypeIo1, ec2types.VolumeTypeIo2: o.Ebs.Iops = i.EbsVolumeIops } if fi.ValueOf(i.EbsEncrypted) { diff --git a/upup/pkg/fi/cloudup/awstasks/classic_load_balancer.go b/upup/pkg/fi/cloudup/awstasks/classic_load_balancer.go index a8476cc7c9c21..7534818ff8286 100644 --- a/upup/pkg/fi/cloudup/awstasks/classic_load_balancer.go +++ b/upup/pkg/fi/cloudup/awstasks/classic_load_balancer.go @@ -27,7 +27,6 @@ import ( elb "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing" elbtypes "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" route53types "github.com/aws/aws-sdk-go-v2/service/route53/types" - "github.com/aws/aws-sdk-go/aws/awserr" "k8s.io/klog/v2" "k8s.io/kops/pkg/wellknownservices" "k8s.io/kops/upup/pkg/fi" @@ -131,10 +130,8 @@ func findLoadBalancerByLoadBalancerName(ctx context.Context, cloud awsup.AWSClou return false }) if err != nil { - if awsError, ok := err.(awserr.Error); ok { - if awsError.Code() == "LoadBalancerNotFound" { - return nil, nil - } + if awsup.AWSErrorCode(err) == "LoadBalancerNotFound" { + return nil, nil } return nil, fmt.Errorf("error listing ELBs: %v", err) diff --git a/upup/pkg/fi/cloudup/awstasks/ebsvolume.go b/upup/pkg/fi/cloudup/awstasks/ebsvolume.go index cb75c64b69092..dbfb54a0c2954 100644 --- a/upup/pkg/fi/cloudup/awstasks/ebsvolume.go +++ b/upup/pkg/fi/cloudup/awstasks/ebsvolume.go @@ -17,6 +17,7 @@ limitations under the License. package awstasks import ( + "context" "fmt" "os" @@ -25,7 +26,8 @@ import ( "k8s.io/kops/upup/pkg/fi/cloudup/terraform" "k8s.io/kops/upup/pkg/fi/cloudup/terraformWriter" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) @@ -38,11 +40,11 @@ type EBSVolume struct { Encrypted *bool ID *string KmsKeyId *string - SizeGB *int64 + SizeGB *int32 Tags map[string]string - VolumeIops *int64 - VolumeThroughput *int64 - VolumeType *string + VolumeIops *int32 + VolumeThroughput *int32 + VolumeType ec2types.VolumeType } var _ fi.CompareWithID = &EBSVolume{} @@ -52,15 +54,16 @@ func (e *EBSVolume) CompareWithID() *string { return e.ID } -func (e *EBSVolume) Find(context *fi.CloudupContext) (*EBSVolume, error) { - cloud := context.T.Cloud.(awsup.AWSCloud) +func (e *EBSVolume) Find(c *fi.CloudupContext) (*EBSVolume, error) { + ctx := c.Context() + cloud := c.T.Cloud.(awsup.AWSCloud) filters := cloud.BuildFilters(e.Name) request := &ec2.DescribeVolumesInput{ Filters: filters, } - response, err := cloud.EC2().DescribeVolumes(request) + response, err := cloud.EC2().DescribeVolumes(ctx, request) if err != nil { return nil, fmt.Errorf("error listing volumes: %v", err) } @@ -131,6 +134,7 @@ func (_ *EBSVolume) CheckChanges(a, e, changes *EBSVolume) error { } func (_ *EBSVolume) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *EBSVolume) error { + ctx := context.TODO() if a == nil { klog.V(2).Infof("Creating PersistentVolume with Name:%q", *e.Name) @@ -142,10 +146,10 @@ func (_ *EBSVolume) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *EBSVolume) e Encrypted: e.Encrypted, Iops: e.VolumeIops, Throughput: e.VolumeThroughput, - TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeVolume, e.Tags), + TagSpecifications: awsup.EC2TagSpecification(ec2types.ResourceTypeVolume, e.Tags), } - response, err := t.Cloud.EC2().CreateVolume(request) + response, err := t.Cloud.EC2().CreateVolume(ctx, request) if err != nil { return fmt.Errorf("error creating PersistentVolume: %v", err) } @@ -165,7 +169,7 @@ func (_ *EBSVolume) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *EBSVolume) e } } - if changes.VolumeType != nil || + if len(changes.VolumeType) > 0 || changes.VolumeIops != nil || changes.VolumeThroughput != nil || changes.SizeGB != nil { @@ -178,7 +182,7 @@ func (_ *EBSVolume) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *EBSVolume) e Size: e.SizeGB, } - _, err := t.Cloud.EC2().ModifyVolume(request) + _, err := t.Cloud.EC2().ModifyVolume(ctx, request) if err != nil { return fmt.Errorf("error modifying volume: %v", err) } @@ -201,14 +205,14 @@ func (e *EBSVolume) getEBSVolumeTagsToDelete(currentTags map[string]string) map[ } type terraformVolume struct { - AvailabilityZone *string `cty:"availability_zone"` - Size *int64 `cty:"size"` - Type *string `cty:"type"` - Iops *int64 `cty:"iops"` - Throughput *int64 `cty:"throughput"` - KmsKeyId *string `cty:"kms_key_id"` - Encrypted *bool `cty:"encrypted"` - Tags map[string]string `cty:"tags"` + AvailabilityZone *string `cty:"availability_zone"` + Size *int32 `cty:"size"` + Type ec2types.VolumeType `cty:"type"` + Iops *int32 `cty:"iops"` + Throughput *int32 `cty:"throughput"` + KmsKeyId *string `cty:"kms_key_id"` + Encrypted *bool `cty:"encrypted"` + Tags map[string]string `cty:"tags"` } func (_ *EBSVolume) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *EBSVolume) error { diff --git a/upup/pkg/fi/cloudup/awstasks/helper.go b/upup/pkg/fi/cloudup/awstasks/helper.go index c87ad86d2ec56..308b96d59312d 100644 --- a/upup/pkg/fi/cloudup/awstasks/helper.go +++ b/upup/pkg/fi/cloudup/awstasks/helper.go @@ -20,12 +20,13 @@ import ( "errors" "fmt" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" ) // buildEphemeralDevices looks up the machine type and discovery any ephemeral device mappings -func buildEphemeralDevices(cloud awsup.AWSCloud, machineType string) (map[string]*BlockDeviceMapping, error) { +func buildEphemeralDevices(cloud awsup.AWSCloud, machineType ec2types.InstanceType) (map[string]*BlockDeviceMapping, error) { if machineType == "" { return nil, nil } diff --git a/upup/pkg/fi/cloudup/awstasks/instance.go b/upup/pkg/fi/cloudup/awstasks/instance.go index 24fc32ff20b32..3178a7c43e2bd 100644 --- a/upup/pkg/fi/cloudup/awstasks/instance.go +++ b/upup/pkg/fi/cloudup/awstasks/instance.go @@ -17,12 +17,14 @@ limitations under the License. package awstasks import ( + "context" "encoding/base64" "fmt" "strings" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" @@ -49,7 +51,7 @@ type Instance struct { Shared *bool ImageID *string - InstanceType *string + InstanceType ec2types.InstanceType SSHKey *SSHKey SecurityGroups []*SecurityGroup AssociatePublicIP *bool @@ -63,12 +65,13 @@ func (s *Instance) CompareWithID() *string { } func (e *Instance) Find(c *fi.CloudupContext) (*Instance, error) { + ctx := c.Context() cloud := c.T.Cloud.(awsup.AWSCloud) var request *ec2.DescribeInstancesInput if fi.ValueOf(e.Shared) { - var instanceIds []*string - instanceIds = append(instanceIds, e.ID) + var instanceIds []string + instanceIds = append(instanceIds, aws.ToString(e.ID)) request = &ec2.DescribeInstancesInput{ InstanceIds: instanceIds, } @@ -80,12 +83,12 @@ func (e *Instance) Find(c *fi.CloudupContext) (*Instance, error) { } } - response, err := cloud.EC2().DescribeInstances(request) + response, err := cloud.EC2().DescribeInstances(ctx, request) if err != nil { return nil, fmt.Errorf("error listing instances: %v", err) } - instances := []*ec2.Instance{} + instances := []ec2types.Instance{} if response != nil { for _, reservation := range response.Reservations { instances = append(instances, reservation.Instances...) @@ -119,8 +122,8 @@ func (e *Instance) Find(c *fi.CloudupContext) (*Instance, error) { { request := &ec2.DescribeInstanceAttributeInput{} request.InstanceId = i.InstanceId - request.Attribute = aws.String("userData") - response, err := cloud.EC2().DescribeInstanceAttribute(request) + request.Attribute = ec2types.InstanceAttributeNameUserData + response, err := cloud.EC2().DescribeInstanceAttribute(ctx, request) if err != nil { return nil, fmt.Errorf("error querying EC2 for user metadata for instance %q: %v", *i.InstanceId, err) } @@ -208,6 +211,7 @@ func (_ *Instance) CheckChanges(a, e, changes *Instance) error { } func (_ *Instance) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Instance) error { + ctx := context.TODO() if a == nil { if fi.ValueOf(e.Shared) { @@ -226,21 +230,21 @@ func (_ *Instance) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Instance) err request := &ec2.RunInstancesInput{ ImageId: image.ImageId, InstanceType: e.InstanceType, - MinCount: aws.Int64(1), - MaxCount: aws.Int64(1), + MinCount: aws.Int32(1), + MaxCount: aws.Int32(1), } if e.SSHKey != nil { request.KeyName = e.SSHKey.Name } - securityGroupIDs := []*string{} + securityGroupIDs := []string{} for _, sg := range e.SecurityGroups { - securityGroupIDs = append(securityGroupIDs, sg.ID) + securityGroupIDs = append(securityGroupIDs, fi.ValueOf(sg.ID)) } - request.NetworkInterfaces = []*ec2.InstanceNetworkInterfaceSpecification{ + request.NetworkInterfaces = []ec2types.InstanceNetworkInterfaceSpecification{ { - DeviceIndex: aws.Int64(0), + DeviceIndex: aws.Int32(0), AssociatePublicIpAddress: e.AssociatePublicIP, SubnetId: e.Subnet.ID, PrivateIpAddress: e.PrivateIPAddress, @@ -250,13 +254,13 @@ func (_ *Instance) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Instance) err // Build up the actual block device mappings // TODO: Support RootVolumeType & RootVolumeSize (see launchconfiguration) - blockDeviceMappings, err := buildEphemeralDevices(t.Cloud, fi.ValueOf(e.InstanceType)) + blockDeviceMappings, err := buildEphemeralDevices(t.Cloud, e.InstanceType) if err != nil { return err } if len(blockDeviceMappings) != 0 { - request.BlockDeviceMappings = []*ec2.BlockDeviceMapping{} + request.BlockDeviceMappings = []ec2types.BlockDeviceMapping{} for deviceName, bdm := range blockDeviceMappings { request.BlockDeviceMappings = append(request.BlockDeviceMappings, bdm.ToEC2(deviceName)) } @@ -280,12 +284,12 @@ func (_ *Instance) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *Instance) err } if e.IAMInstanceProfile != nil { - request.IamInstanceProfile = &ec2.IamInstanceProfileSpecification{ + request.IamInstanceProfile = &ec2types.IamInstanceProfileSpecification{ Name: e.IAMInstanceProfile.Name, } } - response, err := t.Cloud.EC2().RunInstances(request) + response, err := t.Cloud.EC2().RunInstances(ctx, request) if err != nil { return fmt.Errorf("error creating Instance: %v", err) } diff --git a/upup/pkg/fi/cloudup/awstasks/launchtemplate.go b/upup/pkg/fi/cloudup/awstasks/launchtemplate.go index 60863795ce689..6e751c0bb3a13 100644 --- a/upup/pkg/fi/cloudup/awstasks/launchtemplate.go +++ b/upup/pkg/fi/cloudup/awstasks/launchtemplate.go @@ -21,6 +21,7 @@ import ( "sort" "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" ) @@ -42,34 +43,34 @@ type LaunchTemplate struct { // CPUCredits is the credit option for CPU Usage on some instance types CPUCredits *string // HTTPPutResponseHopLimit is the desired HTTP PUT response hop limit for instance metadata requests. - HTTPPutResponseHopLimit *int64 + HTTPPutResponseHopLimit *int32 // HTTPTokens is the state of token usage for your instance metadata requests. - HTTPTokens *string + HTTPTokens *ec2types.LaunchTemplateHttpTokensState // HTTPProtocolIPv6 enables the IPv6 instance metadata endpoint - HTTPProtocolIPv6 *string + HTTPProtocolIPv6 *ec2types.LaunchTemplateInstanceMetadataProtocolIpv6 // IAMInstanceProfile is the IAM profile to assign to the nodes IAMInstanceProfile *IAMInstanceProfile // ImageID is the AMI to use for the instances ImageID *string // InstanceInterruptionBehavior defines if a spot instance should be terminated, hibernated, // or stopped after interruption - InstanceInterruptionBehavior *string + InstanceInterruptionBehavior *ec2types.InstanceInterruptionBehavior // InstanceMonitoring indicates if monitoring is enabled InstanceMonitoring *bool // InstanceType is the type of instance we are using - InstanceType *string + InstanceType *ec2types.InstanceType // Ipv6AddressCount is the number of IPv6 addresses to assign with the primary network interface. - IPv6AddressCount *int64 + IPv6AddressCount *int32 // RootVolumeIops is the provisioned IOPS when the volume type is io1, io2 or gp3 - RootVolumeIops *int64 + RootVolumeIops *int32 // RootVolumeOptimization enables EBS optimization for an instance RootVolumeOptimization *bool // RootVolumeSize is the size of the EBS root volume to use, in GB - RootVolumeSize *int64 + RootVolumeSize *int32 // RootVolumeThroughput is the volume throughput in MBps when the volume type is gp3 - RootVolumeThroughput *int64 + RootVolumeThroughput *int32 // RootVolumeType is the type of the EBS root volume to use (e.g. gp2) - RootVolumeType *string + RootVolumeType ec2types.VolumeType // RootVolumeEncryption enables EBS root volume encryption for an instance RootVolumeEncryption *bool // RootVolumeKmsKey is the encryption key identifier for EBS root volume encryption @@ -81,11 +82,11 @@ type LaunchTemplate struct { // SpotPrice is set to the spot-price bid if this is a spot pricing request SpotPrice *string // SpotDurationInMinutes is set for requesting spot blocks - SpotDurationInMinutes *int64 + SpotDurationInMinutes *int32 // Tags are the keypairs to apply to the instance and volume on launch as well as the launch template itself. Tags map[string]string // Tenancy. Can be either default or dedicated. - Tenancy *string + Tenancy *ec2types.Tenancy // UserData is the user data configuration UserData fi.Resource } @@ -171,7 +172,7 @@ func (t *LaunchTemplate) FindDeletions(c *fi.CloudupContext) ([]fi.CloudupDeleti for _, lt := range list { if aws.ToString(lt.LaunchTemplateName) != aws.ToString(t.Name) { - removals = append(removals, &deleteLaunchTemplate{lc: lt}) + removals = append(removals, &deleteLaunchTemplate{lc: fi.PtrTo(lt)}) } } diff --git a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_api.go b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_api.go index 15bcd8556ee48..c8ed8787529d3 100644 --- a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_api.go +++ b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_api.go @@ -17,13 +17,15 @@ limitations under the License. package awstasks import ( + "context" "encoding/base64" "fmt" "sort" "strconv" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi" @@ -32,6 +34,7 @@ import ( // RenderAWS is responsible for performing creating / updating the launch template func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, e, changes *LaunchTemplate) error { + ctx := context.TODO() // @step: resolve the image id to an AMI for us image, err := c.Cloud.ResolveImage(fi.ValueOf(t.ImageID)) if err != nil { @@ -39,21 +42,21 @@ func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, e, changes *LaunchT } // @step: lets build the launch template data - data := &ec2.RequestLaunchTemplateData{ + data := &ec2types.RequestLaunchTemplateData{ DisableApiTermination: fi.PtrTo(false), EbsOptimized: t.RootVolumeOptimization, ImageId: image.ImageId, - InstanceType: t.InstanceType, - MetadataOptions: &ec2.LaunchTemplateInstanceMetadataOptionsRequest{ + InstanceType: fi.ValueOf(t.InstanceType), + MetadataOptions: &ec2types.LaunchTemplateInstanceMetadataOptionsRequest{ HttpPutResponseHopLimit: t.HTTPPutResponseHopLimit, - HttpTokens: t.HTTPTokens, - HttpProtocolIpv6: t.HTTPProtocolIPv6, + HttpTokens: fi.ValueOf(t.HTTPTokens), + HttpProtocolIpv6: fi.ValueOf(t.HTTPProtocolIPv6), }, - NetworkInterfaces: []*ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{ + NetworkInterfaces: []ec2types.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{ { AssociatePublicIpAddress: t.AssociatePublicIP, DeleteOnTermination: aws.Bool(true), - DeviceIndex: fi.PtrTo(int64(0)), + DeviceIndex: fi.PtrTo(int32(0)), Ipv6AddressCount: t.IPv6AddressCount, }, }, @@ -84,38 +87,38 @@ func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, e, changes *LaunchT } // @step: add the security groups for _, sg := range t.SecurityGroups { - data.NetworkInterfaces[0].Groups = append(data.NetworkInterfaces[0].Groups, sg.ID) + data.NetworkInterfaces[0].Groups = append(data.NetworkInterfaces[0].Groups, fi.ValueOf(sg.ID)) } // @step: add any tenancy details if t.Tenancy != nil { - data.Placement = &ec2.LaunchTemplatePlacementRequest{Tenancy: t.Tenancy} + data.Placement = &ec2types.LaunchTemplatePlacementRequest{Tenancy: fi.ValueOf(t.Tenancy)} } // @step: set the instance monitoring - data.Monitoring = &ec2.LaunchTemplatesMonitoringRequest{Enabled: fi.PtrTo(false)} + data.Monitoring = &ec2types.LaunchTemplatesMonitoringRequest{Enabled: fi.PtrTo(false)} if t.InstanceMonitoring != nil { - data.Monitoring = &ec2.LaunchTemplatesMonitoringRequest{Enabled: t.InstanceMonitoring} + data.Monitoring = &ec2types.LaunchTemplatesMonitoringRequest{Enabled: t.InstanceMonitoring} } // @step: add the iam instance profile if t.IAMInstanceProfile != nil { - data.IamInstanceProfile = &ec2.LaunchTemplateIamInstanceProfileSpecificationRequest{ + data.IamInstanceProfile = &ec2types.LaunchTemplateIamInstanceProfileSpecificationRequest{ Name: t.IAMInstanceProfile.Name, } } // @step: add the tags - var tags []*ec2.Tag + var tags []ec2types.Tag if len(t.Tags) > 0 { for k, v := range t.Tags { - tags = append(tags, &ec2.Tag{ + tags = append(tags, ec2types.Tag{ Key: aws.String(k), Value: aws.String(v), }) } - data.TagSpecifications = append(data.TagSpecifications, &ec2.LaunchTemplateTagSpecificationRequest{ - ResourceType: aws.String(ec2.ResourceTypeInstance), + data.TagSpecifications = append(data.TagSpecifications, ec2types.LaunchTemplateTagSpecificationRequest{ + ResourceType: ec2types.ResourceTypeInstance, Tags: tags, }) - data.TagSpecifications = append(data.TagSpecifications, &ec2.LaunchTemplateTagSpecificationRequest{ - ResourceType: aws.String(ec2.ResourceTypeVolume), + data.TagSpecifications = append(data.TagSpecifications, ec2types.LaunchTemplateTagSpecificationRequest{ + ResourceType: ec2types.ResourceTypeVolume, Tags: tags, }) } @@ -129,18 +132,20 @@ func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, e, changes *LaunchT } // @step: add market options if fi.ValueOf(t.SpotPrice) != "" { - s := &ec2.LaunchTemplateSpotMarketOptionsRequest{ - BlockDurationMinutes: t.SpotDurationInMinutes, - InstanceInterruptionBehavior: t.InstanceInterruptionBehavior, - MaxPrice: t.SpotPrice, + s := &ec2types.LaunchTemplateSpotMarketOptionsRequest{ + BlockDurationMinutes: t.SpotDurationInMinutes, + MaxPrice: t.SpotPrice, } - data.InstanceMarketOptions = &ec2.LaunchTemplateInstanceMarketOptionsRequest{ - MarketType: fi.PtrTo("spot"), + if t.InstanceInterruptionBehavior != nil { + s.InstanceInterruptionBehavior = fi.ValueOf(t.InstanceInterruptionBehavior) + } + data.InstanceMarketOptions = &ec2types.LaunchTemplateInstanceMarketOptionsRequest{ + MarketType: ec2types.MarketTypeSpot, SpotOptions: s, } } if fi.ValueOf(t.CPUCredits) != "" { - data.CreditSpecification = &ec2.CreditSpecificationRequest{ + data.CreditSpecification = &ec2types.CreditSpecificationRequest{ CpuCredits: t.CPUCredits, } } @@ -149,14 +154,14 @@ func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, e, changes *LaunchT input := &ec2.CreateLaunchTemplateInput{ LaunchTemplateName: t.Name, LaunchTemplateData: data, - TagSpecifications: []*ec2.TagSpecification{ + TagSpecifications: []ec2types.TagSpecification{ { - ResourceType: aws.String(ec2.ResourceTypeLaunchTemplate), + ResourceType: ec2types.ResourceTypeLaunchTemplate, Tags: tags, }, }, } - output, err := c.Cloud.EC2().CreateLaunchTemplate(input) + output, err := c.Cloud.EC2().CreateLaunchTemplate(ctx, input) if err != nil || output.LaunchTemplate == nil { return fmt.Errorf("error creating LaunchTemplate %q: %v", fi.ValueOf(t.Name), err) } @@ -166,7 +171,7 @@ func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, e, changes *LaunchT LaunchTemplateName: t.Name, LaunchTemplateData: data, } - if version, err := c.Cloud.EC2().CreateLaunchTemplateVersion(input); err != nil { + if version, err := c.Cloud.EC2().CreateLaunchTemplateVersion(ctx, input); err != nil { return fmt.Errorf("error creating LaunchTemplateVersion: %v", err) } else { newDefault := strconv.FormatInt(*version.LaunchTemplateVersion.VersionNumber, 10) @@ -174,7 +179,7 @@ func (t *LaunchTemplate) RenderAWS(c *awsup.AWSAPITarget, a, e, changes *LaunchT DefaultVersion: &newDefault, LaunchTemplateId: version.LaunchTemplateVersion.LaunchTemplateId, } - if _, err := c.Cloud.EC2().ModifyLaunchTemplate(input); err != nil { + if _, err := c.Cloud.EC2().ModifyLaunchTemplate(ctx, input); err != nil { return fmt.Errorf("error updating launch template version: %w", err) } } @@ -214,11 +219,13 @@ func (t *LaunchTemplate) Find(c *fi.CloudupContext) (*LaunchTemplate, error) { ID: lt.LaunchTemplateId, ImageID: lt.LaunchTemplateData.ImageId, InstanceMonitoring: fi.PtrTo(false), - InstanceType: lt.LaunchTemplateData.InstanceType, Lifecycle: t.Lifecycle, Name: t.Name, RootVolumeOptimization: lt.LaunchTemplateData.EbsOptimized, } + if len(lt.LaunchTemplateData.InstanceType) > 0 { + actual.InstanceType = fi.PtrTo(lt.LaunchTemplateData.InstanceType) + } // @step: check if any of the interfaces are public facing for _, x := range lt.LaunchTemplateData.NetworkInterfaces { @@ -226,13 +233,13 @@ func (t *LaunchTemplate) Find(c *fi.CloudupContext) (*LaunchTemplate, error) { actual.AssociatePublicIP = fi.PtrTo(true) } for _, id := range x.Groups { - actual.SecurityGroups = append(actual.SecurityGroups, &SecurityGroup{ID: id}) + actual.SecurityGroups = append(actual.SecurityGroups, &SecurityGroup{ID: fi.PtrTo(id)}) } actual.IPv6AddressCount = x.Ipv6AddressCount } // In older Kops versions, security groups were added to LaunchTemplateData.SecurityGroupIds for _, id := range lt.LaunchTemplateData.SecurityGroupIds { - actual.SecurityGroups = append(actual.SecurityGroups, &SecurityGroup{ID: fi.PtrTo("legacy-" + *id)}) + actual.SecurityGroups = append(actual.SecurityGroups, &SecurityGroup{ID: fi.PtrTo("legacy-" + id)}) } sort.Sort(OrderSecurityGroupsById(actual.SecurityGroups)) @@ -246,8 +253,8 @@ func (t *LaunchTemplate) Find(c *fi.CloudupContext) (*LaunchTemplate, error) { actual.InstanceMonitoring = lt.LaunchTemplateData.Monitoring.Enabled } // @step: add the tenancy - if lt.LaunchTemplateData.Placement != nil { - actual.Tenancy = lt.LaunchTemplateData.Placement.Tenancy + if lt.LaunchTemplateData.Placement != nil && len(lt.LaunchTemplateData.Placement.Tenancy) > 0 { + actual.Tenancy = fi.PtrTo(lt.LaunchTemplateData.Placement.Tenancy) } // @step: add the ssh if there is one if lt.LaunchTemplateData.KeyName != nil { @@ -262,7 +269,9 @@ func (t *LaunchTemplate) Find(c *fi.CloudupContext) (*LaunchTemplate, error) { if imo != nil && imo.SpotOptions != nil && aws.ToString(imo.SpotOptions.MaxPrice) != "" { actual.SpotPrice = imo.SpotOptions.MaxPrice actual.SpotDurationInMinutes = imo.SpotOptions.BlockDurationMinutes - actual.InstanceInterruptionBehavior = imo.SpotOptions.InstanceInterruptionBehavior + if len(imo.SpotOptions.InstanceInterruptionBehavior) > 0 { + actual.InstanceInterruptionBehavior = fi.PtrTo(imo.SpotOptions.InstanceInterruptionBehavior) + } } else { actual.SpotPrice = aws.String("") } @@ -316,8 +325,12 @@ func (t *LaunchTemplate) Find(c *fi.CloudupContext) (*LaunchTemplate, error) { // @step: add instance metadata options if options := lt.LaunchTemplateData.MetadataOptions; options != nil { actual.HTTPPutResponseHopLimit = options.HttpPutResponseHopLimit - actual.HTTPTokens = options.HttpTokens - actual.HTTPProtocolIPv6 = options.HttpProtocolIpv6 + if len(options.HttpTokens) > 0 { + actual.HTTPTokens = fi.PtrTo(options.HttpTokens) + } + if len(options.HttpProtocolIpv6) > 0 { + actual.HTTPProtocolIPv6 = fi.PtrTo(options.HttpProtocolIpv6) + } } // @step: to avoid spurious changes on ImageId @@ -341,7 +354,7 @@ func (t *LaunchTemplate) Find(c *fi.CloudupContext) (*LaunchTemplate, error) { } // findAllLaunchTemplates returns all the launch templates for us -func (t *LaunchTemplate) findAllLaunchTemplates(c *fi.CloudupContext) ([]*ec2.LaunchTemplate, error) { +func (t *LaunchTemplate) findAllLaunchTemplates(c *fi.CloudupContext) ([]ec2types.LaunchTemplate, error) { ctx := c.Context() cloud, ok := c.T.Cloud.(awsup.AWSCloud) @@ -350,28 +363,29 @@ func (t *LaunchTemplate) findAllLaunchTemplates(c *fi.CloudupContext) ([]*ec2.La } input := &ec2.DescribeLaunchTemplatesInput{ - Filters: []*ec2.Filter{ + Filters: []ec2types.Filter{ { Name: aws.String("tag:Name"), - Values: []*string{t.Name}, + Values: []string{fi.ValueOf(t.Name)}, }, }, } - var list []*ec2.LaunchTemplate - err := cloud.EC2().DescribeLaunchTemplatesPagesWithContext(ctx, input, func(p *ec2.DescribeLaunchTemplatesOutput, lastPage bool) (shouldContinue bool) { - list = append(list, p.LaunchTemplates...) - return true - }) - if err != nil { - return nil, fmt.Errorf("error listing AutoScaling LaunchTemplates: %v", err) + var list []ec2types.LaunchTemplate + paginator := ec2.NewDescribeLaunchTemplatesPaginator(cloud.EC2(), input) + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("error listing AutoScaling LaunchTemplates: %v", err) + } + list = append(list, page.LaunchTemplates...) } return list, nil } // findLatestLaunchTemplateVersion returns the latest template version -func (t *LaunchTemplate) findLatestLaunchTemplateVersion(c *fi.CloudupContext) (*ec2.LaunchTemplateVersion, error) { +func (t *LaunchTemplate) findLatestLaunchTemplateVersion(c *fi.CloudupContext) (*ec2types.LaunchTemplateVersion, error) { ctx := c.Context() cloud, ok := c.T.Cloud.(awsup.AWSCloud) @@ -381,10 +395,10 @@ func (t *LaunchTemplate) findLatestLaunchTemplateVersion(c *fi.CloudupContext) ( input := &ec2.DescribeLaunchTemplateVersionsInput{ LaunchTemplateName: t.Name, - Versions: []*string{aws.String("$Latest")}, + Versions: []string{("$Latest")}, } - output, err := cloud.EC2().DescribeLaunchTemplateVersionsWithContext(ctx, input) + output, err := cloud.EC2().DescribeLaunchTemplateVersions(ctx, input) if err != nil { if awsup.AWSErrorCode(err) == "InvalidLaunchTemplateName.NotFoundException" { klog.V(4).Infof("Got InvalidLaunchTemplateName.NotFoundException error describing latest launch template version: %q", aws.ToString(t.Name)) @@ -398,13 +412,13 @@ func (t *LaunchTemplate) findLatestLaunchTemplateVersion(c *fi.CloudupContext) ( return nil, nil } - return output.LaunchTemplateVersions[0], nil + return &output.LaunchTemplateVersions[0], nil } // deleteLaunchTemplate tracks a LaunchConfiguration that we're going to delete // It implements fi.CloudupDeletion type deleteLaunchTemplate struct { - lc *ec2.LaunchTemplate + lc *ec2types.LaunchTemplate } var _ fi.CloudupDeletion = &deleteLaunchTemplate{} @@ -420,12 +434,13 @@ func (d *deleteLaunchTemplate) Item() string { } func (d *deleteLaunchTemplate) Delete(t fi.CloudupTarget) error { + ctx := context.TODO() awsTarget, ok := t.(*awsup.AWSAPITarget) if !ok { return fmt.Errorf("unexpected target type for deletion: %T", t) } - if _, err := awsTarget.Cloud.EC2().DeleteLaunchTemplate(&ec2.DeleteLaunchTemplateInput{ + if _, err := awsTarget.Cloud.EC2().DeleteLaunchTemplate(ctx, &ec2.DeleteLaunchTemplateInput{ LaunchTemplateName: d.lc.LaunchTemplateName, }); err != nil { return fmt.Errorf("error deleting LaunchTemplate %s: error: %s", d.Item(), err) diff --git a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform.go b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform.go index a446c44f6abe3..d8786b87069f3 100644 --- a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform.go +++ b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform.go @@ -17,6 +17,7 @@ limitations under the License. package awstasks import ( + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" "k8s.io/kops/upup/pkg/fi/cloudup/terraform" @@ -29,7 +30,7 @@ type terraformLaunchTemplateNetworkInterface struct { // DeleteOnTermination indicates whether the network interface should be destroyed on instance termination. DeleteOnTermination *bool `cty:"delete_on_termination"` // Ipv6AddressCount is the number of IPv6 addresses to assign with the primary network interface. - Ipv6AddressCount *int64 `cty:"ipv6_address_count"` + Ipv6AddressCount *int32 `cty:"ipv6_address_count"` // SecurityGroups is a list of security group ids. SecurityGroups []*terraformWriter.Literal `cty:"security_groups"` } @@ -51,7 +52,7 @@ type terraformLaunchTemplatePlacement struct { // SpreadDomain are reserved for future use. SpreadDomain *string `cty:"spread_domain"` // Tenancy ist he tenancy of the instance. Can be default, dedicated, or host. - Tenancy *string `cty:"tenancy"` + Tenancy *ec2types.Tenancy `cty:"tenancy"` } type terraformLaunchTemplateIAMProfile struct { @@ -61,9 +62,9 @@ type terraformLaunchTemplateIAMProfile struct { type terraformLaunchTemplateMarketOptionsSpotOptions struct { // BlockDurationMinutes is required duration in minutes. This value must be a multiple of 60. - BlockDurationMinutes *int64 `cty:"block_duration_minutes"` + BlockDurationMinutes *int32 `cty:"block_duration_minutes"` // InstanceInterruptionBehavior is the behavior when a Spot Instance is interrupted. Can be hibernate, stop, or terminate - InstanceInterruptionBehavior *string `cty:"instance_interruption_behavior"` + InstanceInterruptionBehavior *ec2types.InstanceInterruptionBehavior `cty:"instance_interruption_behavior"` // MaxPrice is the maximum hourly price you're willing to pay for the Spot Instances MaxPrice *string `cty:"max_price"` // SpotInstanceType is the Spot Instance request type. Can be one-time, or persistent @@ -83,11 +84,11 @@ type terraformLaunchTemplateBlockDeviceEBS struct { // VolumeType is the ebs type to use VolumeType *string `cty:"volume_type"` // VolumeSize is the volume size - VolumeSize *int64 `cty:"volume_size"` + VolumeSize *int32 `cty:"volume_size"` // IOPS is the provisioned IOPS - IOPS *int64 `cty:"iops"` + IOPS *int32 `cty:"iops"` // Throughput is the gp3 volume throughput - Throughput *int64 `cty:"throughput"` + Throughput *int32 `cty:"throughput"` // DeleteOnTermination indicates the volume should die with the instance DeleteOnTermination *bool `cty:"delete_on_termination"` // Encrypted indicates the device should be encrypted @@ -120,11 +121,11 @@ type terraformLaunchTemplateInstanceMetadata struct { // HTTPEndpoint enables or disables the HTTP metadata endpoint on instances. HTTPEndpoint *string `cty:"http_endpoint"` // HTTPPutResponseHopLimit is the desired HTTP PUT response hop limit for instance metadata requests. - HTTPPutResponseHopLimit *int64 `cty:"http_put_response_hop_limit"` + HTTPPutResponseHopLimit *int32 `cty:"http_put_response_hop_limit"` // HTTPTokens is the state of token usage for your instance metadata requests. - HTTPTokens *string `cty:"http_tokens"` + HTTPTokens *ec2types.LaunchTemplateHttpTokensState `cty:"http_tokens"` // HTTPProtocolIPv6 enables the IPv6 instance metadata endpoint - HTTPProtocolIPv6 *string `cty:"http_protocol_ipv6"` + HTTPProtocolIPv6 *ec2types.LaunchTemplateInstanceMetadataProtocolIpv6 `cty:"http_protocol_ipv6"` } type terraformLaunchTemplate struct { @@ -144,7 +145,7 @@ type terraformLaunchTemplate struct { // ImageID is the ami to use for the instances ImageID *string `cty:"image_id"` // InstanceType is the type of instance - InstanceType *string `cty:"instance_type"` + InstanceType *ec2types.InstanceType `cty:"instance_type"` // KeyName is the ssh key to use KeyName *terraformWriter.Literal `cty:"key_name"` // MarketOptions are the spot pricing options @@ -213,12 +214,10 @@ func (t *LaunchTemplate) RenderTerraform(target *terraform.TerraformTarget, a, e } if fi.ValueOf(e.SpotPrice) != "" { - marketSpotOptions := terraformLaunchTemplateMarketOptionsSpotOptions{MaxPrice: e.SpotPrice} - if e.SpotDurationInMinutes != nil { - marketSpotOptions.BlockDurationMinutes = e.SpotDurationInMinutes - } - if e.InstanceInterruptionBehavior != nil { - marketSpotOptions.InstanceInterruptionBehavior = e.InstanceInterruptionBehavior + marketSpotOptions := terraformLaunchTemplateMarketOptionsSpotOptions{ + BlockDurationMinutes: e.SpotDurationInMinutes, + InstanceInterruptionBehavior: e.InstanceInterruptionBehavior, + MaxPrice: e.SpotPrice, } tf.MarketOptions = []*terraformLaunchTemplateMarketOptions{ { @@ -278,7 +277,7 @@ func (t *LaunchTemplate) RenderTerraform(target *terraform.TerraformTarget, a, e IOPS: x.EbsVolumeIops, Throughput: x.EbsVolumeThroughput, VolumeSize: x.EbsVolumeSize, - VolumeType: x.EbsVolumeType, + VolumeType: fi.PtrTo(string(x.EbsVolumeType)), }, }, }) @@ -298,7 +297,7 @@ func (t *LaunchTemplate) RenderTerraform(target *terraform.TerraformTarget, a, e Throughput: x.EbsVolumeThroughput, KmsKeyID: x.EbsKmsKey, VolumeSize: x.EbsVolumeSize, - VolumeType: x.EbsVolumeType, + VolumeType: fi.PtrTo(string(x.EbsVolumeType)), }, }, }) diff --git a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform_test.go b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform_test.go index 35fec3f9c7af0..8a3033df79d0c 100644 --- a/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform_test.go +++ b/upup/pkg/fi/cloudup/awstasks/launchtemplate_target_terraform_test.go @@ -19,6 +19,8 @@ package awstasks import ( "testing" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "k8s.io/kops/upup/pkg/fi" ) @@ -33,13 +35,13 @@ func TestLaunchTemplateTerraformRender(t *testing.T) { }, ID: fi.PtrTo("test-11"), InstanceMonitoring: fi.PtrTo(true), - InstanceType: fi.PtrTo("t2.medium"), + InstanceType: fi.PtrTo(ec2types.InstanceTypeT2Medium), SpotPrice: fi.PtrTo("0.1"), - SpotDurationInMinutes: fi.PtrTo(int64(60)), - InstanceInterruptionBehavior: fi.PtrTo("hibernate"), + SpotDurationInMinutes: fi.PtrTo(int32(60)), + InstanceInterruptionBehavior: fi.PtrTo(ec2types.InstanceInterruptionBehaviorHibernate), RootVolumeOptimization: fi.PtrTo(true), - RootVolumeIops: fi.PtrTo(int64(100)), - RootVolumeSize: fi.PtrTo(int64(64)), + RootVolumeIops: fi.PtrTo(int32(100)), + RootVolumeSize: fi.PtrTo(int32(64)), SSHKey: &SSHKey{ Name: fi.PtrTo("newkey"), PublicKey: fi.NewStringResource("newkey"), @@ -48,9 +50,9 @@ func TestLaunchTemplateTerraformRender(t *testing.T) { {Name: fi.PtrTo("nodes-1"), ID: fi.PtrTo("1111")}, {Name: fi.PtrTo("nodes-2"), ID: fi.PtrTo("2222")}, }, - Tenancy: fi.PtrTo("dedicated"), - HTTPTokens: fi.PtrTo("optional"), - HTTPPutResponseHopLimit: fi.PtrTo(int64(1)), + Tenancy: fi.PtrTo(ec2types.TenancyDedicated), + HTTPTokens: fi.PtrTo(ec2types.LaunchTemplateHttpTokensStateOptional), + HTTPPutResponseHopLimit: fi.PtrTo(int32(1)), }, Expected: `provider "aws" { region = "eu-west-2" @@ -114,18 +116,18 @@ terraform { BlockDeviceMappings: []*BlockDeviceMapping{ { DeviceName: fi.PtrTo("/dev/xvdd"), - EbsVolumeType: fi.PtrTo("gp2"), - EbsVolumeSize: fi.PtrTo(int64(100)), + EbsVolumeType: ec2types.VolumeTypeGp2, + EbsVolumeSize: fi.PtrTo(int32(100)), EbsDeleteOnTermination: fi.PtrTo(true), EbsEncrypted: fi.PtrTo(true), }, }, ID: fi.PtrTo("test-11"), InstanceMonitoring: fi.PtrTo(true), - InstanceType: fi.PtrTo("t2.medium"), + InstanceType: fi.PtrTo(ec2types.InstanceTypeT2Medium), RootVolumeOptimization: fi.PtrTo(true), - RootVolumeIops: fi.PtrTo(int64(100)), - RootVolumeSize: fi.PtrTo(int64(64)), + RootVolumeIops: fi.PtrTo(int32(100)), + RootVolumeSize: fi.PtrTo(int32(64)), SSHKey: &SSHKey{ Name: fi.PtrTo("mykey"), }, @@ -133,9 +135,9 @@ terraform { {Name: fi.PtrTo("nodes-1"), ID: fi.PtrTo("1111")}, {Name: fi.PtrTo("nodes-2"), ID: fi.PtrTo("2222")}, }, - Tenancy: fi.PtrTo("dedicated"), - HTTPTokens: fi.PtrTo("required"), - HTTPPutResponseHopLimit: fi.PtrTo(int64(5)), + Tenancy: fi.PtrTo(ec2types.TenancyDedicated), + HTTPTokens: fi.PtrTo(ec2types.LaunchTemplateHttpTokensStateRequired), + HTTPPutResponseHopLimit: fi.PtrTo(int32(5)), }, Expected: `provider "aws" { region = "eu-west-2" diff --git a/upup/pkg/fi/cloudup/awstasks/sshkey.go b/upup/pkg/fi/cloudup/awstasks/sshkey.go index 13c7eecf233d8..5f5ff733ca62b 100644 --- a/upup/pkg/fi/cloudup/awstasks/sshkey.go +++ b/upup/pkg/fi/cloudup/awstasks/sshkey.go @@ -17,11 +17,12 @@ limitations under the License. package awstasks import ( + "context" "fmt" "strings" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi/cloudup/terraformWriter" @@ -55,21 +56,16 @@ func (e *SSHKey) CompareWithID() *string { func (e *SSHKey) Find(c *fi.CloudupContext) (*SSHKey, error) { cloud := c.T.Cloud.(awsup.AWSCloud) - return e.find(cloud) + return e.find(c.Context(), cloud) } -func (e *SSHKey) find(cloud awsup.AWSCloud) (*SSHKey, error) { +func (e *SSHKey) find(ctx context.Context, cloud awsup.AWSCloud) (*SSHKey, error) { request := &ec2.DescribeKeyPairsInput{ - KeyNames: []*string{e.Name}, + KeyNames: []string{fi.ValueOf(e.Name)}, } - response, err := cloud.EC2().DescribeKeyPairs(request) - if awsErr, ok := err.(awserr.Error); ok { - if awsErr.Code() == "InvalidKeyPair.NotFound" { - err = nil - } - } - if err != nil { + response, err := cloud.EC2().DescribeKeyPairs(ctx, request) + if err != nil && awsup.AWSErrorCode(err) != "InvalidKeyPair.NotFound" { return nil, fmt.Errorf("error listing SSHKeys: %v", err) } @@ -94,7 +90,7 @@ func (e *SSHKey) find(cloud awsup.AWSCloud) (*SSHKey, error) { } // Avoid spurious changes - if fi.ValueOf(k.KeyType) == ec2.KeyTypeEd25519 { + if k.KeyType == ec2types.KeyTypeEd25519 { // Trim the trailing "=" and prefix with "SHA256:" to match the output of "ssh-keygen -lf" fingerprint := fi.ValueOf(k.KeyFingerprint) fingerprint = strings.TrimRight(fingerprint, "=") @@ -152,11 +148,12 @@ func (s *SSHKey) CheckChanges(a, e, changes *SSHKey) error { } func (e *SSHKey) createKeypair(cloud awsup.AWSCloud) error { + ctx := context.TODO() klog.V(2).Infof("Creating SSHKey with Name:%q", *e.Name) request := &ec2.ImportKeyPairInput{ KeyName: e.Name, - TagSpecifications: awsup.EC2TagSpecification(ec2.ResourceTypeKeyPair, e.Tags), + TagSpecifications: awsup.EC2TagSpecification(ec2types.ResourceTypeKeyPair, e.Tags), } if e.PublicKey != nil { @@ -167,7 +164,7 @@ func (e *SSHKey) createKeypair(cloud awsup.AWSCloud) error { request.PublicKeyMaterial = d } - response, err := cloud.EC2().ImportKeyPair(request) + response, err := cloud.EC2().ImportKeyPair(ctx, request) if err != nil { return fmt.Errorf("error creating SSHKey: %v", err) } diff --git a/upup/pkg/fi/cloudup/awstasks/tags.go b/upup/pkg/fi/cloudup/awstasks/tags.go index 67b0e91e3e424..4b9eb87209430 100644 --- a/upup/pkg/fi/cloudup/awstasks/tags.go +++ b/upup/pkg/fi/cloudup/awstasks/tags.go @@ -20,12 +20,12 @@ import ( "strings" "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" eventbridgetypes "github.com/aws/aws-sdk-go-v2/service/eventbridge/types" iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" - "github.com/aws/aws-sdk-go/service/ec2" ) -func mapEC2TagsToMap(tags []*ec2.Tag) map[string]string { +func mapEC2TagsToMap(tags []ec2types.Tag) map[string]string { if tags == nil { return nil } @@ -81,7 +81,7 @@ func mapEventBridgeTagsToMap(tags []eventbridgetypes.Tag) map[string]string { return m } -func findNameTag(tags []*ec2.Tag) *string { +func findNameTag(tags []ec2types.Tag) *string { for _, tag := range tags { if aws.ToString(tag.Key) == "Name" { return tag.Value @@ -92,7 +92,7 @@ func findNameTag(tags []*ec2.Tag) *string { // intersectTags returns the tags of interest from a specified list of AWS tags; // because we only add tags, this set of tags of interest is the tags that occur in the desired set. -func intersectTags(tags []*ec2.Tag, desired map[string]string) map[string]string { +func intersectTags(tags []ec2types.Tag, desired map[string]string) map[string]string { if tags == nil { return nil } diff --git a/upup/pkg/fi/cloudup/spotinsttasks/elastigroup.go b/upup/pkg/fi/cloudup/spotinsttasks/elastigroup.go index df74e1441fda5..2365885e32b58 100644 --- a/upup/pkg/fi/cloudup/spotinsttasks/elastigroup.go +++ b/upup/pkg/fi/cloudup/spotinsttasks/elastigroup.go @@ -24,7 +24,7 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/aws" "github.com/spotinst/spotinst-sdk-go/spotinst/client" "github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil" @@ -1642,10 +1642,10 @@ func (_ *Elastigroup) RenderTerraform(t *terraform.TerraformTarget, a, e, change tf.RootBlockDevice = &terraformElastigroupBlockDevice{ DeviceName: rootDevice.DeviceName, - VolumeType: rootDevice.EbsVolumeType, - VolumeSize: rootDevice.EbsVolumeSize, - VolumeIOPS: rootDevice.EbsVolumeIops, - VolumeThroughput: rootDevice.EbsVolumeThroughput, + VolumeType: fi.PtrTo(string(rootDevice.EbsVolumeType)), + VolumeSize: ptrInt32ToPtrInt64(rootDevice.EbsVolumeSize), + VolumeIOPS: ptrInt32ToPtrInt64(rootDevice.EbsVolumeIops), + VolumeThroughput: ptrInt32ToPtrInt64(rootDevice.EbsVolumeThroughput), Encrypted: rootDevice.EbsEncrypted, DeleteOnTermination: fi.PtrTo(true), } @@ -1805,7 +1805,7 @@ func (e *Elastigroup) buildTargetGroups() []*aws.LoadBalancer { } func buildEphemeralDevices(cloud awsup.AWSCloud, machineType *string) ([]*awstasks.BlockDeviceMapping, error) { - info, err := awsup.GetMachineTypeInfo(cloud, fi.ValueOf(machineType)) + info, err := awsup.GetMachineTypeInfo(cloud, ec2types.InstanceType(fi.ValueOf(machineType))) if err != nil { return nil, err } @@ -1831,20 +1831,20 @@ func buildRootDevice(cloud awsup.AWSCloud, volumeOpts *RootVolumeOpts, bdm := &awstasks.BlockDeviceMapping{ DeviceName: img.RootDeviceName, - EbsVolumeSize: volumeOpts.Size, - EbsVolumeType: volumeOpts.Type, + EbsVolumeSize: ptrInt64ToPtrInt32(volumeOpts.Size), + EbsVolumeType: ec2types.VolumeType(fi.ValueOf(volumeOpts.Type)), EbsEncrypted: volumeOpts.Encryption, EbsDeleteOnTermination: fi.PtrTo(true), } // IOPS is not supported for gp2 volumes. if volumeOpts.IOPS != nil && fi.ValueOf(volumeOpts.Type) != "gp2" { - bdm.EbsVolumeIops = volumeOpts.IOPS + bdm.EbsVolumeIops = ptrInt64ToPtrInt32(volumeOpts.IOPS) } // Throughput is only supported for gp3 volumes. if volumeOpts.Throughput != nil && fi.ValueOf(volumeOpts.Type) == "gp3" { - bdm.EbsVolumeThroughput = volumeOpts.Throughput + bdm.EbsVolumeThroughput = ptrInt64ToPtrInt32(volumeOpts.Throughput) } return bdm, nil @@ -1856,21 +1856,21 @@ func (e *Elastigroup) convertBlockDeviceMapping(in *awstasks.BlockDeviceMapping) VirtualName: in.VirtualName, } - if in.EbsDeleteOnTermination != nil || in.EbsVolumeSize != nil || in.EbsVolumeType != nil { + if in.EbsDeleteOnTermination != nil || in.EbsVolumeSize != nil || len(in.EbsVolumeType) > 0 { out.EBS = &aws.EBS{ - VolumeType: in.EbsVolumeType, + VolumeType: fi.PtrTo(string(in.EbsVolumeType)), VolumeSize: fi.PtrTo(int(fi.ValueOf(in.EbsVolumeSize))), Encrypted: in.EbsEncrypted, DeleteOnTermination: in.EbsDeleteOnTermination, } // IOPS is not valid for gp2 volumes. - if in.EbsVolumeIops != nil && fi.ValueOf(in.EbsVolumeType) != "gp2" { + if in.EbsVolumeIops != nil && in.EbsVolumeType != ec2types.VolumeTypeGp2 { out.EBS.IOPS = fi.PtrTo(int(fi.ValueOf(in.EbsVolumeIops))) } // Throughput is only valid for gp3 volumes. - if in.EbsVolumeThroughput != nil && fi.ValueOf(in.EbsVolumeType) == "gp3" { + if in.EbsVolumeThroughput != nil && in.EbsVolumeType == ec2types.VolumeTypeGp3 { out.EBS.Throughput = fi.PtrTo(int(fi.ValueOf(in.EbsVolumeThroughput))) } } @@ -1904,7 +1904,7 @@ func (e *Elastigroup) applyDefaults() { } } -func resolveImage(cloud awsup.AWSCloud, name string) (*ec2.Image, error) { +func resolveImage(cloud awsup.AWSCloud, name string) (*ec2types.Image, error) { image, err := cloud.ResolveImage(name) if err != nil { return nil, fmt.Errorf("spotinst: unable to resolve image %q: %v", name, err) diff --git a/upup/pkg/fi/cloudup/spotinsttasks/launch_spec.go b/upup/pkg/fi/cloudup/spotinsttasks/launch_spec.go index 58f6e78e21c2f..8533860561cb8 100644 --- a/upup/pkg/fi/cloudup/spotinsttasks/launch_spec.go +++ b/upup/pkg/fi/cloudup/spotinsttasks/launch_spec.go @@ -23,6 +23,7 @@ import ( "reflect" "strings" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/spotinst/spotinst-sdk-go/service/ocean/providers/aws" "github.com/spotinst/spotinst-sdk-go/spotinst/util/stringutil" corev1 "k8s.io/api/core/v1" @@ -1047,10 +1048,10 @@ func (_ *LaunchSpec) RenderTerraform(t *terraform.TerraformTarget, a, e, changes tf.BlockDeviceMappings = append(tf.BlockDeviceMappings, &terraformBlockDeviceMapping{ DeviceName: rootDevice.DeviceName, EBS: &terraformBlockDeviceMappingEBS{ - VolumeType: rootDevice.EbsVolumeType, - VolumeSize: rootDevice.EbsVolumeSize, - VolumeIOPS: rootDevice.EbsVolumeIops, - VolumeThroughput: rootDevice.EbsVolumeThroughput, + VolumeType: fi.PtrTo(string(rootDevice.EbsVolumeType)), + VolumeSize: ptrInt32ToPtrInt64(rootDevice.EbsVolumeSize), + VolumeIOPS: ptrInt32ToPtrInt64(rootDevice.EbsVolumeIops), + VolumeThroughput: ptrInt32ToPtrInt64(rootDevice.EbsVolumeThroughput), Encrypted: rootDevice.EbsEncrypted, DeleteOnTermination: fi.PtrTo(true), }, @@ -1154,20 +1155,20 @@ func (o *LaunchSpec) convertBlockDeviceMapping(in *awstasks.BlockDeviceMapping) VirtualName: in.VirtualName, } - if in.EbsDeleteOnTermination != nil || in.EbsVolumeSize != nil || in.EbsVolumeType != nil { + if in.EbsDeleteOnTermination != nil || in.EbsVolumeSize != nil || len(in.EbsVolumeType) > 0 { out.EBS = &aws.EBS{ - VolumeType: in.EbsVolumeType, + VolumeType: fi.PtrTo(string(in.EbsVolumeType)), VolumeSize: fi.PtrTo(int(fi.ValueOf(in.EbsVolumeSize))), DeleteOnTermination: in.EbsDeleteOnTermination, } // IOPS is not valid for gp2 volumes. - if in.EbsVolumeIops != nil && fi.ValueOf(in.EbsVolumeType) != "gp2" { + if in.EbsVolumeIops != nil && in.EbsVolumeType != ec2types.VolumeTypeGp2 { out.EBS.IOPS = fi.PtrTo(int(fi.ValueOf(in.EbsVolumeIops))) } // Throughput is only valid for gp3 volumes. - if in.EbsVolumeThroughput != nil && fi.ValueOf(in.EbsVolumeType) == "gp3" { + if in.EbsVolumeThroughput != nil && in.EbsVolumeType == ec2types.VolumeTypeGp3 { out.EBS.Throughput = fi.PtrTo(int(fi.ValueOf(in.EbsVolumeThroughput))) } } diff --git a/upup/pkg/fi/cloudup/spotinsttasks/ocean.go b/upup/pkg/fi/cloudup/spotinsttasks/ocean.go index 0785373d7a883..3adb538b25b17 100644 --- a/upup/pkg/fi/cloudup/spotinsttasks/ocean.go +++ b/upup/pkg/fi/cloudup/spotinsttasks/ocean.go @@ -1367,3 +1367,19 @@ func NormalizeClusterOrientation(orientation *string) ClusterOrientation { return out } + +func ptrInt32ToPtrInt64(i *int32) *int64 { + if i == nil { + return nil + } + v := int64(*i) + return fi.PtrTo(v) +} + +func ptrInt64ToPtrInt32(i *int64) *int32 { + if i == nil { + return nil + } + v := int32(*i) + return fi.PtrTo(v) +} From c7fba183cdbedbf90cda5ce9f63d25d1a6ecd919 Mon Sep 17 00:00:00 2001 From: Peter Rifel Date: Tue, 9 Apr 2024 21:09:10 -0500 Subject: [PATCH 04/14] Update EC2 tagging to aws-sdk-go-v2 --- upup/pkg/fi/cloudup/awsup/aws_cloud.go | 53 ++++++++++----------- upup/pkg/fi/cloudup/awsup/aws_utils.go | 14 +++--- upup/pkg/fi/cloudup/awsup/aws_utils_test.go | 15 +++--- 3 files changed, 40 insertions(+), 42 deletions(-) diff --git a/upup/pkg/fi/cloudup/awsup/aws_cloud.go b/upup/pkg/fi/cloudup/awsup/aws_cloud.go index ef6d32e2eff8f..57819413d428d 100644 --- a/upup/pkg/fi/cloudup/awsup/aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/aws_cloud.go @@ -140,7 +140,7 @@ type AWSCloud interface { // TODO: Document and rationalize these tags/filters methods AddTags(name *string, tags map[string]string) - BuildFilters(name *string) []*ec2.Filter + BuildFilters(name *string) []ec2types.Filter BuildTags(name *string) map[string]string Tags() map[string]string @@ -1038,8 +1038,8 @@ func matchesAsgTags(tags map[string]string, actual []autoscalingtypes.TagDescrip for k, v := range tags { found := false for _, a := range actual { - if aws.StringValue(a.Key) == k { - if aws.StringValue(a.Value) == v { + if aws.ToString(a.Key) == k { + if aws.ToString(a.Value) == v { found = true break } @@ -1331,11 +1331,12 @@ func getTags(c AWSCloud, resourceID string) (map[string]string, error) { if resourceID == "" { return nil, fmt.Errorf("resourceID not provided to getTags") } + ctx := context.TODO() tags := map[string]string{} request := &ec2.DescribeTagsInput{ - Filters: []*ec2.Filter{ + Filters: []ec2types.Filter{ NewEC2Filter("resource-id", resourceID), }, } @@ -1344,7 +1345,7 @@ func getTags(c AWSCloud, resourceID string) (map[string]string, error) { for { attempt++ - response, err := c.EC2().DescribeTags(request) + response, err := c.EC2().DescribeTags(ctx, request) if err != nil { if isTagsEventualConsistencyError(err) { if attempt > DescribeTagsMaxAttempts { @@ -1364,11 +1365,7 @@ func getTags(c AWSCloud, resourceID string) (map[string]string, error) { } for _, tag := range response.Tags { - if tag == nil { - klog.Warning("unexpected nil tag") - continue - } - tags[aws.StringValue(tag.Key)] = aws.StringValue(tag.Value) + tags[aws.ToString(tag.Key)] = aws.ToString(tag.Value) } return tags, nil @@ -1384,10 +1381,11 @@ func createTags(c AWSCloud, resourceID string, tags map[string]string) error { if len(tags) == 0 { return nil } + ctx := context.TODO() - ec2Tags := []*ec2.Tag{} + ec2Tags := []ec2types.Tag{} for k, v := range tags { - ec2Tags = append(ec2Tags, &ec2.Tag{Key: aws.String(k), Value: aws.String(v)}) + ec2Tags = append(ec2Tags, ec2types.Tag{Key: aws.String(k), Value: aws.String(v)}) } attempt := 0 @@ -1396,10 +1394,10 @@ func createTags(c AWSCloud, resourceID string, tags map[string]string) error { request := &ec2.CreateTagsInput{ Tags: ec2Tags, - Resources: []*string{&resourceID}, + Resources: []string{resourceID}, } - _, err := c.EC2().CreateTags(request) + _, err := c.EC2().CreateTags(ctx, request) if err != nil { if isTagsEventualConsistencyError(err) { if attempt > CreateTagsMaxAttempts { @@ -1432,10 +1430,11 @@ func deleteTags(c AWSCloud, resourceID string, tags map[string]string) error { if len(tags) == 0 { return nil } + ctx := context.TODO() - ec2Tags := []*ec2.Tag{} + ec2Tags := []ec2types.Tag{} for k, v := range tags { - ec2Tags = append(ec2Tags, &ec2.Tag{Key: aws.String(k), Value: aws.String(v)}) + ec2Tags = append(ec2Tags, ec2types.Tag{Key: aws.String(k), Value: aws.String(v)}) } attempt := 0 @@ -1444,10 +1443,10 @@ func deleteTags(c AWSCloud, resourceID string, tags map[string]string) error { request := &ec2.DeleteTagsInput{ Tags: ec2Tags, - Resources: []*string{&resourceID}, + Resources: []string{resourceID}, } - _, err := c.EC2().DeleteTags(request) + _, err := c.EC2().DeleteTags(ctx, request) if err != nil { if isTagsEventualConsistencyError(err) { if attempt > DeleteTagsMaxAttempts { @@ -1562,7 +1561,7 @@ func getELBTags(c AWSCloud, loadBalancerName string) (map[string]string, error) for _, tagset := range response.TagDescriptions { for _, tag := range tagset.Tags { - tags[aws.StringValue(tag.Key)] = aws.StringValue(tag.Value) + tags[aws.ToString(tag.Key)] = aws.ToString(tag.Value) } } return tags, nil @@ -1673,7 +1672,7 @@ func getELBV2Tags(c AWSCloud, ResourceArn string) (map[string]string, error) { for _, tagset := range response.TagDescriptions { for _, tag := range tagset.Tags { - tags[aws.StringValue(tag.Key)] = aws.StringValue(tag.Value) + tags[aws.ToString(tag.Key)] = aws.ToString(tag.Value) } } @@ -1763,7 +1762,7 @@ func findELBByNameTag(c AWSCloud, findNameTag string) (*elbtypes.LoadBalancerDes var names []string nameToELB := make(map[string]elbtypes.LoadBalancerDescription) for _, elb := range page.LoadBalancerDescriptions { - name := aws.StringValue(elb.LoadBalancerName) + name := aws.ToString(elb.LoadBalancerName) nameToELB[name] = elb names = append(names, name) } @@ -1815,7 +1814,7 @@ func describeELBTags(c AWSCloud, loadBalancerNames []string) (map[string][]elbty tagMap := make(map[string][]elbtypes.Tag) for _, tagset := range response.TagDescriptions { - tagMap[aws.StringValue(tagset.LoadBalancerName)] = tagset.Tags + tagMap[aws.ToString(tagset.LoadBalancerName)] = tagset.Tags } return tagMap, nil } @@ -1835,7 +1834,7 @@ func FindLatestELBV2ByNameTag(loadBalancers []*LoadBalancerInfo, findNameTag str } else { n, err := strconv.Atoi(revisionTag) if err != nil { - klog.Warningf("ignoring load balancer %q with revision %q", aws.StringValue(lb.LoadBalancer.LoadBalancerArn), revision) + klog.Warningf("ignoring load balancer %q with revision %q", aws.ToString(lb.LoadBalancer.LoadBalancerArn), revision) continue } revision = n @@ -1899,17 +1898,17 @@ func describeELBV2Tags(c AWSCloud, loadBalancerArns []string) (map[string][]elbv tagMap := make(map[string][]elbv2types.Tag) for _, tagset := range response.TagDescriptions { - tagMap[aws.StringValue(tagset.ResourceArn)] = tagset.Tags + tagMap[aws.ToString(tagset.ResourceArn)] = tagset.Tags } return tagMap, nil } -func (c *awsCloudImplementation) BuildFilters(name *string) []*ec2.Filter { +func (c *awsCloudImplementation) BuildFilters(name *string) []ec2types.Filter { return buildFilters(c.tags, name) } -func buildFilters(commonTags map[string]string, name *string) []*ec2.Filter { - filters := []*ec2.Filter{} +func buildFilters(commonTags map[string]string, name *string) []ec2types.Filter { + filters := []ec2types.Filter{} merged := make(map[string]string) if name != nil { diff --git a/upup/pkg/fi/cloudup/awsup/aws_utils.go b/upup/pkg/fi/cloudup/awsup/aws_utils.go index 83c6682e52ad7..efd7119a1b0ca 100644 --- a/upup/pkg/fi/cloudup/awsup/aws_utils.go +++ b/upup/pkg/fi/cloudup/awsup/aws_utils.go @@ -116,7 +116,7 @@ func FindRegion(cluster *kops.Cluster) (string, error) { } // FindEC2Tag find the value of the tag with the specified key -func FindEC2Tag(tags []*ec2.Tag, key string) (string, bool) { +func FindEC2Tag(tags []ec2types.Tag, key string) (string, bool) { for _, tag := range tags { if key == aws.ToString(tag.Key) { return aws.ToString(tag.Value), true @@ -180,22 +180,22 @@ func AWSErrorMessage(err error) string { } // EC2TagSpecification converts a map of tags to an EC2 TagSpecification -func EC2TagSpecification(resourceType string, tags map[string]string) []*ec2.TagSpecification { +func EC2TagSpecification(resourceType ec2types.ResourceType, tags map[string]string) []ec2types.TagSpecification { if len(tags) == 0 { return nil } - specification := &ec2.TagSpecification{ - ResourceType: aws.String(resourceType), - Tags: make([]*ec2.Tag, 0), + specification := ec2types.TagSpecification{ + ResourceType: resourceType, + Tags: make([]ec2types.Tag, 0), } for k, v := range tags { - specification.Tags = append(specification.Tags, &ec2.Tag{ + specification.Tags = append(specification.Tags, ec2types.Tag{ Key: aws.String(k), Value: aws.String(v), }) } - return []*ec2.TagSpecification{specification} + return []ec2types.TagSpecification{specification} } // ELBv2Tags converts a map of tags to ELBv2 Tags diff --git a/upup/pkg/fi/cloudup/awsup/aws_utils_test.go b/upup/pkg/fi/cloudup/awsup/aws_utils_test.go index 248e721805fda..cba8ba7267a29 100644 --- a/upup/pkg/fi/cloudup/awsup/aws_utils_test.go +++ b/upup/pkg/fi/cloudup/awsup/aws_utils_test.go @@ -23,7 +23,6 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" - "github.com/aws/aws-sdk-go/service/ec2" "k8s.io/kops/pkg/apis/kops" ) @@ -72,23 +71,23 @@ func TestFindRegion(t *testing.T) { func TestEC2TagSpecification(t *testing.T) { cases := []struct { Name string - ResourceType string + ResourceType ec2types.ResourceType Tags map[string]string - Specification []*ec2.TagSpecification + Specification []ec2types.TagSpecification }{ { Name: "No tags", }, { Name: "simple tag", - ResourceType: "vpc", + ResourceType: ec2types.ResourceTypeVpc, Tags: map[string]string{ "foo": "bar", }, - Specification: []*ec2.TagSpecification{ + Specification: []ec2types.TagSpecification{ { - ResourceType: aws.String("vpc"), - Tags: []*ec2.Tag{ + ResourceType: ec2types.ResourceTypeVpc, + Tags: []ec2types.Tag{ { Key: aws.String("foo"), Value: aws.String("bar"), @@ -102,7 +101,7 @@ func TestEC2TagSpecification(t *testing.T) { t.Run(tc.Name, func(t *testing.T) { s := EC2TagSpecification(tc.ResourceType, tc.Tags) if !reflect.DeepEqual(s, tc.Specification) { - t.Fatalf("tag specifications did not match: %q vs %q", s, tc.Specification) + t.Fatalf("tag specifications did not match: %+v vs %+v", s, tc.Specification) } }) } From f05284a2f9e7407860639f5536b26cd1d158549b Mon Sep 17 00:00:00 2001 From: Peter Rifel Date: Tue, 9 Apr 2024 21:19:12 -0500 Subject: [PATCH 05/14] Migrate Instance Group management to aws-sdk-go-v2 --- pkg/instancegroups/rollingupdate_test.go | 23 ++- .../rollingupdate_warmpool_test.go | 11 +- upup/pkg/fi/cloudup/awsup/aws_apitarget.go | 3 +- upup/pkg/fi/cloudup/awsup/aws_cloud.go | 154 +++++++++--------- upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go | 4 +- 5 files changed, 99 insertions(+), 96 deletions(-) diff --git a/pkg/instancegroups/rollingupdate_test.go b/pkg/instancegroups/rollingupdate_test.go index 6b23f9f0270be..ca92e14a85bab 100644 --- a/pkg/instancegroups/rollingupdate_test.go +++ b/pkg/instancegroups/rollingupdate_test.go @@ -28,8 +28,7 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/autoscaling" autoscalingtypes "github.com/aws/aws-sdk-go-v2/service/autoscaling/types" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" v1meta "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -1030,7 +1029,7 @@ func TestRollingUpdateDisabledSurge(t *testing.T) { // <-- validated type concurrentTest struct { - ec2iface.EC2API + awsinterfaces.EC2API t *testing.T mutex sync.Mutex surge int @@ -1093,7 +1092,7 @@ func (c *concurrentTest) Validate() (*validation.ValidationCluster, error) { return &validation.ValidationCluster{}, nil } -func (c *concurrentTest) TerminateInstances(input *ec2.TerminateInstancesInput) (*ec2.TerminateInstancesOutput, error) { +func (c *concurrentTest) TerminateInstances(ctx context.Context, input *ec2.TerminateInstancesInput, optFns ...func(*ec2.Options)) (*ec2.TerminateInstancesOutput, error) { if input.DryRun != nil && *input.DryRun { return &ec2.TerminateInstancesOutput{}, nil } @@ -1103,7 +1102,7 @@ func (c *concurrentTest) TerminateInstances(input *ec2.TerminateInstancesInput) for _, id := range input.InstanceIds { assert.Equal(c.t, c.surge, len(c.detached), "Number of detached instances") - if c.detached[*id] { + if c.detached[id] { assert.LessOrEqual(c.t, c.terminationRequestsLeft, c.surge, "Deleting detached instances last") } @@ -1126,7 +1125,7 @@ func (c *concurrentTest) TerminateInstances(input *ec2.TerminateInstancesInput) assert.Equal(c.t, terminationRequestsLeft+1, c.previousValidation, "previous validation") } } - return c.EC2API.TerminateInstances(input) + return c.EC2API.TerminateInstances(ctx, input) } const postTerminationValidationDelay = 100 * time.Millisecond // NodeInterval plus some @@ -1258,11 +1257,11 @@ func (m *concurrentTestAutoscaling) DetachInstances(ctx context.Context, input * } type ec2IgnoreTags struct { - ec2iface.EC2API + awsinterfaces.EC2API } // CreateTags ignores tagging of instances done by the AWS fi.Cloud implementation of DetachInstance() -func (e *ec2IgnoreTags) CreateTags(*ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) { +func (e *ec2IgnoreTags) CreateTags(ctx context.Context, params *ec2.CreateTagsInput, optFns ...func(*ec2.Options)) (*ec2.CreateTagsOutput, error) { return &ec2.CreateTagsOutput{}, nil } @@ -1407,7 +1406,7 @@ func TestRollingUpdateDetachFails(t *testing.T) { // // <-- validated type alreadyDetachedTest struct { - ec2iface.EC2API + awsinterfaces.EC2API t *testing.T mutex sync.Mutex terminationRequestsLeft int @@ -1437,7 +1436,7 @@ func (t *alreadyDetachedTest) Validate() (*validation.ValidationCluster, error) return &validation.ValidationCluster{}, nil } -func (t *alreadyDetachedTest) TerminateInstances(input *ec2.TerminateInstancesInput) (*ec2.TerminateInstancesOutput, error) { +func (t *alreadyDetachedTest) TerminateInstances(ctx context.Context, input *ec2.TerminateInstancesInput, optFns ...func(*ec2.Options)) (*ec2.TerminateInstancesOutput, error) { if input.DryRun != nil && *input.DryRun { return &ec2.TerminateInstancesOutput{}, nil } @@ -1449,12 +1448,12 @@ func (t *alreadyDetachedTest) TerminateInstances(input *ec2.TerminateInstancesIn assert.Equal(t.t, 3, len(t.detached), "Number of detached instances") assert.GreaterOrEqual(t.t, t.numValidations, 3, "Number of previous validations") if t.terminationRequestsLeft == 1 { - assert.True(t.t, t.detached[*id], "Last deleted instance %q was detached", *id) + assert.True(t.t, t.detached[id], "Last deleted instance %q was detached", id) } t.terminationRequestsLeft-- } - return t.EC2API.TerminateInstances(input) + return t.EC2API.TerminateInstances(ctx, input) } type alreadyDetachedTestAutoscaling struct { diff --git a/pkg/instancegroups/rollingupdate_warmpool_test.go b/pkg/instancegroups/rollingupdate_warmpool_test.go index 5ae6708ae001b..9d31c5889ec99 100644 --- a/pkg/instancegroups/rollingupdate_warmpool_test.go +++ b/pkg/instancegroups/rollingupdate_warmpool_test.go @@ -17,11 +17,11 @@ limitations under the License. package instancegroups import ( + "context" "testing" autoscalingtypes "github.com/aws/aws-sdk-go-v2/service/autoscaling/types" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/stretchr/testify/assert" "k8s.io/client-go/kubernetes" "k8s.io/kops/cloudmock/aws/mockautoscaling" @@ -29,6 +29,7 @@ import ( "k8s.io/kops/pkg/cloudinstances" "k8s.io/kops/pkg/validation" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" + "k8s.io/kops/util/pkg/awsinterfaces" ) // Here we have three nodes that are up to date, while three warm nodes need updating. @@ -102,13 +103,13 @@ func makeGroupWithWarmPool(groups map[string]*cloudinstances.CloudInstanceGroup, } type warmPoolBeforeJoinedNodesTest struct { - ec2iface.EC2API + awsinterfaces.EC2API t *testing.T numTerminations int } -func (t *warmPoolBeforeJoinedNodesTest) TerminateInstances(input *ec2.TerminateInstancesInput) (*ec2.TerminateInstancesOutput, error) { +func (t *warmPoolBeforeJoinedNodesTest) TerminateInstances(ctx context.Context, input *ec2.TerminateInstancesInput, optFns ...func(*ec2.Options)) (*ec2.TerminateInstancesOutput, error) { t.numTerminations++ - return t.EC2API.TerminateInstances(input) + return t.EC2API.TerminateInstances(ctx, input, optFns...) } diff --git a/upup/pkg/fi/cloudup/awsup/aws_apitarget.go b/upup/pkg/fi/cloudup/awsup/aws_apitarget.go index 7ea25cdb68c00..ddd8ea6fca59d 100644 --- a/upup/pkg/fi/cloudup/awsup/aws_apitarget.go +++ b/upup/pkg/fi/cloudup/awsup/aws_apitarget.go @@ -20,7 +20,6 @@ import ( "fmt" "time" - "github.com/aws/aws-sdk-go-v2/aws" "k8s.io/klog/v2" "k8s.io/kops/upup/pkg/fi" @@ -185,7 +184,7 @@ func (t *AWSAPITarget) WaitForInstanceRunning(instanceID string) error { state := "?" if instance.State != nil { - state = aws.ToString(instance.State.Name) + state = string(instance.State.Name) } if state == "running" { return nil diff --git a/upup/pkg/fi/cloudup/awsup/aws_cloud.go b/upup/pkg/fi/cloudup/awsup/aws_cloud.go index 57819413d428d..dfbc91ae356c4 100644 --- a/upup/pkg/fi/cloudup/awsup/aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/aws_cloud.go @@ -169,7 +169,7 @@ type AWSCloud interface { FindELBV2NetworkInterfacesByName(vpcID string, loadBalancerName string) ([]ec2types.NetworkInterface, error) // DescribeInstance is a helper that queries for the specified instance by id - DescribeInstance(instanceID string) (*ec2.Instance, error) + DescribeInstance(instanceID string) (*ec2types.Instance, error) // DescribeVPC is a helper that queries for the specified vpc by id DescribeVPC(vpcID string) (*ec2types.Vpc, error) @@ -460,16 +460,16 @@ func (c *awsCloudImplementation) DeleteGroup(g *cloudinstances.CloudInstanceGrou func deleteGroup(ctx context.Context, c AWSCloud, g *cloudinstances.CloudInstanceGroup) error { asg := g.Raw.(*autoscalingtypes.AutoScalingGroup) - name := aws.StringValue(asg.AutoScalingGroupName) - template := aws.StringValue(asg.LaunchConfigurationName) + name := aws.ToString(asg.AutoScalingGroupName) + template := aws.ToString(asg.LaunchConfigurationName) launchTemplate := "" if asg.LaunchTemplate != nil { - launchTemplate = aws.StringValue(asg.LaunchTemplate.LaunchTemplateName) + launchTemplate = aws.ToString(asg.LaunchTemplate.LaunchTemplateName) } // Delete detached instances { - detached, err := findDetachedInstances(c, asg) + detached, err := findDetachedInstances(ctx, c, asg) if err != nil { return fmt.Errorf("error searching for detached instances for autoscaling group %q: %v", name, err) } @@ -478,7 +478,7 @@ func deleteGroup(ctx context.Context, c AWSCloud, g *cloudinstances.CloudInstanc req := &ec2.TerminateInstancesInput{ InstanceIds: detached, } - if _, err := c.EC2().TerminateInstances(req); err != nil { + if _, err := c.EC2().TerminateInstances(ctx, req); err != nil { return fmt.Errorf("error deleting detached instances for autoscaling group %q: %v", name, err) } } @@ -505,7 +505,7 @@ func deleteGroup(ctx context.Context, c AWSCloud, g *cloudinstances.CloudInstanc req := &ec2.DeleteLaunchTemplateInput{ LaunchTemplateName: aws.String(launchTemplate), } - _, err := c.EC2().DeleteLaunchTemplate(req) + _, err := c.EC2().DeleteLaunchTemplate(ctx, req) if err != nil { return fmt.Errorf("error deleting autoscaling launch template %q: %v", launchTemplate, err) } @@ -531,17 +531,18 @@ func deleteGroup(ctx context.Context, c AWSCloud, g *cloudinstances.CloudInstanc // DeleteInstance deletes an aws instance func (c *awsCloudImplementation) DeleteInstance(i *cloudinstances.CloudInstance) error { + ctx := context.TODO() if c.spotinst != nil { if featureflag.SpotinstHybrid.Enabled() { if _, ok := i.CloudInstanceGroup.Raw.(*autoscalingtypes.AutoScalingGroup); ok { - return deleteInstance(c, i) + return deleteInstance(ctx, c, i) } } return spotinst.DeleteInstance(c.spotinst, i) } - return deleteInstance(c, i) + return deleteInstance(ctx, c, i) } // DeregisterInstance drains a cloud instance and load balancers. @@ -560,17 +561,17 @@ func (c *awsCloudImplementation) DeregisterInstance(i *cloudinstances.CloudInsta return nil } -func deleteInstance(c AWSCloud, i *cloudinstances.CloudInstance) error { +func deleteInstance(ctx context.Context, c AWSCloud, i *cloudinstances.CloudInstance) error { id := i.ID if id == "" { return fmt.Errorf("id was not set on CloudInstance: %v", i) } request := &ec2.TerminateInstancesInput{ - InstanceIds: []*string{aws.String(id)}, + InstanceIds: []string{id}, } - if _, err := c.EC2().TerminateInstances(request); err != nil { + if _, err := c.EC2().TerminateInstances(ctx, request); err != nil { if AWSErrorCode(err) == "InvalidInstanceID.NotFound" { klog.V(2).Infof("Got InvalidInstanceID.NotFound error deleting instance %q; will treat as already-deleted", id) } else { @@ -588,7 +589,7 @@ func deregisterInstance(ctx context.Context, c AWSCloud, i *cloudinstances.Cloud asg := i.CloudInstanceGroup.Raw.(*autoscalingtypes.AutoScalingGroup) asgDetails, err := c.Autoscaling().DescribeAutoScalingGroups(ctx, &autoscaling.DescribeAutoScalingGroupsInput{ - AutoScalingGroupNames: []string{aws.StringValue(asg.AutoScalingGroupName)}, + AutoScalingGroupNames: []string{aws.ToString(asg.AutoScalingGroupName)}, }) if err != nil { return fmt.Errorf("error describing autoScalingGroups: %v", err) @@ -646,7 +647,7 @@ func deregisterInstanceFromClassicLoadBalancer(ctx context.Context, c AWSCloud, } // there will be only one instance in the DescribeInstanceHealth response. - if aws.StringValue(response.InstanceStates[0].State) == instanceInServiceState { + if aws.ToString(response.InstanceStates[0].State) == instanceInServiceState { c.ELB().DeregisterInstancesFromLoadBalancer(ctx, &elb.DeregisterInstancesFromLoadBalancerInput{ LoadBalancerName: aws.String(loadBalancerName), Instances: []elbtypes.Instance{{ @@ -827,26 +828,28 @@ func getKarpenterGroups(c AWSCloud, cluster *kops.Cluster, instancegroups []*kop } func buildKarpenterGroup(c AWSCloud, cluster *kops.Cluster, ig *kops.InstanceGroup, nodes []v1.Node) (*cloudinstances.CloudInstanceGroup, error) { + ctx := context.TODO() nodeMap := cloudinstances.GetNodeMap(nodes, cluster) - instances := make(map[string]*ec2.Instance) - updatedInstances := make(map[string]*ec2.Instance) + instances := make(map[string]*ec2types.Instance) + updatedInstances := make(map[string]*ec2types.Instance) clusterName := c.Tags()[TagClusterName] var version string { input := &ec2.DescribeLaunchTemplatesInput{ - Filters: []*ec2.Filter{ + Filters: []ec2types.Filter{ NewEC2Filter("tag:"+identity_aws.CloudTagInstanceGroupName, ig.ObjectMeta.Name), NewEC2Filter("tag:"+TagClusterName, clusterName), }, } - var list []*ec2.LaunchTemplate - err := c.EC2().DescribeLaunchTemplatesPages(input, func(p *ec2.DescribeLaunchTemplatesOutput, lastPage bool) (shouldContinue bool) { - list = append(list, p.LaunchTemplates...) - return true - }) - if err != nil { - return nil, err + var list []ec2types.LaunchTemplate + paginator := ec2.NewDescribeLaunchTemplatesPaginator(c.EC2(), input) + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("error listing launch templates: %v", err) + } + list = append(list, page.LaunchTemplates...) } lt := list[0] versionNumber := *lt.LatestVersionNumber @@ -860,22 +863,22 @@ func buildKarpenterGroup(c AWSCloud, cluster *kops.Cluster, ig *kops.InstanceGro } { req := &ec2.DescribeInstancesInput{ - Filters: []*ec2.Filter{ + Filters: []ec2types.Filter{ NewEC2Filter("tag:"+identity_aws.CloudTagInstanceGroupName, ig.ObjectMeta.Name), NewEC2Filter("tag:"+TagClusterName, clusterName), NewEC2Filter("instance-state-name", "pending", "running", "stopping", "stopped"), }, } - result, err := c.EC2().DescribeInstances(req) + result, err := c.EC2().DescribeInstances(ctx, req) if err != nil { return nil, err } for _, r := range result.Reservations { for _, i := range r.Instances { - id := aws.StringValue(i.InstanceId) - instances[id] = i + id := aws.ToString(i.InstanceId) + instances[id] = &i } } } @@ -884,7 +887,7 @@ func buildKarpenterGroup(c AWSCloud, cluster *kops.Cluster, ig *kops.InstanceGro { req := &ec2.DescribeInstancesInput{ - Filters: []*ec2.Filter{ + Filters: []ec2types.Filter{ NewEC2Filter("tag:"+identity_aws.CloudTagInstanceGroupName, ig.ObjectMeta.Name), NewEC2Filter("tag:"+TagClusterName, clusterName), NewEC2Filter("instance-state-name", "pending", "running", "stopping", "stopped"), @@ -892,15 +895,15 @@ func buildKarpenterGroup(c AWSCloud, cluster *kops.Cluster, ig *kops.InstanceGro }, } - result, err := c.EC2().DescribeInstances(req) + result, err := c.EC2().DescribeInstances(ctx, req) if err != nil { return nil, err } for _, r := range result.Reservations { for _, i := range r.Instances { - id := aws.StringValue(i.InstanceId) - updatedInstances[id] = i + id := aws.ToString(i.InstanceId) + updatedInstances[id] = &i } } } @@ -933,7 +936,7 @@ func getCloudGroups(ctx context.Context, c AWSCloud, cluster *kops.Cluster, inst } for _, asg := range asgs { - name := aws.StringValue(asg.AutoScalingGroupName) + name := aws.ToString(asg.AutoScalingGroupName) instancegroup, err := matchInstanceGroup(name, cluster.ObjectMeta.Name, instancegroups) if err != nil { @@ -986,7 +989,7 @@ func FindAutoscalingGroups(c AWSCloud, tags map[string]string) ([]*autoscalingty for _, t := range page.Tags { switch *t.ResourceType { case "auto-scaling-group": - asgNames = append(asgNames, aws.StringValue(t.ResourceId)) + asgNames = append(asgNames, aws.ToString(t.ResourceId)) default: klog.Warningf("Unknown resource type: %v", *t.ResourceType) } @@ -1053,8 +1056,8 @@ func matchesAsgTags(tags map[string]string, actual []autoscalingtypes.TagDescrip } // findAutoscalingGroupLaunchConfiguration is responsible for finding the launch - which could be a launchconfiguration, a template or a mixed instance policy template -func findAutoscalingGroupLaunchConfiguration(c AWSCloud, g *autoscalingtypes.AutoScalingGroup) (string, error) { - name := aws.StringValue(g.LaunchConfigurationName) +func findAutoscalingGroupLaunchConfiguration(ctx context.Context, c AWSCloud, g *autoscalingtypes.AutoScalingGroup) (string, error) { + name := aws.ToString(g.LaunchConfigurationName) if name != "" { return name, nil } @@ -1066,22 +1069,22 @@ func findAutoscalingGroupLaunchConfiguration(c AWSCloud, g *autoscalingtypes.Aut } else if g.MixedInstancesPolicy != nil && g.MixedInstancesPolicy.LaunchTemplate != nil && g.MixedInstancesPolicy.LaunchTemplate.LaunchTemplateSpecification != nil { launchTemplate = g.MixedInstancesPolicy.LaunchTemplate.LaunchTemplateSpecification } else { - return "", fmt.Errorf("error finding launch template or configuration for autoscaling group: %s", aws.StringValue(g.AutoScalingGroupName)) + return "", fmt.Errorf("error finding launch template or configuration for autoscaling group: %s", aws.ToString(g.AutoScalingGroupName)) } - id := aws.StringValue(launchTemplate.LaunchTemplateId) + id := aws.ToString(launchTemplate.LaunchTemplateId) if id == "" { - return "", fmt.Errorf("error finding launch template ID for autoscaling group: %s", aws.StringValue(g.AutoScalingGroupName)) + return "", fmt.Errorf("error finding launch template ID for autoscaling group: %s", aws.ToString(g.AutoScalingGroupName)) } - version := aws.StringValue(launchTemplate.Version) + version := aws.ToString(launchTemplate.Version) // Correctly Handle Default and Latest Versions klog.V(4).Infof("Launch Template Version Specified By ASG: %v", version) if version == "" || version == "$Default" || version == "$Latest" { input := &ec2.DescribeLaunchTemplatesInput{ - LaunchTemplateIds: []*string{&id}, + LaunchTemplateIds: []string{id}, } - output, err := c.EC2().DescribeLaunchTemplates(input) + output, err := c.EC2().DescribeLaunchTemplates(ctx, input) if err != nil { return "", fmt.Errorf("error describing launch templates: %q", err) } @@ -1102,15 +1105,15 @@ func findAutoscalingGroupLaunchConfiguration(c AWSCloud, g *autoscalingtypes.Aut // findInstanceLaunchConfiguration is responsible for discoverying the launch configuration for an instance func findInstanceLaunchConfiguration(i autoscalingtypes.Instance) string { - name := aws.StringValue(i.LaunchConfigurationName) + name := aws.ToString(i.LaunchConfigurationName) if name != "" { return name } // else we need to check the launch template if i.LaunchTemplate != nil { - id := aws.StringValue(i.LaunchTemplate.LaunchTemplateId) - version := aws.StringValue(i.LaunchTemplate.Version) + id := aws.ToString(i.LaunchTemplate.LaunchTemplateId) + version := aws.ToString(i.LaunchTemplate.Version) if id != "" { launchTemplate := id + ":" + version return launchTemplate @@ -1121,23 +1124,23 @@ func findInstanceLaunchConfiguration(i autoscalingtypes.Instance) string { } func awsBuildCloudInstanceGroup(ctx context.Context, c AWSCloud, cluster *kops.Cluster, ig *kops.InstanceGroup, g *autoscalingtypes.AutoScalingGroup, nodeMap map[string]*v1.Node) (*cloudinstances.CloudInstanceGroup, error) { - newConfigName, err := findAutoscalingGroupLaunchConfiguration(c, g) + newConfigName, err := findAutoscalingGroupLaunchConfiguration(ctx, c, g) if err != nil { return nil, err } instanceSeen := map[string]bool{} - instances, err := findInstances(c, ig) + instances, err := findInstances(ctx, c, ig) if err != nil { return nil, fmt.Errorf("failed to fetch instances: %v", err) } cg := &cloudinstances.CloudInstanceGroup{ - HumanName: aws.StringValue(g.AutoScalingGroupName), + HumanName: aws.ToString(g.AutoScalingGroupName), InstanceGroup: ig, - MinSize: int(aws.Int32Value(g.MinSize)), - TargetSize: int(aws.Int32Value(g.DesiredCapacity)), - MaxSize: int(aws.Int32Value(g.MaxSize)), + MinSize: int(aws.ToInt32(g.MinSize)), + TargetSize: int(aws.ToInt32(g.DesiredCapacity)), + MaxSize: int(aws.ToInt32(g.MaxSize)), Raw: g, } @@ -1163,7 +1166,7 @@ func awsBuildCloudInstanceGroup(ctx context.Context, c AWSCloud, cluster *kops.C var detached []*string for id, instance := range instances { for _, tag := range instance.Tags { - if aws.StringValue(tag.Key) == tagNameDetachedInstance { + if aws.ToString(tag.Key) == tagNameDetachedInstance { detached = append(detached, aws.String(id)) } } @@ -1178,15 +1181,15 @@ func awsBuildCloudInstanceGroup(ctx context.Context, c AWSCloud, cluster *kops.C return nil, fmt.Errorf("error creating cloud instance group member: %v", err) } instanceSeen[*id] = true - addCloudInstanceData(cm, instances[aws.StringValue(id)]) + addCloudInstanceData(cm, instances[aws.ToString(id)]) } } return cg, nil } -func buildCloudInstance(i autoscalingtypes.Instance, instances map[string]*ec2.Instance, instanceSeen map[string]bool, nodeMap map[string]*v1.Node, cg *cloudinstances.CloudInstanceGroup, newConfigName string) error { - id := aws.StringValue(i.InstanceId) +func buildCloudInstance(i autoscalingtypes.Instance, instances map[string]*ec2types.Instance, instanceSeen map[string]bool, nodeMap map[string]*v1.Node, cg *cloudinstances.CloudInstanceGroup, newConfigName string) error { + id := aws.ToString(i.InstanceId) if id == "" { klog.Warningf("ignoring instance with no instance id: %s in autoscaling group: %s", id, cg.HumanName) return nil @@ -1217,15 +1220,15 @@ func buildCloudInstance(i autoscalingtypes.Instance, instances map[string]*ec2.I return nil } -func addCloudInstanceData(cm *cloudinstances.CloudInstance, instance *ec2.Instance) { - cm.MachineType = aws.StringValue(instance.InstanceType) +func addCloudInstanceData(cm *cloudinstances.CloudInstance, instance *ec2types.Instance) { + cm.MachineType = string(instance.InstanceType) for _, tag := range instance.Tags { - key := aws.StringValue(tag.Key) + key := aws.ToString(tag.Key) if !strings.HasPrefix(key, TagNameRolePrefix) { continue } role := strings.TrimPrefix(key, TagNameRolePrefix) - cm.PrivateIP = aws.StringValue(instance.PrivateIpAddress) + cm.PrivateIP = aws.ToString(instance.PrivateIpAddress) if role == "master" || role == "control-plane" { cm.Roles = append(cm.Roles, "control-plane") } else { @@ -1234,48 +1237,48 @@ func addCloudInstanceData(cm *cloudinstances.CloudInstance, instance *ec2.Instan } } -func findInstances(c AWSCloud, ig *kops.InstanceGroup) (map[string]*ec2.Instance, error) { +func findInstances(ctx context.Context, c AWSCloud, ig *kops.InstanceGroup) (map[string]*ec2types.Instance, error) { clusterName := c.Tags()[TagClusterName] req := &ec2.DescribeInstancesInput{ - Filters: []*ec2.Filter{ + Filters: []ec2types.Filter{ NewEC2Filter("tag:"+identity_aws.CloudTagInstanceGroupName, ig.ObjectMeta.Name), NewEC2Filter("tag:"+TagClusterName, clusterName), NewEC2Filter("instance-state-name", "pending", "running", "stopping", "stopped"), }, } - result, err := c.EC2().DescribeInstances(req) + result, err := c.EC2().DescribeInstances(ctx, req) if err != nil { return nil, err } - instances := make(map[string]*ec2.Instance) + instances := make(map[string]*ec2types.Instance) for _, r := range result.Reservations { for _, i := range r.Instances { - id := aws.StringValue(i.InstanceId) - instances[id] = i + id := aws.ToString(i.InstanceId) + instances[id] = &i } } return instances, nil } -func findDetachedInstances(c AWSCloud, g *autoscalingtypes.AutoScalingGroup) ([]*string, error) { +func findDetachedInstances(ctx context.Context, c AWSCloud, g *autoscalingtypes.AutoScalingGroup) ([]string, error) { clusterName := c.Tags()[TagClusterName] req := &ec2.DescribeInstancesInput{ - Filters: []*ec2.Filter{ - NewEC2Filter("tag:"+tagNameDetachedInstance, aws.StringValue(g.AutoScalingGroupName)), + Filters: []ec2types.Filter{ + NewEC2Filter("tag:"+tagNameDetachedInstance, aws.ToString(g.AutoScalingGroupName)), NewEC2Filter("tag:"+TagClusterName, clusterName), NewEC2Filter("instance-state-name", "pending", "running", "stopping", "stopped"), }, } - result, err := c.EC2().DescribeInstances(req) + result, err := c.EC2().DescribeInstances(ctx, req) if err != nil { return nil, err } - var detached []*string + var detached []string for _, r := range result.Reservations { for _, i := range r.Instances { - detached = append(detached, i.InstanceId) + detached = append(detached, aws.ToString(i.InstanceId)) } } return detached, nil @@ -1928,13 +1931,14 @@ func buildFilters(commonTags map[string]string, name *string) []ec2types.Filter } // DescribeInstance is a helper that queries for the specified instance by id -func (c *awsCloudImplementation) DescribeInstance(instanceID string) (*ec2.Instance, error) { +func (c *awsCloudImplementation) DescribeInstance(instanceID string) (*ec2types.Instance, error) { klog.V(2).Infof("Calling DescribeInstances for instance %q", instanceID) + ctx := context.TODO() request := &ec2.DescribeInstancesInput{ - InstanceIds: []*string{&instanceID}, + InstanceIds: []string{instanceID}, } - response, err := c.EC2().DescribeInstances(request) + response, err := c.EC2().DescribeInstances(ctx, request) if err != nil { return nil, fmt.Errorf("error listing Instances: %v", err) } @@ -1955,7 +1959,7 @@ func (c *awsCloudImplementation) DescribeInstance(instanceID string) (*ec2.Insta } instance := reservation.Instances[0] - return instance, nil + return &instance, nil } // DescribeVPC is a helper that queries for the specified vpc by id diff --git a/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go b/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go index e212cab063bec..ff68fe575ce54 100644 --- a/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go @@ -91,7 +91,7 @@ func (c *MockAWSCloud) DeleteGroup(g *cloudinstances.CloudInstanceGroup) error { } func (c *MockAWSCloud) DeleteInstance(i *cloudinstances.CloudInstance) error { - return deleteInstance(c, i) + return deleteInstance(context.TODO(), c, i) } func (c *MockAWSCloud) DeregisterInstance(i *cloudinstances.CloudInstance) error { @@ -213,7 +213,7 @@ func (c *MockAWSCloud) FindELBV2NetworkInterfacesByName(vpcID, loadBalancerName return nil, nil } -func (c *MockAWSCloud) DescribeInstance(instanceID string) (*ec2.Instance, error) { +func (c *MockAWSCloud) DescribeInstance(instanceID string) (*ec2types.Instance, error) { return nil, fmt.Errorf("MockAWSCloud DescribeInstance not implemented") } From 2bf59688c2c9d0bad8e265c652d85ce3c34099ef Mon Sep 17 00:00:00 2001 From: Peter Rifel Date: Tue, 9 Apr 2024 21:24:58 -0500 Subject: [PATCH 06/14] Migrate instance types to aws-sdk-go-v2 --- nodeup/pkg/model/kubelet.go | 9 +-- pkg/testutils/integrationtestharness.go | 4 +- upup/pkg/fi/cloudup/awsup/aws_cloud.go | 59 +++++++++++-------- upup/pkg/fi/cloudup/awsup/machine_types.go | 41 ++++++------- upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go | 50 ++++++++-------- upup/pkg/fi/cloudup/new_cluster.go | 15 ++--- .../fi/cloudup/populate_instancegroup_spec.go | 3 +- upup/pkg/fi/cloudup/template_functions.go | 37 ++++++------ upup/pkg/fi/nodeup/command.go | 3 +- 9 files changed, 117 insertions(+), 104 deletions(-) diff --git a/nodeup/pkg/model/kubelet.go b/nodeup/pkg/model/kubelet.go index 12236f58a440d..6bfa7c073aaa6 100644 --- a/nodeup/pkg/model/kubelet.go +++ b/nodeup/pkg/model/kubelet.go @@ -29,6 +29,7 @@ import ( awsconfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/klog/v2" @@ -647,18 +648,18 @@ func (b *KubeletBuilder) buildKubeletConfigSpec(ctx context.Context) (*kops.Kube } metadata := imds.NewFromConfig(config) - var instanceTypeName string + var instanceTypeName ec2types.InstanceType // Get the actual instance type by querying the EC2 instance metadata service. resp, err := metadata.GetMetadata(ctx, &imds.GetMetadataInput{Path: "instance-type"}) if err == nil { defer resp.Content.Close() itName, err := io.ReadAll(resp.Content) if err == nil { - instanceTypeName = string(itName) + instanceTypeName = ec2types.InstanceType(string(itName)) } } if instanceTypeName == "" { - instanceTypeName = *b.NodeupConfig.DefaultMachineType + instanceTypeName = ec2types.InstanceType(*b.NodeupConfig.DefaultMachineType) } awsCloud := b.Cloud.(awsup.AWSCloud) @@ -669,7 +670,7 @@ func (b *KubeletBuilder) buildKubeletConfigSpec(ctx context.Context) (*kops.Kube } // Default maximum pods per node defined by KubeletConfiguration - maxPods := 110 + maxPods := int32(110) // AWS VPC CNI plugin-specific maximum pod calculation based on: // https://github.com/aws/amazon-vpc-cni-k8s/blob/v1.9.3/README.md#setup diff --git a/pkg/testutils/integrationtestharness.go b/pkg/testutils/integrationtestharness.go index ec4f89db5d56e..5134220c4dd78 100644 --- a/pkg/testutils/integrationtestharness.go +++ b/pkg/testutils/integrationtestharness.go @@ -179,13 +179,13 @@ func (h *IntegrationTestHarness) SetupMockAWS() *awsup.MockAWSCloud { VPCId: aws.String("vpc-12345678"), }}) - mockEC2.Images = append(mockEC2.Images, &ec2.Image{ + mockEC2.Images = append(mockEC2.Images, &ec2types.Image{ CreationDate: aws.String("2022-04-04T00:00:00.000Z"), ImageId: aws.String("ami-12345678"), Name: aws.String("images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20220404"), OwnerId: aws.String(awsup.WellKnownAccountUbuntu), RootDeviceName: aws.String("/dev/xvda"), - Architecture: aws.String("x86_64"), + Architecture: ec2types.ArchitectureValuesX8664, }) mockEC2.CreateVpcWithId(&ec2.CreateVpcInput{ diff --git a/upup/pkg/fi/cloudup/awsup/aws_cloud.go b/upup/pkg/fi/cloudup/awsup/aws_cloud.go index dfbc91ae356c4..e24737c7e971e 100644 --- a/upup/pkg/fi/cloudup/awsup/aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/aws_cloud.go @@ -189,7 +189,7 @@ type AWSCloud interface { DefaultInstanceType(cluster *kops.Cluster, ig *kops.InstanceGroup) (string, error) // DescribeInstanceType calls ec2.DescribeInstanceType to get information for a particular instance type - DescribeInstanceType(instanceType string) (*ec2.InstanceTypeInfo, error) + DescribeInstanceType(instanceType string) (*ec2types.InstanceTypeInfo, error) // AccountInfo returns the AWS account ID and AWS partition that we are deploying into AccountInfo(ctx context.Context) (string, string, error) @@ -2274,24 +2274,33 @@ func findDNSName(cloud AWSCloud, cluster *kops.Cluster) (string, error) { // DefaultInstanceType determines an instance type for the specified cluster & instance group func (c *awsCloudImplementation) DefaultInstanceType(cluster *kops.Cluster, ig *kops.InstanceGroup) (string, error) { - var candidates []string + var candidates []ec2types.InstanceType switch ig.Spec.Role { case kops.InstanceGroupRoleControlPlane, kops.InstanceGroupRoleNode, kops.InstanceGroupRoleAPIServer: // t3.medium is the cheapest instance with 4GB of mem, unlimited by default, fast and has decent network // c5.large and c4.large are a good second option in case t3.medium is not available in the AZ - candidates = []string{"t3.medium", "c5.large", "c4.large", "t4g.medium"} + candidates = []ec2types.InstanceType{ + ec2types.InstanceTypeT3Medium, + ec2types.InstanceTypeC5Large, + ec2types.InstanceTypeC4Large, + ec2types.InstanceTypeT4gMedium, + } case kops.InstanceGroupRoleBastion: - candidates = []string{"t3.micro", "t2.micro", "t4g.micro"} + candidates = []ec2types.InstanceType{ + ec2types.InstanceTypeT3Micro, + ec2types.InstanceTypeT2Micro, + ec2types.InstanceTypeT4gMicro, + } default: return "", fmt.Errorf("unhandled role %q", ig.Spec.Role) } - imageArch := "x86_64" + imageArch := ec2types.ArchitectureTypeX8664 if imageInfo, err := c.ResolveImage(ig.Spec.Image); err == nil { - imageArch = fi.ValueOf(imageInfo.Architecture) + imageArch = ec2types.ArchitectureType(imageInfo.Architecture) } // Find the AZs the InstanceGroup targets @@ -2304,13 +2313,13 @@ func (c *awsCloudImplementation) DefaultInstanceType(cluster *kops.Cluster, ig * // TODO: Validate that instance type exists in all AZs, but skip AZs that don't support any VPC stuff var reasons []string for _, instanceType := range candidates { - if strings.HasPrefix(instanceType, "t4g") { - if imageArch != "arm64" { + if strings.HasPrefix(string(instanceType), "t4g") { + if imageArch != ec2types.ArchitectureTypeArm64 { reasons = append(reasons, fmt.Sprintf("instance type %q does not match image architecture %q", instanceType, imageArch)) continue } } else { - if imageArch == "arm64" { + if imageArch == ec2types.ArchitectureTypeArm64 { reasons = append(reasons, fmt.Sprintf("instance type %q does not match image architecture %q", instanceType, imageArch)) continue } @@ -2321,7 +2330,7 @@ func (c *awsCloudImplementation) DefaultInstanceType(cluster *kops.Cluster, ig * return "", err } if zones.IsSuperset(igZonesSet) { - return instanceType, nil + return string(instanceType), nil } else { reasons = append(reasons, fmt.Sprintf("instance type %q is not available in all zones (available in zones %v, need %v)", instanceType, zones, igZones)) klog.V(2).Infof("can't use instance type %q, available in zones %v but need %v", instanceType, zones, igZones) @@ -2337,26 +2346,27 @@ func (c *awsCloudImplementation) DefaultInstanceType(cluster *kops.Cluster, ig * } // supportsInstanceType uses the DescribeReservedInstancesOfferings API call to determine if an instance type is supported in a region -func (c *awsCloudImplementation) zonesWithInstanceType(instanceType string) (sets.String, error) { +func (c *awsCloudImplementation) zonesWithInstanceType(instanceType ec2types.InstanceType) (sets.String, error) { klog.V(4).Infof("checking if instance type %q is supported in region %q", instanceType, c.region) + ctx := context.TODO() request := &ec2.DescribeReservedInstancesOfferingsInput{} - request.InstanceTenancy = aws.String("default") + request.InstanceTenancy = ec2types.TenancyDefault request.IncludeMarketplace = aws.Bool(false) - request.OfferingClass = aws.String(ec2.OfferingClassTypeStandard) - request.OfferingType = aws.String(ec2.OfferingTypeValuesNoUpfront) - request.ProductDescription = aws.String(ec2.RIProductDescriptionLinuxUnixamazonVpc) - request.InstanceType = aws.String(instanceType) + request.OfferingClass = ec2types.OfferingClassTypeStandard + request.OfferingType = ec2types.OfferingTypeValuesNoUpfront + request.ProductDescription = ec2types.RIProductDescriptionLinuxUnixAmazonVpc + request.InstanceType = instanceType zones := sets.NewString() - response, err := c.ec2.DescribeReservedInstancesOfferings(request) + response, err := c.ec2.DescribeReservedInstancesOfferings(ctx, request) if err != nil { return zones, fmt.Errorf("error checking if instance type %q is supported in region %q: %v", instanceType, c.region, err) } for _, item := range response.ReservedInstancesOfferings { - if aws.StringValue(item.InstanceType) == instanceType { - zones.Insert(aws.StringValue(item.AvailabilityZone)) + if item.InstanceType == instanceType { + zones.Insert(aws.ToString(item.AvailabilityZone)) } else { klog.Warningf("skipping non-matching instance type offering: %v", item) } @@ -2366,7 +2376,7 @@ func (c *awsCloudImplementation) zonesWithInstanceType(instanceType string) (set } // DescribeInstanceType calls ec2.DescribeInstanceType to get information for a particular instance type -func (c *awsCloudImplementation) DescribeInstanceType(instanceType string) (*ec2.InstanceTypeInfo, error) { +func (c *awsCloudImplementation) DescribeInstanceType(instanceType string) (*ec2types.InstanceTypeInfo, error) { if info, ok := c.instanceTypes.typeMap[instanceType]; ok { return info, nil } @@ -2381,18 +2391,19 @@ func (c *awsCloudImplementation) DescribeInstanceType(instanceType string) (*ec2 return info, nil } -func describeInstanceType(c AWSCloud, instanceType string) (*ec2.InstanceTypeInfo, error) { +func describeInstanceType(c AWSCloud, instanceType string) (*ec2types.InstanceTypeInfo, error) { + ctx := context.TODO() req := &ec2.DescribeInstanceTypesInput{ - InstanceTypes: aws.StringSlice([]string{instanceType}), + InstanceTypes: []ec2types.InstanceType{ec2types.InstanceType(instanceType)}, } - resp, err := c.EC2().DescribeInstanceTypes(req) + resp, err := c.EC2().DescribeInstanceTypes(ctx, req) if err != nil { return nil, fmt.Errorf("describing instance type %q in region %q: %w", instanceType, c.Region(), err) } if len(resp.InstanceTypes) != 1 { return nil, fmt.Errorf("instance type %q not found in region %q", instanceType, c.Region()) } - return resp.InstanceTypes[0], nil + return &resp.InstanceTypes[0], nil } // AccountInfo returns the AWS account ID and AWS partition that we are deploying into diff --git a/upup/pkg/fi/cloudup/awsup/machine_types.go b/upup/pkg/fi/cloudup/awsup/machine_types.go index f182411560f03..28e36781a57d5 100644 --- a/upup/pkg/fi/cloudup/awsup/machine_types.go +++ b/upup/pkg/fi/cloudup/awsup/machine_types.go @@ -22,28 +22,29 @@ import ( "sync" "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" ) type AWSMachineTypeInfo struct { - Name string + Name ec2types.InstanceType MemoryGB float32 - Cores int - EphemeralDisks []int + Cores int32 + EphemeralDisks []int64 GPU bool - MaxPods int - InstanceENIs int - InstanceIPsPerENI int + MaxPods int32 + InstanceENIs int32 + InstanceIPsPerENI int32 } type EphemeralDevice struct { DeviceName string VirtualName string - SizeGB int + SizeGB int64 } var ( - machineTypeInfo map[string]*AWSMachineTypeInfo + machineTypeInfo map[ec2types.InstanceType]*AWSMachineTypeInfo machineTypeMutex sync.Mutex ) @@ -66,36 +67,36 @@ func (m *AWSMachineTypeInfo) EphemeralDevices() []*EphemeralDevice { return disks } -func GetMachineTypeInfo(c AWSCloud, machineType string) (*AWSMachineTypeInfo, error) { +func GetMachineTypeInfo(c AWSCloud, machineType ec2types.InstanceType) (*AWSMachineTypeInfo, error) { machineTypeMutex.Lock() defer machineTypeMutex.Unlock() if machineTypeInfo == nil { - machineTypeInfo = make(map[string]*AWSMachineTypeInfo) + machineTypeInfo = make(map[ec2types.InstanceType]*AWSMachineTypeInfo) } else if i, ok := machineTypeInfo[machineType]; ok { return i, nil } - info, err := c.DescribeInstanceType(machineType) + info, err := c.DescribeInstanceType(string(machineType)) if err != nil { return nil, err } machine := AWSMachineTypeInfo{ Name: machineType, GPU: info.GpuInfo != nil, - InstanceENIs: intValue(info.NetworkInfo.MaximumNetworkInterfaces), - InstanceIPsPerENI: intValue(info.NetworkInfo.Ipv4AddressesPerInterface), + InstanceENIs: aws.ToInt32(info.NetworkInfo.MaximumNetworkInterfaces), + InstanceIPsPerENI: aws.ToInt32(info.NetworkInfo.Ipv4AddressesPerInterface), } - memoryGB := float64(intValue(info.MemoryInfo.SizeInMiB)) / 1024 + memoryGB := float64(aws.ToInt64(info.MemoryInfo.SizeInMiB)) / 1024 machine.MemoryGB = float32(math.Round(memoryGB*100) / 100) if info.VCpuInfo != nil && info.VCpuInfo.DefaultVCpus != nil { - machine.Cores = intValue(info.VCpuInfo.DefaultVCpus) + machine.Cores = aws.ToInt32(info.VCpuInfo.DefaultVCpus) } if info.InstanceStorageInfo != nil && len(info.InstanceStorageInfo.Disks) > 0 { - disks := make([]int, 0) + disks := make([]int64, 0) for _, disk := range info.InstanceStorageInfo.Disks { - for i := 0; i < intValue(disk.Count); i++ { - disks = append(disks, intValue(disk.SizeInGB)) + for i := int32(0); i < aws.ToInt32(disk.Count); i++ { + disks = append(disks, aws.ToInt64(disk.SizeInGB)) } } machine.EphemeralDisks = disks @@ -104,7 +105,3 @@ func GetMachineTypeInfo(c AWSCloud, machineType string) (*AWSMachineTypeInfo, er return &machine, nil } - -func intValue(v *int64) int { - return int(aws.ToInt64(v)) -} diff --git a/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go b/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go index ff68fe575ce54..f8266253ab6be 100644 --- a/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go @@ -325,27 +325,27 @@ func (c *MockAWSCloud) DefaultInstanceType(cluster *kops.Cluster, ig *kops.Insta } // DescribeInstanceType calls ec2.DescribeInstanceType to get information for a particular instance type -func (c *MockAWSCloud) DescribeInstanceType(instanceType string) (*ec2.InstanceTypeInfo, error) { +func (c *MockAWSCloud) DescribeInstanceType(instanceType string) (*ec2types.InstanceTypeInfo, error) { if instanceType == "t2.invalidType" { return nil, fmt.Errorf("invalid instance type %q specified", "t2.invalidType") } - info := &ec2.InstanceTypeInfo{ - NetworkInfo: &ec2.NetworkInfo{ - MaximumNetworkInterfaces: aws.Int64(1), - Ipv4AddressesPerInterface: aws.Int64(1), + info := &ec2types.InstanceTypeInfo{ + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkInterfaces: aws.Int32(1), + Ipv4AddressesPerInterface: aws.Int32(1), }, - MemoryInfo: &ec2.MemoryInfo{ + MemoryInfo: &ec2types.MemoryInfo{ SizeInMiB: aws.Int64(1024), }, - VCpuInfo: &ec2.VCpuInfo{ - DefaultVCpus: aws.Int64(2), + VCpuInfo: &ec2types.VCpuInfo{ + DefaultVCpus: aws.Int32(2), }, } if instanceType == "m3.medium" { - info.InstanceStorageInfo = &ec2.InstanceStorageInfo{ - Disks: []*ec2.DiskInfo{ + info.InstanceStorageInfo = &ec2types.InstanceStorageInfo{ + Disks: []ec2types.DiskInfo{ { - Count: aws.Int64(1), + Count: aws.Int32(1), SizeInGB: aws.Int64(1024), }, }, @@ -354,31 +354,31 @@ func (c *MockAWSCloud) DescribeInstanceType(instanceType string) (*ec2.InstanceT switch instanceType { case "c5.large", "m3.medium", "m4.large", "m5.large", "m5.xlarge", "t3.micro", "t3.medium", "t3.large", "c4.large": - info.ProcessorInfo = &ec2.ProcessorInfo{ - SupportedArchitectures: []*string{ - aws.String(ec2.ArchitectureTypeX8664), + info.ProcessorInfo = &ec2types.ProcessorInfo{ + SupportedArchitectures: []ec2types.ArchitectureType{ + ec2types.ArchitectureTypeX8664, }, } case "a1.large", "m6g.xlarge": - info.ProcessorInfo = &ec2.ProcessorInfo{ - SupportedArchitectures: []*string{ - aws.String(ec2.ArchitectureTypeArm64), + info.ProcessorInfo = &ec2types.ProcessorInfo{ + SupportedArchitectures: []ec2types.ArchitectureType{ + ec2types.ArchitectureTypeArm64, }, } case "t2.micro", "t2.medium": - info.ProcessorInfo = &ec2.ProcessorInfo{ - SupportedArchitectures: []*string{ - aws.String(ec2.ArchitectureTypeI386), - aws.String(ec2.ArchitectureTypeX8664), + info.ProcessorInfo = &ec2types.ProcessorInfo{ + SupportedArchitectures: []ec2types.ArchitectureType{ + ec2types.ArchitectureTypeI386, + ec2types.ArchitectureTypeX8664, }, } case "g4dn.xlarge", "g4ad.16xlarge": - info.ProcessorInfo = &ec2.ProcessorInfo{ - SupportedArchitectures: []*string{ - aws.String(ec2.ArchitectureTypeX8664), + info.ProcessorInfo = &ec2types.ProcessorInfo{ + SupportedArchitectures: []ec2types.ArchitectureType{ + ec2types.ArchitectureTypeX8664, }, } - info.GpuInfo = &ec2.GpuInfo{} + info.GpuInfo = &ec2types.GpuInfo{} } return info, nil diff --git a/upup/pkg/fi/cloudup/new_cluster.go b/upup/pkg/fi/cloudup/new_cluster.go index 788243dd42f44..536968c9c29c6 100644 --- a/upup/pkg/fi/cloudup/new_cluster.go +++ b/upup/pkg/fi/cloudup/new_cluster.go @@ -17,12 +17,13 @@ limitations under the License. package cloudup import ( + "context" "fmt" "strconv" "strings" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/blang/semver/v4" "k8s.io/apimachinery/pkg/api/resource" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -1669,16 +1670,16 @@ func MachineArchitecture(cloud fi.Cloud, machineType string) (architectures.Arch if info.ProcessorInfo == nil || len(info.ProcessorInfo.SupportedArchitectures) == 0 { return "", fmt.Errorf("unable to determine architecture info for instance type %q", machineType) } - var unsupported []string + var unsupported []ec2types.ArchitectureType for _, arch := range info.ProcessorInfo.SupportedArchitectures { // Return the first found supported architecture, in order of popularity - switch fi.ValueOf(arch) { - case ec2.ArchitectureTypeX8664: + switch arch { + case ec2types.ArchitectureTypeX8664: return architectures.ArchitectureAmd64, nil - case ec2.ArchitectureTypeArm64: + case ec2types.ArchitectureTypeArm64: return architectures.ArchitectureArm64, nil default: - unsupported = append(unsupported, fi.ValueOf(arch)) + unsupported = append(unsupported, arch) } } return "", fmt.Errorf("unsupported architecture for instance type %q: %v", machineType, unsupported) diff --git a/upup/pkg/fi/cloudup/populate_instancegroup_spec.go b/upup/pkg/fi/cloudup/populate_instancegroup_spec.go index ddbed298cf798..78d057bc4d80c 100644 --- a/upup/pkg/fi/cloudup/populate_instancegroup_spec.go +++ b/upup/pkg/fi/cloudup/populate_instancegroup_spec.go @@ -20,6 +20,7 @@ import ( "fmt" "strings" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" @@ -200,7 +201,7 @@ func PopulateInstanceGroupSpec(cluster *kops.Cluster, input *kops.InstanceGroup, switch cluster.Spec.GetCloudProvider() { case kops.CloudProviderAWS: if clusterNvidia || igNvidia { - mt, err := awsup.GetMachineTypeInfo(cloud.(awsup.AWSCloud), ig.Spec.MachineType) + mt, err := awsup.GetMachineTypeInfo(cloud.(awsup.AWSCloud), ec2types.InstanceType(ig.Spec.MachineType)) if err != nil { return ig, fmt.Errorf("error looking up machine type info: %v", err) } diff --git a/upup/pkg/fi/cloudup/template_functions.go b/upup/pkg/fi/cloudup/template_functions.go index 1881d23472261..b4de81c63e02e 100644 --- a/upup/pkg/fi/cloudup/template_functions.go +++ b/upup/pkg/fi/cloudup/template_functions.go @@ -995,6 +995,7 @@ func (tf *TemplateFunctions) podIdentityWebhookConfigMapData() (string, error) { } func karpenterInstanceTypes(cloud awsup.AWSCloud, ig kops.InstanceGroupSpec) ([]string, error) { + ctx := context.TODO() var mixedInstancesPolicy *kops.MixedInstancesPolicySpec if ig.MachineType == "" && ig.MixedInstancesPolicy == nil { @@ -1033,55 +1034,55 @@ func karpenterInstanceTypes(cloud awsup.AWSCloud, ig kops.InstanceGroupSpec) ([] arch := ami.Architecture hv := ami.VirtualizationType - ir := &ec2.InstanceRequirementsRequest{ - VCpuCount: &ec2.VCpuCountRangeRequest{}, - MemoryMiB: &ec2.MemoryMiBRequest{}, - BurstablePerformance: fi.PtrTo("included"), - InstanceGenerations: []*string{fi.PtrTo("current")}, + ir := &ec2types.InstanceRequirementsRequest{ + VCpuCount: &ec2types.VCpuCountRangeRequest{}, + MemoryMiB: &ec2types.MemoryMiBRequest{}, + BurstablePerformance: ec2types.BurstablePerformanceIncluded, + InstanceGenerations: []ec2types.InstanceGeneration{ec2types.InstanceGenerationCurrent}, } cpu := instanceRequirements.CPU if cpu != nil { if cpu.Max != nil { cpuMax, _ := instanceRequirements.CPU.Max.AsInt64() - ir.VCpuCount.Max = &cpuMax + ir.VCpuCount.Max = fi.PtrTo(int32(cpuMax)) } cpu := instanceRequirements.CPU if cpu != nil { if cpu.Max != nil { cpuMax, _ := instanceRequirements.CPU.Max.AsInt64() - ir.VCpuCount.Max = &cpuMax + ir.VCpuCount.Max = fi.PtrTo(int32(cpuMax)) } if cpu.Min != nil { cpuMin, _ := instanceRequirements.CPU.Min.AsInt64() - ir.VCpuCount.Min = &cpuMin + ir.VCpuCount.Min = fi.PtrTo(int32(cpuMin)) } } else { - ir.VCpuCount.Min = fi.PtrTo(int64(0)) + ir.VCpuCount.Min = fi.PtrTo(int32(0)) } memory := instanceRequirements.Memory if memory != nil { if memory.Max != nil { memoryMax := instanceRequirements.Memory.Max.ScaledValue(resource.Mega) - ir.MemoryMiB.Max = &memoryMax + ir.MemoryMiB.Max = fi.PtrTo(int32(memoryMax)) } if memory.Min != nil { memoryMin := instanceRequirements.Memory.Min.ScaledValue(resource.Mega) - ir.MemoryMiB.Min = &memoryMin + ir.MemoryMiB.Min = fi.PtrTo(int32(memoryMin)) } } else { - ir.MemoryMiB.Min = fi.PtrTo(int64(0)) + ir.MemoryMiB.Min = fi.PtrTo(int32(0)) } - ir.AcceleratorCount = &ec2.AcceleratorCountRequest{ - Min: fi.PtrTo(int64(0)), - Max: fi.PtrTo(int64(0)), + ir.AcceleratorCount = &ec2types.AcceleratorCountRequest{ + Min: fi.PtrTo(int32(0)), + Max: fi.PtrTo(int32(0)), } - response, err := cloud.EC2().GetInstanceTypesFromInstanceRequirements( + response, err := cloud.EC2().GetInstanceTypesFromInstanceRequirements(ctx, &ec2.GetInstanceTypesFromInstanceRequirementsInput{ - ArchitectureTypes: []*string{arch}, - VirtualizationTypes: []*string{hv}, + ArchitectureTypes: []ec2types.ArchitectureType{ec2types.ArchitectureType(arch)}, + VirtualizationTypes: []ec2types.VirtualizationType{hv}, InstanceRequirements: ir, }, ) diff --git a/upup/pkg/fi/nodeup/command.go b/upup/pkg/fi/nodeup/command.go index 5ee54a18dcd4b..6bab6a176fefe 100644 --- a/upup/pkg/fi/nodeup/command.go +++ b/upup/pkg/fi/nodeup/command.go @@ -35,6 +35,7 @@ import ( awsconfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" "github.com/aws/aws-sdk-go-v2/service/autoscaling" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/aws-sdk-go-v2/service/kms" "github.com/aws/aws-sdk-go/aws/awserr" "go.uber.org/multierr" @@ -264,7 +265,7 @@ func (c *NodeUpCommand) Run(out io.Writer) error { if nvidia != nil && fi.ValueOf(nvidia.Enabled) { awsCloud := cloud.(awsup.AWSCloud) // Get the instance type's detailed information. - instanceType, err := awsup.GetMachineTypeInfo(awsCloud, modelContext.MachineType) + instanceType, err := awsup.GetMachineTypeInfo(awsCloud, ec2types.InstanceType(modelContext.MachineType)) if err != nil { return err } From 3faa28b65eb90250064a483d208e0f9e9a881aaf Mon Sep 17 00:00:00 2001 From: Peter Rifel Date: Tue, 9 Apr 2024 21:29:47 -0500 Subject: [PATCH 07/14] Migrate Availability Zones to aws-sdk-go-v2 --- pkg/zones/wellknown.go | 7 +++++-- upup/pkg/fi/cloudup/awsup/aws_cloud.go | 21 +++++++++++---------- upup/pkg/fi/cloudup/awsup/aws_utils.go | 7 +++---- upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go | 8 ++++---- upup/pkg/fi/cloudup/template_functions.go | 3 ++- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/pkg/zones/wellknown.go b/pkg/zones/wellknown.go index 834347497eee0..c84f264bdc2e3 100644 --- a/pkg/zones/wellknown.go +++ b/pkg/zones/wellknown.go @@ -17,12 +17,13 @@ limitations under the License. package zones import ( + "context" "sort" "strings" "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/aws/aws-sdk-go/aws/endpoints" - "github.com/aws/aws-sdk-go/service/ec2" "github.com/scaleway/scaleway-sdk-go/scw" "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/upup/pkg/fi/cloudup/awsup" @@ -244,6 +245,8 @@ func scwZones() []string { } func WellKnownZonesForCloud(matchCloud kops.CloudProviderID, prefix string) []string { + ctx := context.Background() + var found []string switch matchCloud { case kops.CloudProviderAWS: @@ -259,7 +262,7 @@ func WellKnownZonesForCloud(matchCloud kops.CloudProviderID, prefix string) []st continue } var zones *ec2.DescribeAvailabilityZonesOutput - zones, err = awsCloud.EC2().DescribeAvailabilityZones(&ec2.DescribeAvailabilityZonesInput{ + zones, err = awsCloud.EC2().DescribeAvailabilityZones(ctx, &ec2.DescribeAvailabilityZonesInput{ AllAvailabilityZones: aws.Bool(true), }) if err != nil { diff --git a/upup/pkg/fi/cloudup/awsup/aws_cloud.go b/upup/pkg/fi/cloudup/awsup/aws_cloud.go index e24737c7e971e..f4c3b2f865e4a 100644 --- a/upup/pkg/fi/cloudup/awsup/aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/aws_cloud.go @@ -173,7 +173,7 @@ type AWSCloud interface { // DescribeVPC is a helper that queries for the specified vpc by id DescribeVPC(vpcID string) (*ec2types.Vpc, error) - DescribeAvailabilityZones() ([]*ec2.AvailabilityZone, error) + DescribeAvailabilityZones() ([]ec2types.AvailabilityZone, error) // ResolveImage finds an AMI image based on the given name. // The name can be one of: @@ -2088,11 +2088,12 @@ func resolveImage(ctx context.Context, ssmClient awsinterfaces.SSMAPI, ec2Client return image, nil } -func (c *awsCloudImplementation) DescribeAvailabilityZones() ([]*ec2.AvailabilityZone, error) { +func (c *awsCloudImplementation) DescribeAvailabilityZones() ([]ec2types.AvailabilityZone, error) { klog.V(2).Infof("Querying EC2 for all valid zones in region %q", c.region) + ctx := context.TODO() request := &ec2.DescribeAvailabilityZonesInput{} - response, err := c.EC2().DescribeAvailabilityZones(request) + response, err := c.EC2().DescribeAvailabilityZones(ctx, request) if err != nil { return nil, fmt.Errorf("error querying for valid AZs in %q - verify your AWS credentials. Error: %v", c.region, err) } @@ -2107,15 +2108,15 @@ func ValidateZones(zones []string, cloud AWSCloud) error { return err } - zoneMap := make(map[string]*ec2.AvailabilityZone) + zoneMap := make(map[string]ec2types.AvailabilityZone) for _, z := range azs { - name := aws.StringValue(z.ZoneName) + name := aws.ToString(z.ZoneName) zoneMap[name] = z } for _, zone := range zones { - z := zoneMap[zone] - if z == nil { + z, ok := zoneMap[zone] + if !ok { var knownZones []string for z := range zoneMap { knownZones = append(knownZones, z) @@ -2126,11 +2127,11 @@ func ValidateZones(zones []string, cloud AWSCloud) error { } for _, message := range z.Messages { - klog.Warningf("Zone %q has message: %q", zone, aws.StringValue(message.Message)) + klog.Warningf("Zone %q has message: %q", zone, aws.ToString(message.Message)) } - if aws.StringValue(z.State) != ec2.AvailabilityZoneStateAvailable { - klog.Warningf("Zone %q has state %q", zone, aws.StringValue(z.State)) + if z.State != ec2types.AvailabilityZoneStateAvailable { + klog.Warningf("Zone %q has state %q", zone, z.State) } } diff --git a/upup/pkg/fi/cloudup/awsup/aws_utils.go b/upup/pkg/fi/cloudup/awsup/aws_utils.go index efd7119a1b0ca..b119300f5a3d5 100644 --- a/upup/pkg/fi/cloudup/awsup/aws_utils.go +++ b/upup/pkg/fi/cloudup/awsup/aws_utils.go @@ -29,13 +29,12 @@ import ( "github.com/aws/aws-sdk-go-v2/aws/arn" awsconfig "github.com/aws/aws-sdk-go-v2/config" autoscalingtypes "github.com/aws/aws-sdk-go-v2/service/autoscaling/types" - ec2v2 "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2 "github.com/aws/aws-sdk-go-v2/service/ec2" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" elbtypes "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types" iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/smithy-go" "k8s.io/klog/v2" "k8s.io/kops/pkg/apis/kops" @@ -54,7 +53,7 @@ func ValidateRegion(ctx context.Context, region string) error { if allRegions == nil { klog.V(2).Infof("Querying EC2 for all valid regions") - request := &ec2v2.DescribeRegionsInput{} + request := &ec2.DescribeRegionsInput{} awsRegion := os.Getenv("AWS_REGION") if awsRegion == "" { awsRegion = "us-east-1" @@ -68,7 +67,7 @@ func ValidateRegion(ctx context.Context, region string) error { return fmt.Errorf("error starting a new AWS session: %v", err) } - client := ec2v2.NewFromConfig(cfg) + client := ec2.NewFromConfig(cfg) response, err := client.DescribeRegions(ctx, request) if err != nil { diff --git a/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go b/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go index f8266253ab6be..5329491e062f0 100644 --- a/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go @@ -43,7 +43,7 @@ type MockAWSCloud struct { region string tags map[string]string - zones []*ec2.AvailabilityZone + zones []ec2types.AvailabilityZone } var _ fi.Cloud = (*MockAWSCloud)(nil) @@ -61,10 +61,10 @@ func BuildMockAWSCloud(region string, zoneLetters string) *MockAWSCloud { i := &MockAWSCloud{region: region} for _, c := range zoneLetters { azName := fmt.Sprintf("%s%c", region, c) - az := &ec2.AvailabilityZone{ + az := ec2types.AvailabilityZone{ RegionName: aws.String(region), ZoneName: aws.String(azName), - State: aws.String("available"), + State: ec2types.AvailabilityZoneStateAvailable, } i.zones = append(i.zones, az) } @@ -124,7 +124,7 @@ func (c *MockAWSCloud) Region() string { return c.region } -func (c *MockAWSCloud) DescribeAvailabilityZones() ([]*ec2.AvailabilityZone, error) { +func (c *MockAWSCloud) DescribeAvailabilityZones() ([]ec2types.AvailabilityZone, error) { return c.zones, nil } diff --git a/upup/pkg/fi/cloudup/template_functions.go b/upup/pkg/fi/cloudup/template_functions.go index b4de81c63e02e..f83f72e2d46b9 100644 --- a/upup/pkg/fi/cloudup/template_functions.go +++ b/upup/pkg/fi/cloudup/template_functions.go @@ -40,7 +40,8 @@ import ( "text/template" "github.com/Masterminds/sprig/v3" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/util/sets" From 174da2201b1523d004c88c430e92dd304614fb0f Mon Sep 17 00:00:00 2001 From: Peter Rifel Date: Tue, 9 Apr 2024 21:35:52 -0500 Subject: [PATCH 08/14] Migrate AMIs to aws-sdk-go-v2 --- upup/pkg/fi/cloudup/awsup/aws_cloud.go | 38 ++++++++++--------- upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go | 2 +- upup/pkg/fi/cloudup/template_functions.go | 5 ++- .../pkg/fi/cloudup/template_functions_test.go | 6 +-- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/upup/pkg/fi/cloudup/awsup/aws_cloud.go b/upup/pkg/fi/cloudup/awsup/aws_cloud.go index f4c3b2f865e4a..9d00df06c4256 100644 --- a/upup/pkg/fi/cloudup/awsup/aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/aws_cloud.go @@ -180,7 +180,7 @@ type AWSCloud interface { // `ami-...` in which case it is presumed to be an id // owner/name in which case we find the image with the specified name, owned by owner // name in which case we find the image with the specified name, with the current owner - ResolveImage(name string) (*ec2.Image, error) + ResolveImage(name string) (*ec2types.Image, error) // WithTags created a copy of AWSCloud with the specified default-tags bound WithTags(tags map[string]string) AWSCloud @@ -1994,7 +1994,7 @@ func describeVPC(c AWSCloud, vpcID string) (*ec2types.Vpc, error) { // `ami-...` in which case it is presumed to be an id // owner/name in which case we find the image with the specified name, owned by owner // name in which case we find the image with the specified name, with the current owner -func (c *awsCloudImplementation) ResolveImage(name string) (*ec2.Image, error) { +func (c *awsCloudImplementation) ResolveImage(name string) (*ec2types.Image, error) { return resolveImage(context.TODO(), c.ssm, c.ec2, name) } @@ -2009,17 +2009,17 @@ func resolveSSMParameter(ctx context.Context, ssmClient awsinterfaces.SSMAPI, na return "", fmt.Errorf("failed to get value for SSM parameter: %w", err) } - return aws.StringValue(response.Parameter.Value), nil + return aws.ToString(response.Parameter.Value), nil } -func resolveImage(ctx context.Context, ssmClient awsinterfaces.SSMAPI, ec2Client ec2iface.EC2API, name string) (*ec2.Image, error) { +func resolveImage(ctx context.Context, ssmClient awsinterfaces.SSMAPI, ec2Client awsinterfaces.EC2API, name string) (*ec2types.Image, error) { // TODO: Cache this result during a single execution (we get called multiple times) klog.V(2).Infof("Calling DescribeImages to resolve name %q", name) request := &ec2.DescribeImagesInput{} if strings.HasPrefix(name, "ami-") { // ami-xxxxxxxx - request.ImageIds = []*string{&name} + request.ImageIds = []string{name} } else if strings.HasPrefix(name, "ssm:") { parameter := strings.TrimPrefix(name, "ssm:") @@ -2028,13 +2028,13 @@ func resolveImage(ctx context.Context, ssmClient awsinterfaces.SSMAPI, ec2Client return nil, err } - request.ImageIds = []*string{&image} + request.ImageIds = []string{image} } else { // Either or / tokens := strings.SplitN(name, "/", 2) if len(tokens) == 1 { // self is a well-known value in the DescribeImages call - request.Owners = aws.StringSlice([]string{"self"}) + request.Owners = []string{"self"} request.Filters = append(request.Filters, NewEC2Filter("name", name)) } else if len(tokens) == 2 { owner := tokens[0] @@ -2055,36 +2055,38 @@ func resolveImage(ctx context.Context, ssmClient awsinterfaces.SSMAPI, ec2Client owner = WellKnownAccountUbuntu } - request.Owners = []*string{&owner} + request.Owners = []string{owner} request.Filters = append(request.Filters, NewEC2Filter("name", tokens[1])) } else { return nil, fmt.Errorf("image name specification not recognized: %q", name) } } - var image *ec2.Image - err := ec2Client.DescribeImagesPagesWithContext(context.TODO(), request, func(output *ec2.DescribeImagesOutput, b bool) bool { - for _, v := range output.Images { + var image *ec2types.Image + paginator := ec2.NewDescribeImagesPaginator(ec2Client, request) + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("error listing images: %v", err) + } + + for _, v := range page.Images { if image == nil { - image = v + image = &v } else { itime, _ := time.Parse(time.RFC3339, *image.CreationDate) vtime, _ := time.Parse(time.RFC3339, *v.CreationDate) if vtime.After(itime) { - image = v + image = &v } } } - return true - }) - if err != nil { - return nil, fmt.Errorf("error listing images: %v", err) } if image == nil { return nil, fmt.Errorf("could not find Image for %q", name) } - klog.V(4).Infof("Resolved image %q", aws.StringValue(image.ImageId)) + klog.V(4).Infof("Resolved image %q", aws.ToString(image.ImageId)) return image, nil } diff --git a/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go b/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go index 5329491e062f0..72d928883b5d5 100644 --- a/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go @@ -221,7 +221,7 @@ func (c *MockAWSCloud) DescribeVPC(vpcID string) (*ec2types.Vpc, error) { return describeVPC(c, vpcID) } -func (c *MockAWSCloud) ResolveImage(name string) (*ec2.Image, error) { +func (c *MockAWSCloud) ResolveImage(name string) (*ec2types.Image, error) { return resolveImage(context.TODO(), c.MockSSM, c.MockEC2, name) } diff --git a/upup/pkg/fi/cloudup/template_functions.go b/upup/pkg/fi/cloudup/template_functions.go index f83f72e2d46b9..246ca79212957 100644 --- a/upup/pkg/fi/cloudup/template_functions.go +++ b/upup/pkg/fi/cloudup/template_functions.go @@ -28,6 +28,7 @@ When defining a new function: package cloudup import ( + "context" "encoding/base64" "encoding/json" "fmt" @@ -963,8 +964,8 @@ func (tf *TemplateFunctions) GetClusterAutoscalerNodeGroups() map[string]Cluster func (tf *TemplateFunctions) architectureOfAMI(amiID string) string { image, _ := tf.cloud.(awsup.AWSCloud).ResolveImage(amiID) - switch *image.Architecture { - case "x86_64": + switch image.Architecture { + case ec2types.ArchitectureValuesX8664: return "amd64" } return "arm64" diff --git a/upup/pkg/fi/cloudup/template_functions_test.go b/upup/pkg/fi/cloudup/template_functions_test.go index 2eb50973987b6..adc1b09c9db3c 100644 --- a/upup/pkg/fi/cloudup/template_functions_test.go +++ b/upup/pkg/fi/cloudup/template_functions_test.go @@ -22,7 +22,7 @@ import ( "testing" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/cloudmock/aws/mockec2" "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/featureflag" @@ -300,13 +300,13 @@ func Test_TemplateFunctions_CloudControllerConfigArgv(t *testing.T) { func Test_KarpenterInstanceTypes(t *testing.T) { amiId := "ami-073c8c0760395aab8" ec2Client := &mockec2.MockEC2{} - ec2Client.Images = append(ec2Client.Images, &ec2.Image{ + ec2Client.Images = append(ec2Client.Images, &ec2types.Image{ CreationDate: aws.String("2016-10-21T20:07:19.000Z"), ImageId: &amiId, Name: aws.String("focal"), OwnerId: aws.String(awsup.WellKnownAccountUbuntu), RootDeviceName: aws.String("/dev/xvda"), - Architecture: aws.String("x86_64"), + Architecture: ec2types.ArchitectureValuesX8664, }) ig := kops.InstanceGroupSpec{ Image: amiId, From 27db8a08547b978c4e685bd052f08595c0986ee2 Mon Sep 17 00:00:00 2001 From: Peter Rifel Date: Tue, 9 Apr 2024 21:38:18 -0500 Subject: [PATCH 09/14] Migrate AWS error handling to aws-sdk-go-v2 --- upup/pkg/fi/cloudup/awsup/aws_cloud.go | 14 +++++++------- upup/pkg/fi/nodeup/command.go | 8 ++++---- upup/pkg/fi/nodeup/nodetasks/prefix.go | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/upup/pkg/fi/cloudup/awsup/aws_cloud.go b/upup/pkg/fi/cloudup/awsup/aws_cloud.go index 9d00df06c4256..a784b1f6806f4 100644 --- a/upup/pkg/fi/cloudup/awsup/aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/aws_cloud.go @@ -1313,13 +1313,13 @@ var tagsEventualConsistencyErrors = map[string]bool{ // isTagsEventualConsistencyError checks if the error is one of the errors encountered // when we try to create/get tags before the resource has fully 'propagated' in EC2 func isTagsEventualConsistencyError(err error) bool { - if awsErr, ok := err.(awserr.Error); ok { - isEventualConsistency, found := tagsEventualConsistencyErrors[awsErr.Code()] - if found { - return isEventualConsistency - } - - klog.Warningf("Uncategorized error in isTagsEventualConsistencyError: %v", awsErr.Code()) + errCode := AWSErrorCode(err) + isEventualConsistency, found := tagsEventualConsistencyErrors[errCode] + if found { + return isEventualConsistency + } + if errCode != "" { + klog.Warningf("Uncategorized error in isTagsEventualConsistencyError: %v", errCode) } return false } diff --git a/upup/pkg/fi/nodeup/command.go b/upup/pkg/fi/nodeup/command.go index 6bab6a176fefe..ebe062f141856 100644 --- a/upup/pkg/fi/nodeup/command.go +++ b/upup/pkg/fi/nodeup/command.go @@ -24,6 +24,7 @@ import ( "fmt" "io" "net" + "net/http" "net/url" "os" "os/exec" @@ -32,12 +33,12 @@ import ( "time" "github.com/aws/aws-sdk-go-v2/aws" + awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http" awsconfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" "github.com/aws/aws-sdk-go-v2/service/autoscaling" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/aws-sdk-go-v2/service/kms" - "github.com/aws/aws-sdk-go/aws/awserr" "go.uber.org/multierr" "k8s.io/klog/v2" "k8s.io/kops/nodeup/pkg/model" @@ -743,9 +744,8 @@ func getAWSConfigurationMode(ctx context.Context, c *model.NodeupModelContext) ( targetLifecycleState, err := vfs.Context.ReadFile("metadata://aws/meta-data/autoscaling/target-lifecycle-state") if err != nil { - var awsErr awserr.RequestFailure - if errors.As(err, &awsErr) && awsErr.StatusCode() == 404 { - // The instance isn't in an ASG (karpenter, etc.) + var awsErr *awshttp.ResponseError + if errors.As(err, &awsErr) && awsErr.HTTPStatusCode() == http.StatusNotFound { return "", nil } return "", fmt.Errorf("error reading target-lifecycle-state from instance metadata: %v", err) diff --git a/upup/pkg/fi/nodeup/nodetasks/prefix.go b/upup/pkg/fi/nodeup/nodetasks/prefix.go index eb8986796e087..bfb0810e25a87 100644 --- a/upup/pkg/fi/nodeup/nodetasks/prefix.go +++ b/upup/pkg/fi/nodeup/nodetasks/prefix.go @@ -25,9 +25,9 @@ import ( "path" "strings" + awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http" awsconfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go-v2/service/ec2" "k8s.io/klog/v2" "k8s.io/kops/pkg/apis/kops" @@ -128,8 +128,8 @@ func getInstanceMetadataList(ctx context.Context, category string) ([]string, er metadata := imds.NewFromConfig(cfg) resp, err := metadata.GetMetadata(ctx, &imds.GetMetadataInput{Path: category}) if err != nil { - var aerr awserr.RequestFailure - if errors.As(err, &aerr) && aerr.StatusCode() == http.StatusNotFound { + var awsErr *awshttp.ResponseError + if errors.As(err, &awsErr) && awsErr.HTTPStatusCode() == http.StatusNotFound { return nil, nil } else { return nil, fmt.Errorf("failed to get %q from ec2 meta-data: %v", category, err) From 6df88d3d05182985d620f24511a8a6b763bf814e Mon Sep 17 00:00:00 2001 From: Peter Rifel Date: Tue, 9 Apr 2024 21:38:40 -0500 Subject: [PATCH 10/14] Finish migrating EC2 client to aws-sdk-go-v2 --- upup/pkg/fi/cloudup/awsup/aws_cloud.go | 159 ++++--------------- upup/pkg/fi/cloudup/awsup/logging_retryer.go | 63 -------- upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go | 13 +- upup/pkg/fi/cloudup/awsup/request_logger.go | 46 ------ upup/pkg/fi/cloudup/awsup/status.go | 20 ++- 5 files changed, 51 insertions(+), 250 deletions(-) delete mode 100644 upup/pkg/fi/cloudup/awsup/logging_retryer.go delete mode 100644 upup/pkg/fi/cloudup/awsup/request_logger.go diff --git a/upup/pkg/fi/cloudup/awsup/aws_cloud.go b/upup/pkg/fi/cloudup/awsup/aws_cloud.go index a784b1f6806f4..f4139ae61413a 100644 --- a/upup/pkg/fi/cloudup/awsup/aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/aws_cloud.go @@ -25,13 +25,13 @@ import ( "sync" "time" - awsv2 "github.com/aws/aws-sdk-go-v2/aws" awsconfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/eventbridge" "github.com/aws/aws-sdk-go-v2/service/sqs" "github.com/aws/aws-sdk-go-v2/service/ssm" "golang.org/x/sync/errgroup" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/aws/arn" "github.com/aws/aws-sdk-go-v2/aws/retry" stscredsv2 "github.com/aws/aws-sdk-go-v2/credentials/stscreds" @@ -46,12 +46,6 @@ import ( "github.com/aws/aws-sdk-go-v2/service/iam" "github.com/aws/aws-sdk-go-v2/service/route53" "github.com/aws/aws-sdk-go-v2/service/sts" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/aws/credentials/stscreds" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" ec2v1 "github.com/aws/aws-sdk-go/service/ec2" "k8s.io/klog/v2" @@ -126,8 +120,8 @@ const AWSErrCodeInvalidAction = "InvalidAction" type AWSCloud interface { fi.Cloud - Session() (*session.Session, error) - EC2() ec2iface.EC2API + Config() aws.Config + EC2() awsinterfaces.EC2API IAM() awsinterfaces.IAMAPI ELB() awsinterfaces.ELBAPI ELBV2() awsinterfaces.ELBV2API @@ -196,7 +190,7 @@ type AWSCloud interface { } type awsCloudImplementation struct { - ec2 *ec2.EC2 + ec2 *ec2.Client iam *iam.Client elb *elb.Client elbv2 *elbv2.Client @@ -212,19 +206,14 @@ type awsCloudImplementation struct { tags map[string]string - regionDelayers *RegionDelayers - instanceTypes *instanceTypes -} -type RegionDelayers struct { - mutex sync.Mutex - delayerMap map[string]*k8s_aws.CrossRequestRetryDelay + config aws.Config } type instanceTypes struct { mutex sync.Mutex - typeMap map[string]*ec2.InstanceTypeInfo + typeMap map[string]*ec2types.InstanceTypeInfo } var _ fi.Cloud = &awsCloudImplementation{} @@ -256,13 +245,6 @@ func ResetAWSCloudInstances() { awsCloudInstances.mutex.Unlock() } -func setConfig(config *aws.Config) *aws.Config { - // This avoids a confusing error message when we fail to get credentials - // e.g. https://github.com/kubernetes/kops/issues/605 - config = config.WithCredentialsChainVerboseErrors(true) - return request.WithRetryer(config, newLoggingRetryer(ClientMaxRetries)) -} - func updateAwsCloudInstances(region string, cloud AWSCloud) { awsCloudInstances.mutex.Lock() awsCloudInstances.regionMap[region] = cloud @@ -288,68 +270,47 @@ func NewAWSCloud(region string, tags map[string]string) (AWSCloud, error) { if raw == nil { c := &awsCloudImplementation{ region: region, - regionDelayers: &RegionDelayers{ - delayerMap: make(map[string]*k8s_aws.CrossRequestRetryDelay), - }, instanceTypes: &instanceTypes{ - typeMap: make(map[string]*ec2.InstanceTypeInfo), + typeMap: make(map[string]*ec2types.InstanceTypeInfo), }, } loadOptions := []func(*awsconfig.LoadOptions) error{ awsconfig.WithRegion(region), - awsconfig.WithClientLogMode(awsv2.LogRetries), + awsconfig.WithClientLogMode(aws.LogRetries), awsconfig.WithLogger(awsLogger{}), - awsconfig.WithRetryer(func() awsv2.Retryer { + awsconfig.WithRetryer(func() aws.Retryer { return retry.NewStandard() }), } - config := aws.NewConfig().WithRegion(region) - config = setConfig(config) - - requestLogger := newRequestLogger(2) - - sess, err := session.NewSessionWithOptions(session.Options{ - Config: *config, - SharedConfigState: session.SharedConfigEnable, - }) - if err != nil { - return c, err - } - // assumes the role before executing commands roleARN := os.Getenv("KOPS_AWS_ROLE_ARN") if roleARN != "" { - cfgV2, err := awsconfig.LoadDefaultConfig(ctx, loadOptions...) + cfg, err := awsconfig.LoadDefaultConfig(ctx, loadOptions...) if err != nil { return c, fmt.Errorf("failed to load default aws config: %w", err) } - stsClient := sts.NewFromConfig(cfgV2) + stsClient := sts.NewFromConfig(cfg) assumeRoleProvider := stscredsv2.NewAssumeRoleProvider(stsClient, roleARN) loadOptions = append(loadOptions, awsconfig.WithCredentialsProvider(assumeRoleProvider)) - - creds := stscreds.NewCredentials(sess, roleARN) - config = &aws.Config{Credentials: creds} - config = setConfig(config).WithRegion(region) } - c.ec2 = ec2.New(sess, config) - c.ec2.Handlers.Send.PushFront(requestLogger) - c.addHandlers(region, &c.ec2.Handlers) - - cfgV2, err := awsconfig.LoadDefaultConfig(ctx, loadOptions...) + cfg, err := awsconfig.LoadDefaultConfig(ctx, loadOptions...) if err != nil { return c, fmt.Errorf("failed to load default aws config: %w", err) } - c.iam = iam.NewFromConfig(cfgV2) - c.elb = elb.NewFromConfig(cfgV2) - c.elbv2 = elbv2.NewFromConfig(cfgV2) - c.sts = sts.NewFromConfig(cfgV2) - c.autoscaling = autoscaling.NewFromConfig(cfgV2) - c.route53 = route53.NewFromConfig(cfgV2) + c.config = cfg + + c.ec2 = ec2.NewFromConfig(cfg) + c.iam = iam.NewFromConfig(cfg) + c.elb = elb.NewFromConfig(cfg) + c.elbv2 = elbv2.NewFromConfig(cfg) + c.sts = sts.NewFromConfig(cfg) + c.autoscaling = autoscaling.NewFromConfig(cfg) + c.route53 = route53.NewFromConfig(cfg) if featureflag.Spotinst.Enabled() { c.spotinst, err = spotinst.NewCloud(kops.CloudProviderAWS) @@ -358,9 +319,9 @@ func NewAWSCloud(region string, tags map[string]string) (AWSCloud, error) { } } - c.sqs = sqs.NewFromConfig(cfgV2) - c.eventbridge = eventbridge.NewFromConfig(cfgV2) - c.ssm = ssm.NewFromConfig(cfgV2) + c.sqs = sqs.NewFromConfig(cfg) + c.eventbridge = eventbridge.NewFromConfig(cfg) + c.ssm = ssm.NewFromConfig(cfg) updateAwsCloudInstances(region, c) @@ -372,66 +333,14 @@ func NewAWSCloud(region string, tags map[string]string) (AWSCloud, error) { return i, nil } -func (c *awsCloudImplementation) Session() (*session.Session, error) { - config := aws.NewConfig().WithRegion(c.region) - config = config.WithCredentialsChainVerboseErrors(true) - config = request.WithRetryer(config, newLoggingRetryer(ClientMaxRetries)) - - sess, err := session.NewSessionWithOptions(session.Options{ - Config: *config, - SharedConfigState: session.SharedConfigEnable, - }) - if err != nil { - return nil, fmt.Errorf("failed to create session: %w", err) - } - - return sess, err -} - -func (c *awsCloudImplementation) addHandlers(regionName string, h *request.Handlers) { - delayer := c.getCrossRequestRetryDelay(regionName) - if delayer != nil { - h.Sign.PushFrontNamed(request.NamedHandler{ - Name: "kops/delay-presign", - Fn: delayer.BeforeSign, - }) - - h.AfterRetry.PushFrontNamed(request.NamedHandler{ - Name: "kops/delay-afterretry", - Fn: delayer.AfterRetry, - }) - } +func (c *awsCloudImplementation) Config() aws.Config { + return c.config } -// Get a CrossRequestRetryDelay, scoped to the region, not to the request. -// This means that when we hit a limit on a call, we will delay _all_ calls to the API. -// We do this to protect the AWS account from becoming overloaded and effectively locked. -// We also log when we hit request limits. -// Note that this delays the current goroutine; this is bad behaviour and will -// likely cause kops to become slow or unresponsive for cloud operations. -// However, this throttle is intended only as a last resort. When we observe -// this throttling, we need to address the root cause (e.g. add a delay to a -// controller retry loop) -func (c *awsCloudImplementation) getCrossRequestRetryDelay(regionName string) *k8s_aws.CrossRequestRetryDelay { - c.regionDelayers.mutex.Lock() - defer c.regionDelayers.mutex.Unlock() - - delayer, found := c.regionDelayers.delayerMap[regionName] - if !found { - delayer = k8s_aws.NewCrossRequestRetryDelay() - c.regionDelayers.delayerMap[regionName] = delayer - } - return delayer -} - -func NewEC2Filter(name string, values ...string) *ec2.Filter { - awsValues := []*string{} - for _, value := range values { - awsValues = append(awsValues, aws.String(value)) - } - filter := &ec2.Filter{ +func NewEC2Filter(name string, values ...string) ec2types.Filter { + filter := ec2types.Filter{ Name: aws.String(name), - Values: awsValues, + Values: values, } return filter } @@ -2148,7 +2057,7 @@ func (c *awsCloudImplementation) DNS() (dnsprovider.Interface, error) { return provider, nil } -func (c *awsCloudImplementation) EC2() ec2iface.EC2API { +func (c *awsCloudImplementation) EC2() awsinterfaces.EC2API { return c.ec2 } @@ -2259,7 +2168,7 @@ func findDNSName(cloud AWSCloud, cluster *kops.Cluster) (string, error) { if lb, err := cloud.FindELBByNameTag(name); err != nil { return "", fmt.Errorf("error looking for AWS ELB: %v", err) } else if lb != nil { - return aws.StringValue(lb.DNSName), nil + return aws.ToString(lb.DNSName), nil } } else if cluster.Spec.API.LoadBalancer.Class == kops.LoadBalancerClassNetwork { allLoadBalancers, err := ListELBV2LoadBalancers(ctx, cloud) @@ -2269,7 +2178,7 @@ func findDNSName(cloud AWSCloud, cluster *kops.Cluster) (string, error) { latest := FindLatestELBV2ByNameTag(allLoadBalancers, name) if latest != nil { - return aws.StringValue(latest.LoadBalancer.DNSName), nil + return aws.ToString(latest.LoadBalancer.DNSName), nil } } return "", nil @@ -2418,7 +2327,7 @@ func (c *awsCloudImplementation) AccountInfo(ctx context.Context) (string, strin return "", "", fmt.Errorf("error getting AWS account ID: %v", err) } - arn, err := arn.Parse(aws.StringValue(response.Arn)) + arn, err := arn.Parse(aws.ToString(response.Arn)) if err != nil { return "", "", fmt.Errorf("failed to parse GetCallerIdentity ARN: %w", err) } @@ -2449,7 +2358,7 @@ func GetRolesInInstanceProfile(c AWSCloud, profileName string) ([]string, error) // GetInstanceCertificateNames returns the instance hostname and addresses that should go into certificates. // The first value is the node name and any additional values are the DNS name and IP addresses. -func GetInstanceCertificateNames(instances *ec2.DescribeInstancesOutput) (addrs []string, err error) { +func GetInstanceCertificateNames(instances *ec2v1.DescribeInstancesOutput) (addrs []string, err error) { if len(instances.Reservations) != 1 { return nil, fmt.Errorf("too many reservations returned for the single instance-id") } diff --git a/upup/pkg/fi/cloudup/awsup/logging_retryer.go b/upup/pkg/fi/cloudup/awsup/logging_retryer.go deleted file mode 100644 index c9543fa742d2b..0000000000000 --- a/upup/pkg/fi/cloudup/awsup/logging_retryer.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2019 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 awsup - -import ( - "fmt" - "time" - - "github.com/aws/aws-sdk-go/aws/client" - "github.com/aws/aws-sdk-go/aws/request" - "k8s.io/klog/v2" -) - -// LoggingRetryer adds some logging when we are retrying, so we have some idea what is happening -// Right now it is very basic - e.g. it only logs when we retry (so doesn't log when we fail due to too many retries) -type LoggingRetryer struct { - client.DefaultRetryer -} - -var _ request.Retryer = &LoggingRetryer{} - -func newLoggingRetryer(maxRetries int) *LoggingRetryer { - return &LoggingRetryer{ - client.DefaultRetryer{NumMaxRetries: maxRetries}, - } -} - -func (l LoggingRetryer) RetryRules(r *request.Request) time.Duration { - duration := l.DefaultRetryer.RetryRules(r) - - service := r.ClientInfo.ServiceName - name := "?" - if r.Operation != nil { - name = r.Operation.Name - } - methodDescription := service + "/" + name - - var errorDescription string - if r.Error != nil { - // We could check aws error Code & Message, but we expect them to be in the string - errorDescription = fmt.Sprintf("%v", r.Error) - } else { - errorDescription = fmt.Sprintf("%d %s", r.HTTPResponse.StatusCode, r.HTTPResponse.Status) - } - - klog.V(2).Infof("Retryable error (%s) from %s - will retry after delay of %v", errorDescription, methodDescription, duration) - - return duration -} diff --git a/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go b/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go index 72d928883b5d5..070251339638c 100644 --- a/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go +++ b/upup/pkg/fi/cloudup/awsup/mock_aws_cloud.go @@ -24,9 +24,6 @@ import ( ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" elbtypes "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" v1 "k8s.io/api/core/v1" "k8s.io/klog/v2" "k8s.io/kops/dnsprovider/pkg/dnsprovider" @@ -73,7 +70,7 @@ func BuildMockAWSCloud(region string, zoneLetters string) *MockAWSCloud { type MockCloud struct { MockAutoscaling awsinterfaces.AutoScalingAPI - MockEC2 ec2iface.EC2API + MockEC2 awsinterfaces.EC2API MockIAM awsinterfaces.IAMAPI MockRoute53 awsinterfaces.Route53API MockELB awsinterfaces.ELBAPI @@ -137,7 +134,7 @@ func (c *MockAWSCloud) AddTags(name *string, tags map[string]string) { } } -func (c *MockAWSCloud) BuildFilters(name *string) []*ec2.Filter { +func (c *MockAWSCloud) BuildFilters(name *string) []ec2types.Filter { return buildFilters(c.tags, name) } @@ -232,7 +229,7 @@ func (c *MockAWSCloud) WithTags(tags map[string]string) AWSCloud { return m } -func (c *MockAWSCloud) EC2() ec2iface.EC2API { +func (c *MockAWSCloud) EC2() awsinterfaces.EC2API { if c.MockEC2 == nil { klog.Fatalf("MockAWSCloud MockEC2 not set") } @@ -389,6 +386,6 @@ func (c *MockAWSCloud) AccountInfo(ctx context.Context) (string, string, error) return "123456789012", "aws-test", nil } -func (c *MockAWSCloud) Session() (*session.Session, error) { - return nil, nil +func (c *MockAWSCloud) Config() aws.Config { + return aws.Config{} } diff --git a/upup/pkg/fi/cloudup/awsup/request_logger.go b/upup/pkg/fi/cloudup/awsup/request_logger.go deleted file mode 100644 index 7ea1e59324996..0000000000000 --- a/upup/pkg/fi/cloudup/awsup/request_logger.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2019 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 awsup - -import ( - "github.com/aws/aws-sdk-go/aws/request" - "k8s.io/klog/v2" -) - -// RequestLogger logs every AWS request -type RequestLogger struct { - logLevel klog.Level -} - -func newRequestLogger(logLevel int) func(r *request.Request) { - rl := &RequestLogger{ - logLevel: klog.Level(logLevel), - } - return rl.log -} - -// Handler for aws-sdk-go that logs all requests -func (l *RequestLogger) log(r *request.Request) { - service := r.ClientInfo.ServiceName - name := "?" - if r.Operation != nil { - name = r.Operation.Name - } - methodDescription := service + "/" + name - - klog.V(l.logLevel).Infof("AWS request: %s", methodDescription) -} diff --git a/upup/pkg/fi/cloudup/awsup/status.go b/upup/pkg/fi/cloudup/awsup/status.go index e6fd55656e6bb..18e415c842b57 100644 --- a/upup/pkg/fi/cloudup/awsup/status.go +++ b/upup/pkg/fi/cloudup/awsup/status.go @@ -17,11 +17,13 @@ limitations under the License. package awsup import ( + "context" "fmt" "strings" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/klog/v2" "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/protokube/pkg/etcd" @@ -64,16 +66,18 @@ func findEtcdStatus(c AWSCloud, cluster *kops.Cluster) ([]kops.EtcdClusterStatus request.Filters = append(request.Filters, NewEC2Filter("tag:"+k, v)) } - var volumes []*ec2.Volume + var volumes []ec2types.Volume klog.V(2).Infof("Listing EC2 Volumes") - err := c.EC2().DescribeVolumesPages(request, func(p *ec2.DescribeVolumesOutput, lastPage bool) bool { - volumes = append(volumes, p.Volumes...) - return true - }) - if err != nil { - return nil, fmt.Errorf("error describing volumes: %v", err) + paginator := ec2.NewDescribeVolumesPaginator(c.EC2(), request) + for paginator.HasMorePages() { + page, err := paginator.NextPage(context.TODO()) + if err != nil { + return nil, fmt.Errorf("error describing volumes: %v", err) + } + volumes = append(volumes, page.Volumes...) } + var err error for _, volume := range volumes { volumeID := aws.ToString(volume.VolumeId) From bc941d63a4f85de7014758832cb47e735fa1df03 Mon Sep 17 00:00:00 2001 From: Peter Rifel Date: Tue, 9 Apr 2024 21:39:37 -0500 Subject: [PATCH 11/14] Upgrade ec2-instance-selector to latest to pick up aws-sdk-go-v2 upgrade --- go.mod | 23 +- go.sum | 63 +- .../v2/pkg/awsapi/selectorec2.go | 12 + .../v2/pkg/cli/cli.go | 13 + .../v2/pkg/cli/flags.go | 40 + .../v2/pkg/cli/types.go | 40 + .../v2/pkg/ec2pricing/ec2pricing.go | 66 +- .../v2/pkg/ec2pricing/odpricing.go | 174 +- .../v2/pkg/ec2pricing/spotpricing.go | 55 +- .../v2/pkg/instancetypes/instancetypes.go | 46 +- .../v2/pkg/selector/aggregates.go | 57 +- .../v2/pkg/selector/comparators.go | 187 +- .../v2/pkg/selector/eks.go | 134 - .../v2/pkg/selector/emr.go | 8 +- .../v2/pkg/selector/outputs/outputs.go | 26 +- .../v2/pkg/selector/outputs/sortingView.go | 7 +- .../v2/pkg/selector/outputs/tableView.go | 4 +- .../v2/pkg/selector/outputs/verboseView.go | 5 +- .../v2/pkg/selector/selector.go | 250 +- .../v2/pkg/selector/services.go | 3 +- .../v2/pkg/selector/types.go | 61 +- .../v2/pkg/sorter/sorter.go | 17 +- .../service/pricing/CHANGELOG.md | 287 +++ .../aws-sdk-go-v2/service/pricing/LICENSE.txt | 202 ++ .../service/pricing/api_client.go | 526 ++++ .../pricing/api_op_DescribeServices.go | 370 +++ .../pricing/api_op_GetAttributeValues.go | 374 +++ .../pricing/api_op_GetPriceListFileUrl.go | 275 +++ .../service/pricing/api_op_GetProducts.go | 371 +++ .../service/pricing/api_op_ListPriceLists.go | 396 +++ .../service/pricing/deserializers.go | 1680 +++++++++++++ .../aws/aws-sdk-go-v2/service/pricing/doc.go | 28 + .../service/pricing/endpoints.go | 499 ++++ .../service/pricing/generated.json | 34 + .../service/pricing/go_module_metadata.go | 6 + .../pricing/internal/endpoints/endpoints.go | 319 +++ .../service/pricing/serializers.go | 468 ++++ .../service/pricing/types/enums.go | 19 + .../service/pricing/types/errors.go | 165 ++ .../service/pricing/types/types.go | 97 + .../service/pricing/validators.go | 222 ++ .../aws/aws-sdk-go/service/pricing/api.go | 2182 ----------------- .../aws/aws-sdk-go/service/pricing/doc.go | 53 - .../aws/aws-sdk-go/service/pricing/errors.go | 70 - .../service/pricing/pricingiface/interface.go | 96 - .../aws/aws-sdk-go/service/pricing/service.go | 108 - .../aymanbagabas/go-osc52/v2/LICENSE | 21 + .../aymanbagabas/go-osc52/v2/README.md | 83 + .../aymanbagabas/go-osc52/v2/osc52.go | 305 +++ .../github.com/charmbracelet/bubbles/LICENSE | 2 +- .../charmbracelet/bubbles/cursor/cursor.go | 207 ++ .../charmbracelet/bubbles/help/help.go | 10 +- .../charmbracelet/bubbles/key/key.go | 57 +- .../charmbracelet/bubbles/list/README.md | 72 + .../charmbracelet/bubbles/list/defaultitem.go | 6 +- .../charmbracelet/bubbles/list/keys.go | 2 +- .../charmbracelet/bubbles/list/list.go | 68 +- .../bubbles/paginator/paginator.go | 137 +- .../bubbles/runeutil/runeutil.go | 102 + .../charmbracelet/bubbles/spinner/spinner.go | 13 +- .../bubbles/textinput/textinput.go | 609 ++--- .../bubbles/viewport/viewport.go | 78 +- .../charmbracelet/bubbletea/.gitignore | 2 + .../charmbracelet/bubbletea/CONTRIBUTING.md | 13 + .../charmbracelet/bubbletea/LICENSE | 2 +- .../charmbracelet/bubbletea/README.md | 149 +- .../charmbracelet/bubbletea/commands.go | 162 +- .../charmbracelet/bubbletea/exec.go | 16 +- .../github.com/charmbracelet/bubbletea/key.go | 423 ++-- .../charmbracelet/bubbletea/logging.go | 27 +- .../charmbracelet/bubbletea/mouse.go | 6 +- .../charmbracelet/bubbletea/nil_renderer.go | 22 +- .../charmbracelet/bubbletea/options.go | 112 +- .../charmbracelet/bubbletea/renderer.go | 31 +- .../charmbracelet/bubbletea/screen.go | 172 +- .../charmbracelet/bubbletea/signals_unix.go | 22 +- .../bubbletea/signals_windows.go | 8 +- .../bubbletea/standard_renderer.go | 335 ++- .../github.com/charmbracelet/bubbletea/tea.go | 850 ++++--- .../github.com/charmbracelet/bubbletea/tty.go | 97 +- .../charmbracelet/bubbletea/tty_unix.go | 4 +- .../charmbracelet/bubbletea/tty_windows.go | 19 - .../charmbracelet/lipgloss/.gitignore | 1 + .../charmbracelet/lipgloss/README.md | 85 +- .../charmbracelet/lipgloss/align.go | 25 +- .../charmbracelet/lipgloss/borders.go | 64 +- .../charmbracelet/lipgloss/color.go | 229 +- .../github.com/charmbracelet/lipgloss/get.go | 55 +- .../github.com/charmbracelet/lipgloss/join.go | 26 +- .../charmbracelet/lipgloss/position.go | 36 +- .../charmbracelet/lipgloss/renderer.go | 143 ++ .../charmbracelet/lipgloss/runes.go | 2 +- .../github.com/charmbracelet/lipgloss/set.go | 95 +- .../charmbracelet/lipgloss/style.go | 92 +- .../charmbracelet/lipgloss/unset.go | 17 +- .../charmbracelet/lipgloss/whitespace.go | 19 +- .../containerd/console/.golangci.yml | 14 +- .../github.com/containerd/console/console.go | 2 +- .../containerd/console/console_linux.go | 1 + .../containerd/console/console_unix.go | 3 +- .../containerd/console/console_windows.go | 20 +- .../containerd/console/console_zos.go | 163 -- .../containerd/console/pty_freebsd_cgo.go | 1 + .../containerd/console/pty_freebsd_nocgo.go | 1 + .../github.com/containerd/console/pty_unix.go | 3 +- .../github.com/containerd/console/pty_zos.go | 43 + .../containerd/console/tc_freebsd_cgo.go | 1 + .../containerd/console/tc_freebsd_nocgo.go | 1 + .../containerd/console/tc_openbsd_cgo.go | 1 + .../containerd/console/tc_openbsd_nocgo.go | 1 + .../containerd/console/tc_solaris_cgo.go | 51 - .../containerd/console/tc_solaris_nocgo.go | 47 - .../github.com/containerd/console/tc_unix.go | 3 +- .../github.com/containerd/console/tc_zos.go | 13 + .../evertras/bubble-table/table/border.go | 6 +- .../evertras/bubble-table/table/calc.go | 4 +- .../evertras/bubble-table/table/column.go | 15 + .../evertras/bubble-table/table/data.go | 3 +- .../evertras/bubble-table/table/events.go | 10 + .../evertras/bubble-table/table/filter.go | 29 +- .../evertras/bubble-table/table/footer.go | 10 +- .../evertras/bubble-table/table/header.go | 3 +- .../evertras/bubble-table/table/model.go | 4 + .../evertras/bubble-table/table/options.go | 27 + .../evertras/bubble-table/table/pagination.go | 6 +- .../evertras/bubble-table/table/query.go | 15 +- .../evertras/bubble-table/table/row.go | 18 +- .../evertras/bubble-table/table/sort.go | 8 + .../evertras/bubble-table/table/update.go | 17 +- .../evertras/bubble-table/table/view.go | 2 +- .../mattn/go-localereader/README.md | 23 + .../mattn/go-localereader/localereader.go | 19 + .../go-localereader/localereader_unix.go | 11 + .../go-localereader/localereader_windows.go | 85 + .../github.com/mattn/go-runewidth/.travis.yml | 16 - .../github.com/mattn/go-runewidth/README.md | 2 +- .../github.com/mattn/go-runewidth/go.test.sh | 12 - .../mattn/go-runewidth/runewidth.go | 93 +- .../mattn/go-runewidth/runewidth_appengine.go | 1 + .../mattn/go-runewidth/runewidth_js.go | 4 +- .../mattn/go-runewidth/runewidth_posix.go | 5 +- .../mattn/go-runewidth/runewidth_windows.go | 4 +- .../muesli/cancelreader/cancelreader.go | 21 +- .../muesli/cancelreader/cancelreader_bsd.go | 1 + .../cancelreader/cancelreader_default.go | 4 +- .../muesli/cancelreader/cancelreader_linux.go | 7 +- vendor/github.com/muesli/termenv/README.md | 244 +- .../github.com/muesli/termenv/ansi_compat.md | 17 + vendor/github.com/muesli/termenv/color.go | 82 +- vendor/github.com/muesli/termenv/copy.go | 37 + vendor/github.com/muesli/termenv/hyperlink.go | 11 + .../github.com/muesli/termenv/notification.go | 11 + vendor/github.com/muesli/termenv/output.go | 197 ++ vendor/github.com/muesli/termenv/profile.go | 97 + vendor/github.com/muesli/termenv/screen.go | 442 +++- vendor/github.com/muesli/termenv/style.go | 7 +- .../muesli/termenv/templatehelper.go | 57 +- vendor/github.com/muesli/termenv/termenv.go | 89 +- .../github.com/muesli/termenv/termenv_js.go | 18 - .../muesli/termenv/termenv_other.go | 30 + .../github.com/muesli/termenv/termenv_unix.go | 121 +- .../muesli/termenv/termenv_windows.go | 64 +- vendor/modules.txt | 46 +- 163 files changed, 12245 insertions(+), 5450 deletions(-) create mode 100644 vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/awsapi/selectorec2.go delete mode 100644 vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/eks.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/CHANGELOG.md create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/LICENSE.txt create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_client.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_DescribeServices.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_GetAttributeValues.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_GetPriceListFileUrl.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_GetProducts.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_ListPriceLists.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/deserializers.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/doc.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/endpoints.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/generated.json create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/go_module_metadata.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/internal/endpoints/endpoints.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/serializers.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/types/enums.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/types/errors.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/types/types.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/pricing/validators.go delete mode 100644 vendor/github.com/aws/aws-sdk-go/service/pricing/api.go delete mode 100644 vendor/github.com/aws/aws-sdk-go/service/pricing/doc.go delete mode 100644 vendor/github.com/aws/aws-sdk-go/service/pricing/errors.go delete mode 100644 vendor/github.com/aws/aws-sdk-go/service/pricing/pricingiface/interface.go delete mode 100644 vendor/github.com/aws/aws-sdk-go/service/pricing/service.go create mode 100644 vendor/github.com/aymanbagabas/go-osc52/v2/LICENSE create mode 100644 vendor/github.com/aymanbagabas/go-osc52/v2/README.md create mode 100644 vendor/github.com/aymanbagabas/go-osc52/v2/osc52.go create mode 100644 vendor/github.com/charmbracelet/bubbles/cursor/cursor.go create mode 100644 vendor/github.com/charmbracelet/bubbles/list/README.md create mode 100644 vendor/github.com/charmbracelet/bubbles/runeutil/runeutil.go create mode 100644 vendor/github.com/charmbracelet/bubbletea/CONTRIBUTING.md create mode 100644 vendor/github.com/charmbracelet/lipgloss/.gitignore create mode 100644 vendor/github.com/charmbracelet/lipgloss/renderer.go delete mode 100644 vendor/github.com/containerd/console/console_zos.go create mode 100644 vendor/github.com/containerd/console/pty_zos.go delete mode 100644 vendor/github.com/containerd/console/tc_solaris_cgo.go delete mode 100644 vendor/github.com/containerd/console/tc_solaris_nocgo.go create mode 100644 vendor/github.com/mattn/go-localereader/README.md create mode 100644 vendor/github.com/mattn/go-localereader/localereader.go create mode 100644 vendor/github.com/mattn/go-localereader/localereader_unix.go create mode 100644 vendor/github.com/mattn/go-localereader/localereader_windows.go delete mode 100644 vendor/github.com/mattn/go-runewidth/.travis.yml delete mode 100644 vendor/github.com/mattn/go-runewidth/go.test.sh create mode 100644 vendor/github.com/muesli/termenv/copy.go create mode 100644 vendor/github.com/muesli/termenv/hyperlink.go create mode 100644 vendor/github.com/muesli/termenv/notification.go create mode 100644 vendor/github.com/muesli/termenv/output.go create mode 100644 vendor/github.com/muesli/termenv/profile.go delete mode 100644 vendor/github.com/muesli/termenv/termenv_js.go create mode 100644 vendor/github.com/muesli/termenv/termenv_other.go diff --git a/go.mod b/go.mod index 9740c1d13f5e8..6fd30a711c2f8 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/MakeNowJust/heredoc/v2 v2.0.1 github.com/Masterminds/sprig/v3 v3.2.3 github.com/apparentlymart/go-cidr v1.1.0 - github.com/aws/amazon-ec2-instance-selector/v2 v2.4.1 + github.com/aws/amazon-ec2-instance-selector/v2 v2.4.2-0.20231216170552-14d4dfcbaadf github.com/aws/aws-sdk-go v1.51.20 github.com/aws/aws-sdk-go-v2 v1.26.1 github.com/aws/aws-sdk-go-v2/config v1.27.11 @@ -121,15 +121,17 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5 // indirect + github.com/aws/aws-sdk-go-v2/service/pricing v1.21.6 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/charmbracelet/bubbles v0.13.0 // indirect - github.com/charmbracelet/bubbletea v0.21.0 // indirect - github.com/charmbracelet/lipgloss v0.5.0 // indirect - github.com/containerd/console v1.0.3 // indirect + github.com/charmbracelet/bubbles v0.16.1 // indirect + github.com/charmbracelet/bubbletea v0.24.2 // indirect + github.com/charmbracelet/lipgloss v0.7.1 // indirect + github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/containerd/containerd v1.7.12 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect @@ -145,7 +147,7 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v5.7.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.8.0 // indirect - github.com/evertras/bubble-table v0.14.4 // indirect + github.com/evertras/bubble-table v0.15.2 // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -196,8 +198,9 @@ require ( github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect github.com/miekg/dns v1.1.57 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -213,9 +216,9 @@ require ( github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect - github.com/muesli/cancelreader v0.2.0 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 // indirect + github.com/muesli/termenv v0.15.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/oklog/ulid v1.3.1 // indirect diff --git a/go.sum b/go.sum index b595b66bca40b..42bc87e8e402e 100644 --- a/go.sum +++ b/go.sum @@ -69,10 +69,11 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/aws/amazon-ec2-instance-selector/v2 v2.4.1 h1:DmxtwV+pkakkVRhxKcAgnLbxCxvT7k8DBG271dfKPZ8= -github.com/aws/amazon-ec2-instance-selector/v2 v2.4.1/go.mod h1:AEJrtkLkCkfIBIazidrVrgZqaXl+9dxI/wRgjdw+7G0= +github.com/aws/amazon-ec2-instance-selector/v2 v2.4.2-0.20231216170552-14d4dfcbaadf h1:1zems5/6/Fs+1dFsjTZ+oSogVHkfGl1VWuttRXYGx+0= +github.com/aws/amazon-ec2-instance-selector/v2 v2.4.2-0.20231216170552-14d4dfcbaadf/go.mod h1:zsxolOKwtNEvoOPScJy5+Bu8F72LZy7pqVJNhP8tqVE= github.com/aws/aws-sdk-go v1.51.20 h1:ziM90ujYHKKkoTZL+Wg2LwjbQecL+l298GGJeG4ktZs= github.com/aws/aws-sdk-go v1.51.20/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA= github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 h1:x6xsQXGSmW6frevwDA+vi/wqhp1ct18mVXYN08/93to= @@ -83,8 +84,10 @@ github.com/aws/aws-sdk-go-v2/credentials v1.17.11 h1:YuIB1dJNf1Re822rriUOTxopaHH github.com/aws/aws-sdk-go-v2/credentials v1.17.11/go.mod h1:AQtFPsDH9bI2O+71anW6EKL+NcD7LG3dpKGMV4SShgo= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= @@ -113,6 +116,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5 h1:f9RyWNtS8oH7cZ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5/go.mod h1:h5CoMZV2VF297/VLhRhO1WF+XYWOzXo+4HsObA4HjBQ= github.com/aws/aws-sdk-go-v2/service/kms v1.30.1 h1:SBn4I0fJXF9FYOVRSVMWuhvEKoAHDikjGpS3wlmw5DE= github.com/aws/aws-sdk-go-v2/service/kms v1.30.1/go.mod h1:2snWQJQUKsbN66vAawJuOGX7dr37pfOq9hb0tZDGIqQ= +github.com/aws/aws-sdk-go-v2/service/pricing v1.21.6 h1:k/f3T13s7wx/By6aKovlVsjdNkRVT0QRR2RlZEvaTGg= +github.com/aws/aws-sdk-go-v2/service/pricing v1.21.6/go.mod h1:9n3tkRCngy3+Iw/8vK3C69iXh22SCGsy3yn16nTxH+s= github.com/aws/aws-sdk-go-v2/service/route53 v1.40.4 h1:ZZKiHm4cN8IDDZ2kh8DTk+YnYBjVsiFdwf5FwVs//IQ= github.com/aws/aws-sdk-go-v2/service/route53 v1.40.4/go.mod h1:RTfjFUctf+Zyq8e4rgLXmz43+0kIoIXbENvrFtilumI= github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1 h1:6cnno47Me9bRykw9AEv9zkXE+5or7jz8TsskTTccbgc= @@ -127,8 +132,11 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 h1:Jux+gDDyi1Lruk+KHF91tK2K github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak= github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 h1:cwIxeBttqPN3qkaAjcEcsh8NYr8n2HZPkcKgPAi1phU= github.com/aws/aws-sdk-go-v2/service/sts v1.28.6/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw= +github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -152,13 +160,12 @@ github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= -github.com/charmbracelet/bubbles v0.13.0 h1:zP/ROH3wJEBqZWKIsD50ZKKlx3ydLInq3LdD/Nrlb8w= -github.com/charmbracelet/bubbles v0.13.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc= -github.com/charmbracelet/bubbletea v0.21.0 h1:f3y+kanzgev5PA916qxmDybSHU3N804uOnKnhRPXTcI= -github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4= -github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8= -github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= +github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY= +github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc= +github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY= +github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= +github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E= +github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -168,8 +175,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= @@ -221,8 +228,8 @@ github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.8.0 h1:lRj6N9Nci7MvzrXuX6HFzU8XjmhPiXPlsKEy1u0KQro= github.com/evanphx/json-patch/v5 v5.8.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= -github.com/evertras/bubble-table v0.14.4 h1:UHUiPfsJ+lqbPSHIM1n7O8Ie2tbK0r9ReicXFnLg44I= -github.com/evertras/bubble-table v0.14.4/go.mod h1:SPOZKbIpyYWPHBNki3fyNpiPBQkvkULAtOT7NTD5fKY= +github.com/evertras/bubble-table v0.15.2 h1:hVj27V9tk5TD5p6mVv0RK/KJu2sHq0U+mBMux/HptkU= +github.com/evertras/bubble-table v0.15.2/go.mod h1:SPOZKbIpyYWPHBNki3fyNpiPBQkvkULAtOT7NTD5fKY= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -306,6 +313,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -405,6 +413,7 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jacksontj/memberlistmesh v0.0.0-20190905163944-93462b9d2bb7 h1:q9rwMYjPWIFOSijnxXre4+RGo8xS0NVbJzXg+F0NMHc= github.com/jacksontj/memberlistmesh v0.0.0-20190905163944-93462b9d2bb7/go.mod h1:fFX3XoduobgoJsVtpzIFRTgKZAbNhsSJIDNOgeUU5g4= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -447,13 +456,13 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= @@ -494,14 +503,12 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= -github.com/muesli/cancelreader v0.2.0 h1:SOpr+CfyVNce341kKqvbhhzQhBPyJRXQaCtn03Pae1Q= -github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= -github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 h1:QANkGiGr39l1EESqrE0gZw0/AJNYzIvoGLhIoVYtluI= -github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -746,21 +753,17 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/awsapi/selectorec2.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/awsapi/selectorec2.go new file mode 100644 index 0000000000000..8523d29155785 --- /dev/null +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/awsapi/selectorec2.go @@ -0,0 +1,12 @@ +package awsapi + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/ec2" +) + +type SelectorInterface interface { + ec2.DescribeInstanceTypeOfferingsAPIClient + ec2.DescribeInstanceTypesAPIClient + DescribeAvailabilityZones(ctx context.Context, params *ec2.DescribeAvailabilityZonesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeAvailabilityZonesOutput, error) +} diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli/cli.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli/cli.go index a86d3257f6409..77a4d0b811db1 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli/cli.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli/cli.go @@ -178,6 +178,10 @@ func (cl *CommandLineInterface) SetUntouchedFlagValuesToNil() error { return } switch v := cl.Flags[f.Name].(type) { + case *int32: + if reflect.ValueOf(*v).IsZero() { + cl.Flags[f.Name] = nil + } case *int: if reflect.ValueOf(*v).IsZero() { cl.Flags[f.Name] = nil @@ -234,6 +238,8 @@ func (cl *CommandLineInterface) ProcessRangeFilterFlags() error { switch cl.Flags[rangeHelperMax].(type) { case *int: cl.Flags[rangeHelperMin] = cl.IntMe(0) + case *int32: + cl.Flags[rangeHelperMin] = cl.Int32Me(0) case *bytequantity.ByteQuantity: cl.Flags[rangeHelperMin] = cl.ByteQuantityMe(bytequantity.ByteQuantity{Quantity: 0}) case *float64: @@ -245,6 +251,8 @@ func (cl *CommandLineInterface) ProcessRangeFilterFlags() error { switch cl.Flags[rangeHelperMin].(type) { case *int: cl.Flags[rangeHelperMax] = cl.IntMe(maxInt) + case *int32: + cl.Flags[rangeHelperMax] = cl.Int32Me(max32Int) case *bytequantity.ByteQuantity: cl.Flags[rangeHelperMax] = cl.ByteQuantityMe(bytequantity.ByteQuantity{Quantity: maxUint64}) case *float64: @@ -260,6 +268,11 @@ func (cl *CommandLineInterface) ProcessRangeFilterFlags() error { LowerBound: *cl.IntMe(cl.Flags[rangeHelperMin]), UpperBound: *cl.IntMe(cl.Flags[rangeHelperMax]), } + case *int32: + cl.Flags[flagName] = &selector.Int32RangeFilter{ + LowerBound: *cl.Int32Me(cl.Flags[rangeHelperMin]), + UpperBound: *cl.Int32Me(cl.Flags[rangeHelperMax]), + } case *bytequantity.ByteQuantity: cl.Flags[flagName] = &selector.ByteQuantityRangeFilter{ LowerBound: *cl.ByteQuantityMe(cl.Flags[rangeHelperMin]), diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli/flags.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli/flags.go index cc727ba54c08b..5b50221a02106 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli/flags.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli/flags.go @@ -14,6 +14,7 @@ import ( const ( maxInt = int(^uint(0) >> 1) + max32Int = int(^uint32(0) >> 1) maxUint64 = math.MaxUint64 ) @@ -55,6 +56,11 @@ func (cl *CommandLineInterface) IntMinMaxRangeFlags(name string, shorthand *stri cl.IntMinMaxRangeFlagOnFlagSet(cl.Command.Flags(), name, shorthand, defaultValue, description) } +// Int32MinMaxRangeFlags creates and registers a min, max, and helper flag each accepting an int +func (cl *CommandLineInterface) Int32MinMaxRangeFlags(name string, shorthand *string, defaultValue *int32, description string) { + cl.Int32MinMaxRangeFlagOnFlagSet(cl.Command.Flags(), name, shorthand, defaultValue, description) +} + // ByteQuantityMinMaxRangeFlags creates and registers a min, max, and helper flag each accepting a byte quantity like 512mb func (cl *CommandLineInterface) ByteQuantityMinMaxRangeFlags(name string, shorthand *string, defaultValue *bytequantity.ByteQuantity, description string) { cl.ByteQuantityMinMaxRangeFlagOnFlagSet(cl.Command.Flags(), name, shorthand, defaultValue, description) @@ -200,6 +206,27 @@ func (cl *CommandLineInterface) IntMinMaxRangeFlagOnFlagSet(flagSet *pflag.FlagS cl.rangeFlags[name] = true } +// Int32MinMaxRangeFlagOnFlagSet creates and registers a min, max, and helper flag each accepting an int +func (cl *CommandLineInterface) Int32MinMaxRangeFlagOnFlagSet(flagSet *pflag.FlagSet, name string, shorthand *string, defaultValue *int32, description string) { + cl.Int32FlagOnFlagSet(flagSet, name, shorthand, defaultValue, fmt.Sprintf("%s (sets --%s-min and -max to the same value)", description, name)) + cl.Int32FlagOnFlagSet(flagSet, name+"-min", nil, nil, fmt.Sprintf("Minimum %s If --%s-max is not specified, the upper bound will be infinity", description, name)) + cl.Int32FlagOnFlagSet(flagSet, name+"-max", nil, nil, fmt.Sprintf("Maximum %s If --%s-min is not specified, the lower bound will be 0", description, name)) + cl.validators[name] = func(val interface{}) error { + if cl.Flags[name+"-min"] == nil || cl.Flags[name+"-max"] == nil { + return nil + } + minArg := name + "-min" + maxArg := name + "-max" + minVal := cl.Flags[minArg].(*int32) + maxVal := cl.Flags[maxArg].(*int32) + if *minVal > *maxVal { + return fmt.Errorf("Invalid input for --%s and --%s. %s must be less than or equal to %s", minArg, maxArg, minArg, maxArg) + } + return nil + } + cl.rangeFlags[name] = true +} + // Float64MinMaxRangeFlagOnFlagSet creates and registers a min, max, and helper flag each accepting a float64 func (cl *CommandLineInterface) Float64MinMaxRangeFlagOnFlagSet(flagSet *pflag.FlagSet, name string, shorthand *string, defaultValue *float64, description string) { cl.Float64FlagOnFlagSet(flagSet, name, shorthand, defaultValue, fmt.Sprintf("%s (sets --%s-min and -max to the same value)", description, name)) @@ -296,6 +323,19 @@ func (cl *CommandLineInterface) IntFlagOnFlagSet(flagSet *pflag.FlagSet, name st cl.Flags[name] = flagSet.Int(name, *defaultValue, description) } +// Int32FlagOnFlagSet creates and registers a flag accepting an int +func (cl *CommandLineInterface) Int32FlagOnFlagSet(flagSet *pflag.FlagSet, name string, shorthand *string, defaultValue *int32, description string) { + if defaultValue == nil { + cl.nilDefaults[name] = true + defaultValue = cl.Int32Me(0) + } + if shorthand != nil { + cl.Flags[name] = flagSet.Int32P(name, string(*shorthand), *defaultValue, description) + return + } + cl.Flags[name] = flagSet.Int32(name, *defaultValue, description) +} + // Float64FlagOnFlagSet creates and registers a flag accepting a float64 func (cl *CommandLineInterface) Float64FlagOnFlagSet(flagSet *pflag.FlagSet, name string, shorthand *string, defaultValue *float64, description string) { if defaultValue == nil { diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli/types.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli/types.go index d2d059aa334a1..97a4f22ffa9cb 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli/types.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli/types.go @@ -105,6 +105,29 @@ func (*CommandLineInterface) IntMe(i interface{}) *int { } } +// Int32Me takes an interface and returns a pointer to an int value +// If the underlying interface kind is not int or *int then nil is returned +func (*CommandLineInterface) Int32Me(i interface{}) *int32 { + if i == nil { + return nil + } + switch v := i.(type) { + case *int: + val := int32(*v) + return &val + case int: + val := int32(v) + return &val + case *int32: + return v + case int32: + return &v + default: + log.Printf("%s cannot be converted to an int32", i) + return nil + } +} + // IntRangeMe takes an interface and returns a pointer to an IntRangeFilter value // If the underlying interface kind is not IntRangeFilter or *IntRangeFilter then nil is returned func (*CommandLineInterface) IntRangeMe(i interface{}) *selector.IntRangeFilter { @@ -122,6 +145,23 @@ func (*CommandLineInterface) IntRangeMe(i interface{}) *selector.IntRangeFilter } } +// Int32RangeMe takes an interface and returns a pointer to an Int32RangeFilter value +// If the underlying interface kind is not Int32RangeFilter or *Int32RangeFilter then nil is returned +func (*CommandLineInterface) Int32RangeMe(i interface{}) *selector.Int32RangeFilter { + if i == nil { + return nil + } + switch v := i.(type) { + case *selector.Int32RangeFilter: + return v + case selector.Int32RangeFilter: + return &v + default: + log.Printf("%s cannot be converted to an Int32Range", i) + return nil + } +} + // ByteQuantityRangeMe takes an interface and returns a pointer to a ByteQuantityRangeFilter value // If the underlying interface kind is not ByteQuantityRangeFilter or *ByteQuantityRangeFilter then nil is returned func (*CommandLineInterface) ByteQuantityRangeMe(i interface{}) *selector.ByteQuantityRangeFilter { diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/ec2pricing/ec2pricing.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/ec2pricing/ec2pricing.go index d13c941d8a771..18e7f5fc1ba1b 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/ec2pricing/ec2pricing.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/ec2pricing/ec2pricing.go @@ -14,12 +14,13 @@ package ec2pricing import ( + "context" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/pricing" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/aws/aws-sdk-go-v2/service/pricing" "go.uber.org/multierr" ) @@ -40,32 +41,39 @@ type EC2Pricing struct { // EC2PricingIface is the EC2Pricing interface mainly used to mock out ec2pricing during testing type EC2PricingIface interface { - GetOnDemandInstanceTypeCost(instanceType string) (float64, error) - GetSpotInstanceTypeNDayAvgCost(instanceType string, availabilityZones []string, days int) (float64, error) - RefreshOnDemandCache() error - RefreshSpotCache(days int) error + GetOnDemandInstanceTypeCost(ctx context.Context, instanceType ec2types.InstanceType) (float64, error) + GetSpotInstanceTypeNDayAvgCost(ctx context.Context, instanceType ec2types.InstanceType, availabilityZones []string, days int) (float64, error) + RefreshOnDemandCache(ctx context.Context) error + RefreshSpotCache(ctx context.Context, days int) error OnDemandCacheCount() int SpotCacheCount() int Save() error } +// use us-east-1 since pricing only has endpoints in us-east-1 and ap-south-1 +// TODO: In the future we may want to allow the client to select which endpoint is used through some mechanism +// but that would likely happen through overriding this entire function as its signature is fixed +func modifyPricingRegion(opt *pricing.Options) { + opt.Region = "us-east-1" +} + // New creates an instance of instance-selector EC2Pricing -func New(sess *session.Session) *EC2Pricing { - // use us-east-1 since pricing only has endpoints in us-east-1 and ap-south-1 - pricingClient := pricing.New(sess.Copy(aws.NewConfig().WithRegion("us-east-1"))) +func New(ctx context.Context, cfg aws.Config) (*EC2Pricing, error) { + pricingClient := pricing.NewFromConfig(cfg, modifyPricingRegion) + ec2Client := ec2.NewFromConfig(cfg) return &EC2Pricing{ - ODPricing: LoadODCacheOrNew(pricingClient, *sess.Config.Region, 0, ""), - SpotPricing: LoadSpotCacheOrNew(ec2.New(sess), *sess.Config.Region, 0, "", DefaultSpotDaysBack), - } + ODPricing: LoadODCacheOrNew(ctx, pricingClient, cfg.Region, 0, ""), + SpotPricing: LoadSpotCacheOrNew(ctx, ec2Client, cfg.Region, 0, "", DefaultSpotDaysBack), + }, nil } -func NewWithCache(sess *session.Session, ttl time.Duration, cacheDir string) *EC2Pricing { - // use us-east-1 since pricing only has endpoints in us-east-1 and ap-south-1 - pricingClient := pricing.New(sess.Copy(aws.NewConfig().WithRegion("us-east-1"))) +func NewWithCache(ctx context.Context, cfg aws.Config, ttl time.Duration, cacheDir string) (*EC2Pricing, error) { + pricingClient := pricing.NewFromConfig(cfg, modifyPricingRegion) + ec2Client := ec2.NewFromConfig(cfg) return &EC2Pricing{ - ODPricing: LoadODCacheOrNew(pricingClient, *sess.Config.Region, ttl, cacheDir), - SpotPricing: LoadSpotCacheOrNew(ec2.New(sess), *sess.Config.Region, ttl, cacheDir, DefaultSpotDaysBack), - } + ODPricing: LoadODCacheOrNew(ctx, pricingClient, cfg.Region, ttl, cacheDir), + SpotPricing: LoadSpotCacheOrNew(ctx, ec2Client, cfg.Region, ttl, cacheDir, DefaultSpotDaysBack), + }, nil } // OnDemandCacheCount returns the number of items in the OD cache @@ -80,14 +88,14 @@ func (p *EC2Pricing) SpotCacheCount() int { // GetSpotInstanceTypeNDayAvgCost retrieves the spot price history for a given AZ from the past N days and averages the price // Passing an empty list for availabilityZones will retrieve avg cost for all AZs in the current AWSSession's region -func (p *EC2Pricing) GetSpotInstanceTypeNDayAvgCost(instanceType string, availabilityZones []string, days int) (float64, error) { +func (p *EC2Pricing) GetSpotInstanceTypeNDayAvgCost(ctx context.Context, instanceType ec2types.InstanceType, availabilityZones []string, days int) (float64, error) { if len(availabilityZones) == 0 { - return p.SpotPricing.Get(instanceType, "", days) + return p.SpotPricing.Get(ctx, instanceType, "", days) } costs := []float64{} var errs error for _, zone := range availabilityZones { - cost, err := p.SpotPricing.Get(instanceType, zone, days) + cost, err := p.SpotPricing.Get(ctx, instanceType, zone, days) if err != nil { errs = multierr.Append(errs, err) } @@ -101,18 +109,18 @@ func (p *EC2Pricing) GetSpotInstanceTypeNDayAvgCost(instanceType string, availab } // GetOnDemandInstanceTypeCost retrieves the on-demand hourly cost for the specified instance type -func (p *EC2Pricing) GetOnDemandInstanceTypeCost(instanceType string) (float64, error) { - return p.ODPricing.Get(instanceType) +func (p *EC2Pricing) GetOnDemandInstanceTypeCost(ctx context.Context, instanceType ec2types.InstanceType) (float64, error) { + return p.ODPricing.Get(ctx, instanceType) } // RefreshOnDemandCache makes a bulk request to the pricing api to retrieve all instance type pricing and stores them in a local cache -func (p *EC2Pricing) RefreshOnDemandCache() error { - return p.ODPricing.Refresh() +func (p *EC2Pricing) RefreshOnDemandCache(ctx context.Context) error { + return p.ODPricing.Refresh(ctx) } // RefreshSpotCache makes a bulk request to the ec2 api to retrieve all spot instance type pricing and stores them in a local cache -func (p *EC2Pricing) RefreshSpotCache(days int) error { - return p.SpotPricing.Refresh(days) +func (p *EC2Pricing) RefreshSpotCache(ctx context.Context, days int) error { + return p.SpotPricing.Refresh(ctx, days) } func (p *EC2Pricing) Save() error { diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/ec2pricing/odpricing.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/ec2pricing/odpricing.go index 1cc99a6a4acd5..19cde3c7c7bcc 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/ec2pricing/odpricing.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/ec2pricing/odpricing.go @@ -14,6 +14,7 @@ package ec2pricing import ( + "context" "encoding/json" "errors" "fmt" @@ -26,10 +27,10 @@ import ( "sync" "time" - "github.com/aws/aws-sdk-go/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/aws/aws-sdk-go-v2/service/pricing" + pricingtypes "github.com/aws/aws-sdk-go-v2/service/pricing/types" "github.com/aws/aws-sdk-go/aws/endpoints" - "github.com/aws/aws-sdk-go/service/pricing" - "github.com/aws/aws-sdk-go/service/pricing/pricingiface" "github.com/mitchellh/go-homedir" "github.com/patrickmn/go-cache" "go.uber.org/multierr" @@ -44,11 +45,48 @@ type OnDemandPricing struct { FullRefreshTTL time.Duration DirectoryPath string cache *cache.Cache - pricingClient pricingiface.PricingAPI + pricingClient pricing.GetProductsAPIClient sync.RWMutex } -func LoadODCacheOrNew(pricingClient pricingiface.PricingAPI, region string, fullRefreshTTL time.Duration, directoryPath string) *OnDemandPricing { +type PricingList struct { + Product PricingListProduct `json:"product"` + ServiceCode string `json:"serviceCode"` + Terms ProductTerms `json:"terms"` + Version string `json:"version"` + PublicationDate string `json:"publicationDate"` +} + +type PricingListProduct struct { + ProductFamily string `json:"productFamily"` + ProductAttributes map[string]string `json:"attributes"` + SKU string `json:"sku"` +} + +type ProductTerms struct { + OnDemand map[string]ProductPricingInfo `json:"OnDemand"` + Reserved map[string]ProductPricingInfo `json:"Reserved"` +} + +type ProductPricingInfo struct { + PriceDimensions map[string]PriceDimensionInfo `json:"priceDimensions"` + SKU string `json:"sku"` + EffectiveDate string `json:"effectiveDate"` + OfferTermCode string `json:"offerTermCode"` + TermAttributes map[string]string `json:"termAttributes"` +} + +type PriceDimensionInfo struct { + Unit string `json:"unit"` + EndRange string `json:"endRange"` + Description string `json:"description"` + AppliesTo []string `json:"appliesTo"` + RateCode string `json:"rateCode"` + BeginRange string `json:"beginRange"` + PricePerUnit map[string]string `json:"pricePerUnit"` +} + +func LoadODCacheOrNew(ctx context.Context, pricingClient pricing.GetProductsAPIClient, region string, fullRefreshTTL time.Duration, directoryPath string) *OnDemandPricing { expandedDirPath, err := homedir.Expand(directoryPath) if err != nil { log.Printf("Unable to load on-demand pricing cache directory %s: %v", expandedDirPath, err) @@ -72,7 +110,7 @@ func LoadODCacheOrNew(pricingClient pricingiface.PricingAPI, region string, full return odPricing } // Start the cache refresh job - go odCacheRefreshJob(odPricing) + go odCacheRefreshJob(ctx, odPricing) odCache, err := loadODCacheFrom(fullRefreshTTL, region, expandedDirPath) if err != nil { if !errors.Is(err, os.ErrNotExist) { @@ -102,22 +140,22 @@ func getODCacheFilePath(region string, directoryPath string) string { return filepath.Join(directoryPath, fmt.Sprintf("%s-%s", region, ODCacheFileName)) } -func odCacheRefreshJob(odPricing *OnDemandPricing) { +func odCacheRefreshJob(ctx context.Context, odPricing *OnDemandPricing) { if odPricing.FullRefreshTTL <= 0 { return } refreshTicker := time.NewTicker(odPricing.FullRefreshTTL) for range refreshTicker.C { - if err := odPricing.Refresh(); err != nil { + if err := odPricing.Refresh(ctx); err != nil { log.Println(err) } } } -func (c *OnDemandPricing) Refresh() error { +func (c *OnDemandPricing) Refresh(ctx context.Context) error { c.Lock() defer c.Unlock() - odInstanceTypeCosts, err := c.fetchOnDemandPricing("") + odInstanceTypeCosts, err := c.fetchOnDemandPricing(ctx, "") if err != nil { return fmt.Errorf("there was a problem refreshing the on-demand instance type pricing cache: %v", err) } @@ -130,18 +168,18 @@ func (c *OnDemandPricing) Refresh() error { return nil } -func (c *OnDemandPricing) Get(instanceType string) (float64, error) { - if cost, ok := c.cache.Get(instanceType); ok { +func (c *OnDemandPricing) Get(ctx context.Context, instanceType ec2types.InstanceType) (float64, error) { + if cost, ok := c.cache.Get(string(instanceType)); ok { return cost.(float64), nil } c.RLock() defer c.RUnlock() - costs, err := c.fetchOnDemandPricing(instanceType) + costs, err := c.fetchOnDemandPricing(ctx, instanceType) if err != nil { return 0, fmt.Errorf("there was a problem fetching on-demand instance type pricing for %s: %v", instanceType, err) } - c.cache.SetDefault(instanceType, costs[instanceType]) - return costs[instanceType], nil + c.cache.SetDefault(string(instanceType), costs[string(instanceType)]) + return costs[string(instanceType)], nil } // Count of items in the cache @@ -157,7 +195,9 @@ func (c *OnDemandPricing) Save() error { if err != nil { return err } - os.Mkdir(c.DirectoryPath, 0755) + if err := os.Mkdir(c.DirectoryPath, 0755); err != nil && !errors.Is(err, os.ErrExist) { + return err + } return ioutil.WriteFile(getODCacheFilePath(c.Region, c.DirectoryPath), cacheBytes, 0644) } @@ -169,15 +209,24 @@ func (c *OnDemandPricing) Clear() error { } // fetchOnDemandPricing makes a bulk request to the pricing api to retrieve all instance type pricing if the instanceType is the empty string -// or, if instanceType is specified, it can request a specific instance type pricing -func (c *OnDemandPricing) fetchOnDemandPricing(instanceType string) (map[string]float64, error) { +// +// or, if instanceType is specified, it can request a specific instance type pricing +func (c *OnDemandPricing) fetchOnDemandPricing(ctx context.Context, instanceType ec2types.InstanceType) (map[string]float64, error) { odPricing := map[string]float64{} productInput := pricing.GetProductsInput{ - ServiceCode: aws.String(serviceCode), + ServiceCode: c.StringMe(serviceCode), Filters: c.getProductsInputFilters(instanceType), } var processingErr error - errAPI := c.pricingClient.GetProductsPages(&productInput, func(pricingOutput *pricing.GetProductsOutput, nextPage bool) bool { + + p := pricing.NewGetProductsPaginator(c.pricingClient, &productInput) + + for p.HasMorePages() { + pricingOutput, err := p.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get a page, %w", err) + } + for _, priceDoc := range pricingOutput.PriceList { instanceTypeName, price, errParse := c.parseOndemandUnitPrice(priceDoc) if errParse != nil { @@ -186,26 +235,39 @@ func (c *OnDemandPricing) fetchOnDemandPricing(instanceType string) (map[string] } odPricing[instanceTypeName] = price } - return true - }) - if errAPI != nil { - return odPricing, errAPI } return odPricing, processingErr } -func (c *OnDemandPricing) getProductsInputFilters(instanceType string) []*pricing.Filter { +// StringMe takes an interface and returns a pointer to a string value +// If the underlying interface kind is not string or *string then nil is returned +func (*OnDemandPricing) StringMe(i interface{}) *string { + if i == nil { + return nil + } + switch v := i.(type) { + case *string: + return v + case string: + return &v + default: + log.Printf("%s cannot be converted to a string", i) + return nil + } +} + +func (c *OnDemandPricing) getProductsInputFilters(instanceType ec2types.InstanceType) []pricingtypes.Filter { regionDescription := c.getRegionForPricingAPI() - filters := []*pricing.Filter{ - {Type: aws.String(pricing.FilterTypeTermMatch), Field: aws.String("ServiceCode"), Value: aws.String(serviceCode)}, - {Type: aws.String(pricing.FilterTypeTermMatch), Field: aws.String("operatingSystem"), Value: aws.String("linux")}, - {Type: aws.String(pricing.FilterTypeTermMatch), Field: aws.String("location"), Value: aws.String(regionDescription)}, - {Type: aws.String(pricing.FilterTypeTermMatch), Field: aws.String("capacitystatus"), Value: aws.String("used")}, - {Type: aws.String(pricing.FilterTypeTermMatch), Field: aws.String("preInstalledSw"), Value: aws.String("NA")}, - {Type: aws.String(pricing.FilterTypeTermMatch), Field: aws.String("tenancy"), Value: aws.String("shared")}, + filters := []pricingtypes.Filter{ + {Type: pricingtypes.FilterTypeTermMatch, Field: c.StringMe("ServiceCode"), Value: c.StringMe(serviceCode)}, + {Type: pricingtypes.FilterTypeTermMatch, Field: c.StringMe("operatingSystem"), Value: c.StringMe("linux")}, + {Type: pricingtypes.FilterTypeTermMatch, Field: c.StringMe("location"), Value: c.StringMe(regionDescription)}, + {Type: pricingtypes.FilterTypeTermMatch, Field: c.StringMe("capacitystatus"), Value: c.StringMe("used")}, + {Type: pricingtypes.FilterTypeTermMatch, Field: c.StringMe("preInstalledSw"), Value: c.StringMe("NA")}, + {Type: pricingtypes.FilterTypeTermMatch, Field: c.StringMe("tenancy"), Value: c.StringMe("shared")}, } if instanceType != "" { - filters = append(filters, &pricing.Filter{Type: aws.String(pricing.FilterTypeTermMatch), Field: aws.String("instanceType"), Value: aws.String(instanceType)}) + filters = append(filters, pricingtypes.Filter{Type: pricingtypes.FilterTypeTermMatch, Field: c.StringMe("instanceType"), Value: c.StringMe(string(instanceType))}) } return filters } @@ -235,43 +297,25 @@ func (c *OnDemandPricing) getRegionForPricingAPI() string { } // parseOndemandUnitPrice takes a priceList from the pricing API and parses its weirdness -func (c *OnDemandPricing) parseOndemandUnitPrice(priceList aws.JSONValue) (string, float64, error) { - // TODO: this could probably be cleaned up a bit by adding a couple structs with json tags - // We still need to some weird for-loops to get at elements under json keys that are IDs... - // But it would probably be cleaner than this. - attributes, ok := priceList["product"].(map[string]interface{})["attributes"] - if !ok { - return "", float64(-1.0), fmt.Errorf("unable to find product attributes") - } - instanceTypeName, ok := attributes.(map[string]interface{})["instanceType"].(string) - if !ok { - return "", float64(-1.0), fmt.Errorf("unable to find instance type name from product attributes") - } - terms, ok := priceList["terms"] - if !ok { - return instanceTypeName, float64(-1.0), fmt.Errorf("unable to find pricing terms") - } - ondemandTerms, ok := terms.(map[string]interface{})["OnDemand"] - if !ok { - return instanceTypeName, float64(-1.0), fmt.Errorf("unable to find on-demand pricing terms") +func (c *OnDemandPricing) parseOndemandUnitPrice(priceList string) (string, float64, error) { + var productPriceList PricingList + err := json.Unmarshal([]byte(priceList), &productPriceList) + if err != nil { + return "", float64(-1.0), fmt.Errorf("unable to parse pricing doc: %w", err) } - for _, priceDimensions := range ondemandTerms.(map[string]interface{}) { - dim, ok := priceDimensions.(map[string]interface{})["priceDimensions"] - if !ok { - return instanceTypeName, float64(-1.0), fmt.Errorf("unable to find on-demand pricing dimensions") - } - for _, dimension := range dim.(map[string]interface{}) { - dims := dimension.(map[string]interface{}) - pricePerUnit, ok := dims["pricePerUnit"] - if !ok { - return instanceTypeName, float64(-1.0), fmt.Errorf("unable to find on-demand price per unit in pricing dimensions") - } - pricePerUnitInUSDStr, ok := pricePerUnit.(map[string]interface{})["USD"] + attributes := productPriceList.Product.ProductAttributes + instanceTypeName := attributes["instanceType"] + + for _, priceDimensions := range productPriceList.Terms.OnDemand { + dim := priceDimensions.PriceDimensions + for _, dimension := range dim { + pricePerUnit := dimension.PricePerUnit + pricePerUnitInUSDStr, ok := pricePerUnit["USD"] if !ok { return instanceTypeName, float64(-1.0), fmt.Errorf("unable to find on-demand price per unit in USD") } var err error - pricePerUnitInUSD, err := strconv.ParseFloat(pricePerUnitInUSDStr.(string), 64) + pricePerUnitInUSD, err := strconv.ParseFloat(pricePerUnitInUSDStr, 64) if err != nil { return instanceTypeName, float64(-1.0), fmt.Errorf("could not convert price per unit in USD to a float64") } diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/ec2pricing/spotpricing.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/ec2pricing/spotpricing.go index 6f4bc6d5ea897..cbf56e9fcb4b7 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/ec2pricing/spotpricing.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/ec2pricing/spotpricing.go @@ -14,6 +14,7 @@ package ec2pricing import ( + "context" "encoding/gob" "errors" "fmt" @@ -26,9 +27,8 @@ import ( "sync" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/mitchellh/go-homedir" "github.com/patrickmn/go-cache" "go.uber.org/multierr" @@ -43,7 +43,7 @@ type SpotPricing struct { FullRefreshTTL time.Duration DirectoryPath string cache *cache.Cache - ec2Client ec2iface.EC2API + ec2Client ec2.DescribeSpotPriceHistoryAPIClient sync.RWMutex } @@ -53,7 +53,7 @@ type spotPricingEntry struct { Zone string } -func LoadSpotCacheOrNew(ec2Client ec2iface.EC2API, region string, fullRefreshTTL time.Duration, directoryPath string, days int) *SpotPricing { +func LoadSpotCacheOrNew(ctx context.Context, ec2Client ec2.DescribeSpotPriceHistoryAPIClient, region string, fullRefreshTTL time.Duration, directoryPath string, days int) *SpotPricing { expandedDirPath, err := homedir.Expand(directoryPath) if err != nil { log.Printf("Unable to load spot pricing cache directory %s: %v", expandedDirPath, err) @@ -78,7 +78,7 @@ func LoadSpotCacheOrNew(ec2Client ec2iface.EC2API, region string, fullRefreshTTL } gob.Register([]*spotPricingEntry{}) // Start the cache refresh job - go spotCacheRefreshJob(spotPricing, days) + go spotCacheRefreshJob(ctx, spotPricing, days) spotCache, err := loadSpotCacheFrom(fullRefreshTTL, region, expandedDirPath) if err != nil { if !errors.Is(err, os.ErrNotExist) { @@ -109,22 +109,22 @@ func getSpotCacheFilePath(region string, directoryPath string) string { return filepath.Join(directoryPath, fmt.Sprintf("%s-%s", region, SpotCacheFileName)) } -func spotCacheRefreshJob(spotPricing *SpotPricing, days int) { +func spotCacheRefreshJob(ctx context.Context, spotPricing *SpotPricing, days int) { if spotPricing.FullRefreshTTL <= 0 { return } refreshTicker := time.NewTicker(spotPricing.FullRefreshTTL) for range refreshTicker.C { - if err := spotPricing.Refresh(days); err != nil { + if err := spotPricing.Refresh(ctx, days); err != nil { log.Println(err) } } } -func (c *SpotPricing) Refresh(days int) error { +func (c *SpotPricing) Refresh(ctx context.Context, days int) error { c.Lock() defer c.Unlock() - spotInstanceTypeCosts, err := c.fetchSpotPricingTimeSeries("", days) + spotInstanceTypeCosts, err := c.fetchSpotPricingTimeSeries(ctx, "", days) if err != nil { return fmt.Errorf("there was a problem refreshing the spot instance type pricing cache: %v", err) } @@ -137,8 +137,8 @@ func (c *SpotPricing) Refresh(days int) error { return nil } -func (c *SpotPricing) Get(instanceType string, zone string, days int) (float64, error) { - entries, ok := c.cache.Get(instanceType) +func (c *SpotPricing) Get(ctx context.Context, instanceType ec2types.InstanceType, zone string, days int) (float64, error) { + entries, ok := c.cache.Get(string(instanceType)) if zone != "" && ok { if !c.contains(zone, entries.([]*spotPricingEntry)) { ok = false @@ -147,7 +147,7 @@ func (c *SpotPricing) Get(instanceType string, zone string, days int) (float64, if !ok { c.RLock() defer c.RUnlock() - zonalSpotPricing, err := c.fetchSpotPricingTimeSeries(instanceType, days) + zonalSpotPricing, err := c.fetchSpotPricingTimeSeries(ctx, instanceType, days) if err != nil { return -1, fmt.Errorf("there was a problem fetching spot instance type pricing for %s: %v", instanceType, err) } @@ -156,7 +156,7 @@ func (c *SpotPricing) Get(instanceType string, zone string, days int) (float64, } } - entries, ok = c.cache.Get(instanceType) + entries, ok = c.cache.Get(string(instanceType)) if !ok { return -1, fmt.Errorf("unable to get spot pricing for %s in zone %s for %d days back", instanceType, zone, days) } @@ -240,37 +240,42 @@ func (c *SpotPricing) Clear() error { // fetchSpotPricingTimeSeries makes a bulk request to the ec2 api to retrieve all spot instance type pricing for the past n days // If instanceType is empty, it will fetch for all instance types -func (c *SpotPricing) fetchSpotPricingTimeSeries(instanceType string, days int) (map[string][]*spotPricingEntry, error) { +func (c *SpotPricing) fetchSpotPricingTimeSeries(ctx context.Context, instanceType ec2types.InstanceType, days int) (map[string][]*spotPricingEntry, error) { spotTimeSeries := map[string][]*spotPricingEntry{} endTime := time.Now().UTC() startTime := endTime.Add(time.Hour * time.Duration(24*-1*days)) spotPriceHistInput := ec2.DescribeSpotPriceHistoryInput{ - ProductDescriptions: []*string{aws.String(productDescription)}, + ProductDescriptions: []string{productDescription}, StartTime: &startTime, EndTime: &endTime, } if instanceType != "" { - spotPriceHistInput.InstanceTypes = append(spotPriceHistInput.InstanceTypes, &instanceType) + spotPriceHistInput.InstanceTypes = append(spotPriceHistInput.InstanceTypes, instanceType) } var processingErr error - errAPI := c.ec2Client.DescribeSpotPriceHistoryPages(&spotPriceHistInput, func(dspho *ec2.DescribeSpotPriceHistoryOutput, b bool) bool { - for _, history := range dspho.SpotPriceHistory { + + p := ec2.NewDescribeSpotPriceHistoryPaginator(c.ec2Client, &spotPriceHistInput) + + // Iterate through the Amazon S3 object pages. + for p.HasMorePages() { + spotHistoryOutput, err := p.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get a page, %w", err) + } + + for _, history := range spotHistoryOutput.SpotPriceHistory { spotPrice, errFloat := strconv.ParseFloat(*history.SpotPrice, 64) if errFloat != nil { processingErr = multierr.Append(processingErr, errFloat) continue } - instanceType := *history.InstanceType - spotTimeSeries[instanceType] = append(spotTimeSeries[instanceType], &spotPricingEntry{ + spotTimeSeries[string(history.InstanceType)] = append(spotTimeSeries[string(history.InstanceType)], &spotPricingEntry{ Timestamp: *history.Timestamp, SpotPrice: spotPrice, Zone: *history.AvailabilityZone, }) } - return true - }) - if errAPI != nil { - return spotTimeSeries, errAPI } + return spotTimeSeries, processingErr } diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/instancetypes/instancetypes.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/instancetypes/instancetypes.go index 4e83a84639a4a..f38512d37a752 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/instancetypes/instancetypes.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/instancetypes/instancetypes.go @@ -14,6 +14,7 @@ package instancetypes import ( + "context" "encoding/json" "errors" "fmt" @@ -23,8 +24,8 @@ import ( "path/filepath" "time" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/mitchellh/go-homedir" "github.com/patrickmn/go-cache" ) @@ -35,7 +36,7 @@ var ( // Details hold all the information on an ec2 instance type type Details struct { - ec2.InstanceTypeInfo + ec2types.InstanceTypeInfo OndemandPricePerHour *float64 SpotPrice *float64 } @@ -45,11 +46,11 @@ type Provider struct { DirectoryPath string FullRefreshTTL time.Duration lastFullRefresh *time.Time - ec2Client ec2iface.EC2API + ec2Client ec2.DescribeInstanceTypesAPIClient cache *cache.Cache } -func NewProvider(directoryPath string, region string, ttl time.Duration, ec2Client ec2iface.EC2API) *Provider { +func NewProvider(directoryPath string, region string, ttl time.Duration, ec2Client ec2.DescribeInstanceTypesAPIClient) *Provider { expandedDirPath, err := homedir.Expand(directoryPath) if err != nil { log.Printf("Unable to expand instance type cache directory %s: %v", directoryPath, err) @@ -63,7 +64,7 @@ func NewProvider(directoryPath string, region string, ttl time.Duration, ec2Clie } } -func LoadFromOrNew(directoryPath string, region string, ttl time.Duration, ec2Client ec2iface.EC2API) *Provider { +func LoadFromOrNew(directoryPath string, region string, ttl time.Duration, ec2Client ec2.DescribeInstanceTypesAPIClient) *Provider { expandedDirPath, err := homedir.Expand(directoryPath) if err != nil { log.Printf("Unable to load instance-type cache directory %s: %v", expandedDirPath, err) @@ -106,17 +107,17 @@ func getCacheFilePath(region string, expandedDirPath string) string { return filepath.Join(expandedDirPath, fmt.Sprintf("%s-%s", region, CacheFileName)) } -func (p *Provider) Get(instanceTypes []string) ([]*Details, error) { +func (p *Provider) Get(ctx context.Context, instanceTypes []ec2types.InstanceType) ([]*Details, error) { instanceTypeDetails := []*Details{} describeInstanceTypeOpts := &ec2.DescribeInstanceTypesInput{} if len(instanceTypes) != 0 { for _, it := range instanceTypes { - if cachedIT, ok := p.cache.Get(it); ok { + if cachedIT, ok := p.cache.Get(string(it)); ok { instanceTypeDetails = append(instanceTypeDetails, cachedIT.(*Details)) } else { - // need to reassign so we're not sharing the loop iterators memory space + // need to reassign, so we're not sharing the loop iterators memory space instanceType := it - describeInstanceTypeOpts.InstanceTypes = append(describeInstanceTypeOpts.InstanceTypes, &instanceType) + describeInstanceTypeOpts.InstanceTypes = append(describeInstanceTypeOpts.InstanceTypes, instanceType) } } } else if p.lastFullRefresh != nil && !p.isFullRefreshNeeded() { @@ -125,17 +126,22 @@ func (p *Provider) Get(instanceTypes []string) ([]*Details, error) { } return instanceTypeDetails, nil } - if err := p.ec2Client.DescribeInstanceTypesPages(&ec2.DescribeInstanceTypesInput{}, func(page *ec2.DescribeInstanceTypesOutput, lastPage bool) bool { - for _, instanceTypeInfo := range page.InstanceTypes { - itDetails := &Details{InstanceTypeInfo: *instanceTypeInfo} + + s := ec2.NewDescribeInstanceTypesPaginator(p.ec2Client, &ec2.DescribeInstanceTypesInput{}) + + for s.HasMorePages() { + instanceTypeOutput, err := s.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get a page, %w", err) + } + + for _, instanceTypeInfo := range instanceTypeOutput.InstanceTypes { + itDetails := &Details{InstanceTypeInfo: instanceTypeInfo} instanceTypeDetails = append(instanceTypeDetails, itDetails) - p.cache.SetDefault(*instanceTypeInfo.InstanceType, itDetails) + p.cache.SetDefault(string(instanceTypeInfo.InstanceType), itDetails) } - // continue paging through instance types - return true - }); err != nil { - return instanceTypeDetails, err } + if len(instanceTypes) == 0 { now := time.Now().UTC() p.lastFullRefresh = &now @@ -158,7 +164,9 @@ func (p *Provider) Save() error { if err != nil { return err } - os.Mkdir(p.DirectoryPath, 0755) + if err := os.Mkdir(p.DirectoryPath, 0755); err != nil && !errors.Is(err, os.ErrExist) { + return err + } return ioutil.WriteFile(getCacheFilePath(p.Region, p.DirectoryPath), cacheBytes, 0644) } diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/aggregates.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/aggregates.go index b17bf062d6026..b5f3e613bf703 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/aggregates.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/aggregates.go @@ -1,12 +1,12 @@ package selector import ( + "context" "fmt" - "regexp" - "github.com/aws/amazon-ec2-instance-selector/v2/pkg/bytequantity" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "regexp" ) const ( @@ -18,25 +18,27 @@ const ( // FiltersTransform can be implemented to provide custom transforms type FiltersTransform interface { - Transform(Filters) (Filters, error) + Transform(context.Context, Filters) (Filters, error) } // TransformFn is the func type definition for a FiltersTransform -type TransformFn func(Filters) (Filters, error) +type TransformFn func(context.Context, Filters) (Filters, error) // Transform implements FiltersTransform interface on TransformFn // This allows any TransformFn to be passed into funcs accepting FiltersTransform interface -func (fn TransformFn) Transform(filters Filters) (Filters, error) { - return fn(filters) +func (fn TransformFn) Transform(ctx context.Context, filters Filters) (Filters, error) { + return fn(ctx, filters) } // TransformBaseInstanceType transforms lower level filters based on the instanceTypeBase specs -func (itf Selector) TransformBaseInstanceType(filters Filters) (Filters, error) { +func (itf Selector) TransformBaseInstanceType(ctx context.Context, filters Filters) (Filters, error) { if filters.InstanceTypeBase == nil { return filters, nil } - instanceTypesOutput, err := itf.EC2.DescribeInstanceTypes(&ec2.DescribeInstanceTypesInput{ - InstanceTypes: []*string{filters.InstanceTypeBase}, + instanceTypesOutput, err := itf.EC2.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{ + InstanceTypes: []ec2types.InstanceType{ + ec2types.InstanceType(*filters.InstanceTypeBase), + }, }) if err != nil { return filters, err @@ -49,18 +51,18 @@ func (itf Selector) TransformBaseInstanceType(filters Filters) (Filters, error) filters.BareMetal = instanceTypeInfo.BareMetal } if filters.CPUArchitecture == nil && len(instanceTypeInfo.ProcessorInfo.SupportedArchitectures) == 1 { - filters.CPUArchitecture = instanceTypeInfo.ProcessorInfo.SupportedArchitectures[0] + filters.CPUArchitecture = &instanceTypeInfo.ProcessorInfo.SupportedArchitectures[0] } if filters.Fpga == nil { isFpgaSupported := instanceTypeInfo.FpgaInfo != nil filters.Fpga = &isFpgaSupported } if filters.GpusRange == nil { - gpuCount := 0 + gpuCount := int32(0) if instanceTypeInfo.GpuInfo != nil { - gpuCount = int(*getTotalGpusCount(instanceTypeInfo.GpuInfo)) + gpuCount = *getTotalGpusCount(instanceTypeInfo.GpuInfo) } - filters.GpusRange = &IntRangeFilter{LowerBound: gpuCount, UpperBound: gpuCount} + filters.GpusRange = &Int32RangeFilter{LowerBound: gpuCount, UpperBound: gpuCount} } if filters.MemoryRange == nil { lowerBound := bytequantity.ByteQuantity{Quantity: uint64(float64(*instanceTypeInfo.MemoryInfo.SizeInMiB) * AggregateLowPercentile)} @@ -68,12 +70,12 @@ func (itf Selector) TransformBaseInstanceType(filters Filters) (Filters, error) filters.MemoryRange = &ByteQuantityRangeFilter{LowerBound: lowerBound, UpperBound: upperBound} } if filters.VCpusRange == nil { - lowerBound := int(float64(*instanceTypeInfo.VCpuInfo.DefaultVCpus) * AggregateLowPercentile) - upperBound := int(float64(*instanceTypeInfo.VCpuInfo.DefaultVCpus) * AggregateHighPercentile) - filters.VCpusRange = &IntRangeFilter{LowerBound: lowerBound, UpperBound: upperBound} + lowerBound := int32(float32(*instanceTypeInfo.VCpuInfo.DefaultVCpus) * AggregateLowPercentile) + upperBound := int32(float32(*instanceTypeInfo.VCpuInfo.DefaultVCpus) * AggregateHighPercentile) + filters.VCpusRange = &Int32RangeFilter{LowerBound: lowerBound, UpperBound: upperBound} } if filters.VirtualizationType == nil && len(instanceTypeInfo.SupportedVirtualizationTypes) == 1 { - filters.VirtualizationType = instanceTypeInfo.SupportedVirtualizationTypes[0] + filters.VirtualizationType = &instanceTypeInfo.SupportedVirtualizationTypes[0] } filters.InstanceTypeBase = nil @@ -81,18 +83,21 @@ func (itf Selector) TransformBaseInstanceType(filters Filters) (Filters, error) } // TransformFlexible transforms lower level filters based on a set of opinions -func (itf Selector) TransformFlexible(filters Filters) (Filters, error) { +func (itf Selector) TransformFlexible(ctx context.Context, filters Filters) (Filters, error) { if filters.Flexible == nil { return filters, nil } if filters.CPUArchitecture == nil { - filters.CPUArchitecture = aws.String("x86_64") + defaultArchitecture := ec2types.ArchitectureTypeX8664 + filters.CPUArchitecture = &defaultArchitecture } if filters.BareMetal == nil { - filters.BareMetal = aws.Bool(false) + bareMetalDefault := false + filters.BareMetal = &bareMetalDefault } if filters.Fpga == nil { - filters.Fpga = aws.Bool(false) + fpgaDefault := false + filters.Fpga = &fpgaDefault } if filters.AllowList == nil { @@ -104,14 +109,14 @@ func (itf Selector) TransformFlexible(filters Filters) (Filters, error) { } if filters.VCpusRange == nil && filters.MemoryRange == nil { - defaultVcpus := 4 - filters.VCpusRange = &IntRangeFilter{LowerBound: defaultVcpus, UpperBound: defaultVcpus} + defaultVcpus := int32(4) + filters.VCpusRange = &Int32RangeFilter{LowerBound: defaultVcpus, UpperBound: defaultVcpus} } return filters, nil } // TransformForService transforms lower level filters based on the service -func (itf Selector) TransformForService(filters Filters) (Filters, error) { +func (itf Selector) TransformForService(ctx context.Context, filters Filters) (Filters, error) { return itf.ServiceRegistry.ExecuteTransforms(filters) } diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/comparators.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/comparators.go index d1070bae4e373..eb639ecf9a071 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/comparators.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/comparators.go @@ -16,12 +16,13 @@ package selector import ( "log" "math" + "reflect" "regexp" "strconv" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" ) const ( @@ -68,6 +69,106 @@ func isSupportedWithFloat64(instanceTypeValue *float64, target *float64) bool { return math.Floor(*instanceTypeValue*100)/100 == math.Floor(*target*100)/100 } +func isSupportedUsageClassType(instanceTypeValue []ec2types.UsageClassType, target *ec2types.UsageClassType) bool { + if target == nil { + return true + } + if instanceTypeValue == nil { + return false + } + if reflect.ValueOf(*target).IsZero() { + return true + } + + for _, potentialType := range instanceTypeValue { + if potentialType == *target { + return true + } + } + return false +} + +func isSupportedArchitectureType(instanceTypeValue []ec2types.ArchitectureType, target *ec2types.ArchitectureType) bool { + if target == nil { + return true + } + if instanceTypeValue == nil { + return false + } + if reflect.ValueOf(*target).IsZero() { + return true + } + + for _, potentialType := range instanceTypeValue { + if potentialType == *target { + return true + } + } + return false +} + +func isSupportedVirtualizationType(instanceTypeValue []ec2types.VirtualizationType, target *ec2types.VirtualizationType) bool { + if target == nil { + return true + } + if instanceTypeValue == nil { + return false + } + if reflect.ValueOf(*target).IsZero() { + return true + } + for _, potentialType := range instanceTypeValue { + if potentialType == *target { + return true + } + } + return false +} + +func isSupportedInstanceTypeHypervisorType(instanceTypeValue ec2types.InstanceTypeHypervisor, target *ec2types.InstanceTypeHypervisor) bool { + if target == nil { + return true + } + if reflect.ValueOf(*target).IsZero() { + return true + } + if instanceTypeValue == *target { + return true + } + return false +} + +func isSupportedRootDeviceType(instanceTypeValue []ec2types.RootDeviceType, target *ec2types.RootDeviceType) bool { + if target == nil { + return true + } + if instanceTypeValue == nil { + return false + } + if reflect.ValueOf(*target).IsZero() { + return true + } + for _, potentialType := range instanceTypeValue { + if potentialType == *target { + return true + } + } + return false +} + +func isMatchingCpuArchitecture(instanceTypeValue CPUManufacturer, target *CPUManufacturer) bool { + if target == nil { + return true + } + if reflect.ValueOf(*target).IsZero() { + return true + } + if instanceTypeValue == *target { + return true + } + return false +} + func isSupportedWithRangeInt64(instanceTypeValue *int64, target *IntRangeFilter) bool { if target == nil { return true @@ -79,6 +180,17 @@ func isSupportedWithRangeInt64(instanceTypeValue *int64, target *IntRangeFilter) return int(*instanceTypeValue) >= target.LowerBound && int(*instanceTypeValue) <= target.UpperBound } +func isSupportedWithRangeInt32(instanceTypeValue *int32, target *Int32RangeFilter) bool { + if target == nil { + return true + } else if instanceTypeValue == nil && target.LowerBound == 0 && target.UpperBound == 0 { + return true + } else if instanceTypeValue == nil { + return false + } + return *instanceTypeValue >= target.LowerBound && *instanceTypeValue <= target.UpperBound +} + func isSupportedWithRangeUint64(instanceTypeValue *int64, target *Uint64RangeFilter) bool { if target == nil { return true @@ -113,36 +225,36 @@ func isSupportedWithBool(instanceTypeValue *bool, target *bool) bool { // Helper functions for aggregating data parsed from AWS API calls -func getTotalAcceleratorsCount(acceleratorInfo *ec2.InferenceAcceleratorInfo) *int64 { +func getTotalAcceleratorsCount(acceleratorInfo *ec2types.InferenceAcceleratorInfo) *int32 { if acceleratorInfo == nil { return nil } - total := aws.Int64(0) + total := int32(0) for _, accel := range acceleratorInfo.Accelerators { - total = aws.Int64(*total + *accel.Count) + total = total + *accel.Count } - return total + return &total } -func getTotalGpusCount(gpusInfo *ec2.GpuInfo) *int64 { +func getTotalGpusCount(gpusInfo *ec2types.GpuInfo) *int32 { if gpusInfo == nil { return nil } - total := aws.Int64(0) + total := int32(0) for _, gpu := range gpusInfo.Gpus { - total = aws.Int64(*total + *gpu.Count) + total = total + *gpu.Count } - return total + return &total } -func getTotalGpuMemory(gpusInfo *ec2.GpuInfo) *int64 { +func getTotalGpuMemory(gpusInfo *ec2types.GpuInfo) *int64 { if gpusInfo == nil { return nil } - return gpusInfo.TotalGpuMemoryInMiB + return aws.Int64(int64(*gpusInfo.TotalGpuMemoryInMiB)) } -func getGPUManufacturers(gpusInfo *ec2.GpuInfo) []*string { +func getGPUManufacturers(gpusInfo *ec2types.GpuInfo) []*string { if gpusInfo == nil { return nil } @@ -153,7 +265,7 @@ func getGPUManufacturers(gpusInfo *ec2.GpuInfo) []*string { return manufacturers } -func getGPUModels(gpusInfo *ec2.GpuInfo) []*string { +func getGPUModels(gpusInfo *ec2types.GpuInfo) []*string { if gpusInfo == nil { return nil } @@ -164,7 +276,7 @@ func getGPUModels(gpusInfo *ec2.GpuInfo) []*string { return models } -func getInferenceAcceleratorManufacturers(acceleratorInfo *ec2.InferenceAcceleratorInfo) []*string { +func getInferenceAcceleratorManufacturers(acceleratorInfo *ec2types.InferenceAcceleratorInfo) []*string { if acceleratorInfo == nil { return nil } @@ -175,7 +287,7 @@ func getInferenceAcceleratorManufacturers(acceleratorInfo *ec2.InferenceAccelera return manufacturers } -func getInferenceAcceleratorModels(acceleratorInfo *ec2.InferenceAcceleratorInfo) []*string { +func getInferenceAcceleratorModels(acceleratorInfo *ec2types.InferenceAcceleratorInfo) []*string { if acceleratorInfo == nil { return nil } @@ -210,69 +322,74 @@ func getNetworkPerformance(networkPerformance *string) *int { return aws.Int(bandwidthNumber) } -func getInstanceStorage(instanceStorageInfo *ec2.InstanceStorageInfo) *int64 { +func getInstanceStorage(instanceStorageInfo *ec2types.InstanceStorageInfo) *int64 { if instanceStorageInfo == nil { return aws.Int64(0) } return aws.Int64(*instanceStorageInfo.TotalSizeInGB * 1024) } -func getDiskType(instanceStorageInfo *ec2.InstanceStorageInfo) *string { +func getDiskType(instanceStorageInfo *ec2types.InstanceStorageInfo) *string { if instanceStorageInfo == nil || len(instanceStorageInfo.Disks) == 0 { return nil } - return instanceStorageInfo.Disks[0].Type + return aws.String(string(instanceStorageInfo.Disks[0].Type)) } -func getNVMESupport(instanceStorageInfo *ec2.InstanceStorageInfo, ebsInfo *ec2.EbsInfo) *bool { +func getNVMESupport(instanceStorageInfo *ec2types.InstanceStorageInfo, ebsInfo *ec2types.EbsInfo) *bool { if instanceStorageInfo != nil { - return supportSyntaxToBool(instanceStorageInfo.NvmeSupport) + return supportSyntaxToBool(aws.String(string(instanceStorageInfo.NvmeSupport))) } if ebsInfo != nil { - return supportSyntaxToBool(ebsInfo.EbsOptimizedSupport) + return supportSyntaxToBool(aws.String(string(ebsInfo.EbsOptimizedSupport))) } return aws.Bool(false) } -func getDiskEncryptionSupport(instanceStorageInfo *ec2.InstanceStorageInfo, ebsInfo *ec2.EbsInfo) *bool { +func getDiskEncryptionSupport(instanceStorageInfo *ec2types.InstanceStorageInfo, ebsInfo *ec2types.EbsInfo) *bool { if instanceStorageInfo != nil { - return supportSyntaxToBool(instanceStorageInfo.EncryptionSupport) + encryptionSupport := string(instanceStorageInfo.EncryptionSupport) + return supportSyntaxToBool(&encryptionSupport) } if ebsInfo != nil { - return supportSyntaxToBool(ebsInfo.EncryptionSupport) + ebsEncryptionSupport := string(ebsInfo.EncryptionSupport) + return supportSyntaxToBool(&ebsEncryptionSupport) } return aws.Bool(false) } -func getEBSOptimizedBaselineBandwidth(ebsInfo *ec2.EbsInfo) *int64 { +func getEBSOptimizedBaselineBandwidth(ebsInfo *ec2types.EbsInfo) *int32 { if ebsInfo == nil || ebsInfo.EbsOptimizedInfo == nil { return nil } return ebsInfo.EbsOptimizedInfo.BaselineBandwidthInMbps } -func getEBSOptimizedBaselineThroughput(ebsInfo *ec2.EbsInfo) *float64 { +func getEBSOptimizedBaselineThroughput(ebsInfo *ec2types.EbsInfo) *float64 { if ebsInfo == nil || ebsInfo.EbsOptimizedInfo == nil { return nil } return ebsInfo.EbsOptimizedInfo.BaselineThroughputInMBps } -func getEBSOptimizedBaselineIOPS(ebsInfo *ec2.EbsInfo) *int64 { +func getEBSOptimizedBaselineIOPS(ebsInfo *ec2types.EbsInfo) *int32 { if ebsInfo == nil || ebsInfo.EbsOptimizedInfo == nil { return nil } return ebsInfo.EbsOptimizedInfo.BaselineIops } -func getCPUManufacturer(instanceTypeInfo *ec2.InstanceTypeInfo) *string { - if contains(instanceTypeInfo.ProcessorInfo.SupportedArchitectures, ec2.ArchitectureTypeArm64) { - return aws.String("aws") +func getCPUManufacturer(instanceTypeInfo *ec2types.InstanceTypeInfo) CPUManufacturer { + for _, it := range instanceTypeInfo.ProcessorInfo.SupportedArchitectures { + if it == ec2types.ArchitectureTypeArm64 { + return CPUManufacturerAWS + } } - if amdRegex.Match([]byte(*instanceTypeInfo.InstanceType)) { - return aws.String("amd") + + if amdRegex.Match([]byte(instanceTypeInfo.InstanceType)) { + return CPUManufacturerAMD } - return aws.String("intel") + return CPUManufacturerIntel } // supportSyntaxToBool takes an instance spec field that uses ["unsupported", "supported", "required", or "default"] @@ -287,7 +404,7 @@ func supportSyntaxToBool(instanceTypeSupport *string) *bool { return aws.Bool(false) } -func calculateVCpusToMemoryRatio(vcpusVal *int64, memoryVal *int64) *float64 { +func calculateVCpusToMemoryRatio(vcpusVal *int32, memoryVal *int64) *float64 { if vcpusVal == nil || *vcpusVal == 0 || memoryVal == nil { return nil } diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/eks.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/eks.go deleted file mode 100644 index 184131fcaf8a1..0000000000000 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/eks.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"). You may -// not use this file except in compliance with the License. A copy of the -// License is located at -// -// http://aws.amazon.com/apache2.0/ -// -// or in the "license" file accompanying this file. This file 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 selector - -import ( - "archive/zip" - "bytes" - "fmt" - "io/ioutil" - "log" - "net/http" - "strings" - - "github.com/aws/aws-sdk-go/aws" -) - -const ( - eksAMIRepoURL = "https://github.com/awslabs/amazon-eks-ami" - eksFallbackLatestAMIVersion = "v20210125" - eksInstanceTypesFile = "eni-max-pods.txt" -) - -// EKS is a Service type for a custom service filter transform -type EKS struct { - AMIRepoURL string -} - -// Filters implements the Service interface contract for EKS -func (e *EKS) Filters(version string) (Filters, error) { - if e.AMIRepoURL == "" { - e.AMIRepoURL = eksAMIRepoURL - } - var filters Filters - - if version == "" { - var err error - version, err = e.getLatestAMIVersion() - if err != nil { - log.Printf("There was a problem fetching the latest EKS AMI version, using hardcoded fallback version %s\n", eksFallbackLatestAMIVersion) - version = eksFallbackLatestAMIVersion - } - } - supportedInstanceTypes, err := e.getSupportedInstanceTypes(version) - if err != nil { - log.Printf("Unable to retrieve EKS supported instance types for version %s: %v", version, err) - return filters, err - } - filters.InstanceTypes = &supportedInstanceTypes - filters.VirtualizationType = aws.String("hvm") - return filters, nil -} - -func (e *EKS) getSupportedInstanceTypes(version string) ([]string, error) { - supportedInstanceTypes := []string{} - resp, err := http.Get(fmt.Sprintf("%s/archive/%s.zip", e.AMIRepoURL, version)) - if err != nil { - return supportedInstanceTypes, err - } - - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return supportedInstanceTypes, fmt.Errorf("Unable to retrieve EKS supported instance types, got non-200 status code: %d", resp.StatusCode) - } - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return supportedInstanceTypes, err - } - - zipReader, err := zip.NewReader(bytes.NewReader(body), int64(len(body))) - if err != nil { - return supportedInstanceTypes, err - } - - // Read all the files from zip archive - for _, zipFile := range zipReader.File { - filePathParts := strings.Split(zipFile.Name, "/") - fileName := filePathParts[len(filePathParts)-1] - if fileName == eksInstanceTypesFile { - unzippedFileBytes, err := readZipFile(zipFile) - if err != nil { - log.Println(err) - continue - } - supportedInstanceTypesFileBody := string(unzippedFileBytes) - for _, line := range strings.Split(strings.Replace(supportedInstanceTypesFileBody, "\r\n", "\n", -1), "\n") { - if !strings.HasPrefix(line, "#") { - instanceType := strings.Split(line, " ")[0] - supportedInstanceTypes = append(supportedInstanceTypes, instanceType) - } - } - } - } - return supportedInstanceTypes, nil -} - -func (e EKS) getLatestAMIVersion() (string, error) { - client := &http.Client{ - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - } - // Get latest version - resp, err := client.Get(fmt.Sprintf("%s/releases/latest", e.AMIRepoURL)) - if err != nil { - return "", err - } - if resp.StatusCode != http.StatusFound { - return "", fmt.Errorf("Can't retrieve latest release from github because redirect was not sent") - } - versionRedirect := resp.Header.Get("location") - pathParts := strings.Split(versionRedirect, "/") - return pathParts[len(pathParts)-1], nil -} - -func readZipFile(zf *zip.File) ([]byte, error) { - f, err := zf.Open() - if err != nil { - return nil, err - } - defer f.Close() - return ioutil.ReadAll(f) -} diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/emr.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/emr.go index 116f7c49113d1..212f16f61ebcd 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/emr.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/emr.go @@ -17,7 +17,7 @@ import ( "fmt" "strings" - "github.com/aws/aws-sdk-go/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/blang/semver/v4" ) @@ -46,8 +46,10 @@ func (e EMR) Filters(version string) (Filters, error) { return filters, err } filters.InstanceTypes = &instanceTypes - filters.RootDeviceType = aws.String("ebs") - filters.VirtualizationType = aws.String("hvm") + ebsType := ec2types.RootDeviceTypeEbs + filters.RootDeviceType = &ebsType + hvmType := ec2types.VirtualizationTypeHvm + filters.VirtualizationType = &hvmType return filters, nil } diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/outputs.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/outputs.go index 468073d2157bc..319cf9fc85e88 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/outputs.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/outputs.go @@ -33,15 +33,15 @@ const columnTag = "column" // of a wide output row type wideColumnsData struct { instanceName string `column:"Instance Type"` - vcpu int64 `column:"VCPUs"` + vcpu int32 `column:"VCPUs"` memory string `column:"Mem (GiB)"` hypervisor string `column:"Hypervisor"` currentGen bool `column:"Current Gen"` hibernationSupport bool `column:"Hibernation Support"` cpuArch string `column:"CPU Arch"` networkPerformance string `column:"Network Performance"` - eni int64 `column:"ENIs"` - gpu int64 `column:"GPUs"` + eni int32 `column:"ENIs"` + gpu int32 `column:"GPUs"` gpuMemory string `column:"GPU Mem (GiB)"` gpuInfo string `column:"GPU Info"` odPrice string `column:"On-Demand Price/Hr"` @@ -52,7 +52,7 @@ type wideColumnsData struct { func SimpleInstanceTypeOutput(instanceTypeInfoSlice []*instancetypes.Details) []string { instanceTypeStrings := []string{} for _, instanceTypeInfo := range instanceTypeInfoSlice { - instanceTypeStrings = append(instanceTypeStrings, *instanceTypeInfo.InstanceType) + instanceTypeStrings = append(instanceTypeStrings, string(instanceTypeInfo.InstanceType)) } return instanceTypeStrings } @@ -97,7 +97,7 @@ func TableOutputShort(instanceTypeInfoSlice []*instancetypes.Details) []string { for _, instanceTypeInfo := range instanceTypeInfoSlice { fmt.Fprintf(w, "\n%s\t%d\t%s\t", - *instanceTypeInfo.InstanceType, + instanceTypeInfo.InstanceType, *instanceTypeInfo.VCpuInfo.DefaultVCpus, formatFloat(float64(*instanceTypeInfo.MemoryInfo.SizeInMiB)/1024.0), ) @@ -161,7 +161,7 @@ func TableOutputWide(instanceTypeInfoSlice []*instancetypes.Details) []string { func OneLineOutput(instanceTypeInfoSlice []*instancetypes.Details) []string { instanceTypeNames := []string{} for _, instanceType := range instanceTypeInfoSlice { - instanceTypeNames = append(instanceTypeNames, *instanceType.InstanceType) + instanceTypeNames = append(instanceTypeNames, string(instanceType.InstanceType)) } if len(instanceTypeNames) == 0 { return []string{} @@ -202,18 +202,14 @@ func getWideColumnsData(instanceTypes []*instancetypes.Details) []*wideColumnsDa for _, instanceType := range instanceTypes { none := "none" - hyperisor := instanceType.Hypervisor - if hyperisor == nil { - hyperisor = &none - } cpuArchitectures := []string{} for _, cpuArch := range instanceType.ProcessorInfo.SupportedArchitectures { - cpuArchitectures = append(cpuArchitectures, *cpuArch) + cpuArchitectures = append(cpuArchitectures, string(cpuArch)) } - gpus := int64(0) - gpuMemory := int64(0) + gpus := int32(0) + gpuMemory := int32(0) gpuType := []string{} if instanceType.GpuInfo != nil { gpuMemory = *instanceType.GpuInfo.TotalGpuMemoryInMiB @@ -235,10 +231,10 @@ func getWideColumnsData(instanceTypes []*instancetypes.Details) []*wideColumnsDa } newColumn := wideColumnsData{ - instanceName: *instanceType.InstanceType, + instanceName: string(instanceType.InstanceType), vcpu: *instanceType.VCpuInfo.DefaultVCpus, memory: formatFloat(float64(*instanceType.MemoryInfo.SizeInMiB) / 1024.0), - hypervisor: *hyperisor, + hypervisor: string(instanceType.Hypervisor), currentGen: *instanceType.CurrentGeneration, hibernationSupport: *instanceType.HibernationSupported, cpuArch: strings.Join(cpuArchitectures, ", "), diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/sortingView.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/sortingView.go index 082a5ed0994d7..671b4d4e49794 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/sortingView.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/sortingView.go @@ -91,8 +91,11 @@ func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list fn := listItemStyle.Render if index == m.Index() { - fn = func(s string) string { - return selectedItemStyle.Render("> " + s) + fn = func(s ...string) string { + t := make([]string, 0, len(s)+1) + t = append(t, "> ") + t = append(t, s...) + return selectedItemStyle.Render(t...) } } diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/tableView.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/tableView.go index 07e52e814492a..9dc05bceeb729 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/tableView.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/tableView.go @@ -393,7 +393,7 @@ func (m tableModel) sortTable(sortFilter string, sortDirection string) (tableMod // get sorted rows from sorted instance types rows := []table.Row{} for _, instance := range instanceTypes { - currRow := rowMap[*instance.InstanceType] + currRow := rowMap[string(instance.InstanceType)] rows = append(rows, currRow) } @@ -431,7 +431,7 @@ func (m tableModel) getInstanceTypeFromRows() ([]*instancetypes.Details, map[str } instanceTypes = append(instanceTypes, currInstance) - rowMap[*currInstance.InstanceType] = row + rowMap[string(currInstance.InstanceType)] = row } return instanceTypes, rowMap diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/verboseView.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/verboseView.go index 721e3a3919ad5..0852f73da45f5 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/verboseView.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs/verboseView.go @@ -19,6 +19,7 @@ import ( "strings" "github.com/aws/amazon-ec2-instance-selector/v2/pkg/instancetypes" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" @@ -38,7 +39,7 @@ type verboseModel struct { viewport viewport.Model // the instance which the verbose output is focused on - focusedInstanceName *string + focusedInstanceName ec2types.InstanceType } // styling for viewport @@ -96,7 +97,7 @@ func (m verboseModel) view() string { outputStr := strings.Builder{} // format header for viewport - instanceName := titleStyle.Render(*m.focusedInstanceName) + instanceName := titleStyle.Render(string(m.focusedInstanceName)) line := strings.Repeat("─", int(math.Max(0, float64(m.viewport.Width-lipgloss.Width(instanceName))))) outputStr.WriteString(lipgloss.JoinHorizontal(lipgloss.Center, instanceName, line)) outputStr.WriteString("\n") diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/selector.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/selector.go index 9c8eb36d441cf..897864c341e1f 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/selector.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/selector.go @@ -28,10 +28,10 @@ import ( "github.com/aws/amazon-ec2-instance-selector/v2/pkg/ec2pricing" "github.com/aws/amazon-ec2-instance-selector/v2/pkg/instancetypes" "github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/outputs" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/service/ec2" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "go.uber.org/multierr" ) @@ -42,9 +42,9 @@ var ( const ( locationFilterKey = "location" - zoneIDLocationType = "availability-zone-id" - zoneNameLocationType = "availability-zone" - regionNameLocationType = "region" + zoneIDLocationType = ec2types.LocationTypeAvailabilityZoneId + zoneNameLocationType = ec2types.LocationTypeAvailabilityZone + regionNameLocationType = ec2types.LocationTypeRegion sdkName = "instance-selector" // Filter Keys @@ -93,37 +93,49 @@ const ( dedicatedHosts = "dedicatedHosts" cpuArchitectureAMD64 = "amd64" - cpuArchitectureX8664 = "x86_64" - virtualizationTypeParaVirtual = "paravirtual" - virtualizationTypePV = "pv" + virtualizationTypePV = "pv" pricePerHour = "pricePerHour" ) // New creates an instance of Selector provided an aws session -func New(sess *session.Session) *Selector { +func New(ctx context.Context, cfg aws.Config) (*Selector, error) { serviceRegistry := NewRegistry() serviceRegistry.RegisterAWSServices() - ec2Client := ec2.New(userAgentWith(sess)) + ec2Client := ec2.NewFromConfig(cfg, func(options *ec2.Options) { + options.APIOptions = append(options.APIOptions, middleware.AddUserAgentKeyValue(sdkName, versionID)) + }) + pricingClient, err := ec2pricing.New(ctx, cfg) + if err != nil { + return nil, err + } + return &Selector{ EC2: ec2Client, - EC2Pricing: ec2pricing.New(sess), - InstanceTypesProvider: instancetypes.LoadFromOrNew("", *sess.Config.Region, 0, ec2Client), + EC2Pricing: pricingClient, + InstanceTypesProvider: instancetypes.LoadFromOrNew("", cfg.Region, 0, ec2Client), ServiceRegistry: serviceRegistry, - } + }, nil } -func NewWithCache(sess *session.Session, ttl time.Duration, cacheDir string) *Selector { +func NewWithCache(ctx context.Context, cfg aws.Config, ttl time.Duration, cacheDir string) (*Selector, error) { serviceRegistry := NewRegistry() serviceRegistry.RegisterAWSServices() - ec2Client := ec2.New(userAgentWith(sess)) + ec2Client := ec2.NewFromConfig(cfg, func(options *ec2.Options) { + options.APIOptions = append(options.APIOptions, middleware.AddUserAgentKeyValue(sdkName, versionID)) + }) + pricingClient, err := ec2pricing.NewWithCache(ctx, cfg, ttl, cacheDir) + if err != nil { + return nil, err + } + return &Selector{ EC2: ec2Client, - EC2Pricing: ec2pricing.NewWithCache(sess, ttl, cacheDir), - InstanceTypesProvider: instancetypes.LoadFromOrNew(cacheDir, *sess.Config.Region, ttl, ec2Client), + EC2Pricing: pricingClient, + InstanceTypesProvider: instancetypes.LoadFromOrNew(cacheDir, cfg.Region, ttl, ec2Client), ServiceRegistry: serviceRegistry, - } + }, nil } func (itf Selector) Save() error { @@ -135,9 +147,9 @@ func (itf Selector) Save() error { // // Deprecated: This function will be replaced with GetFilteredInstanceTypes() and // OutputInstanceTypes() in the next major version. -func (itf Selector) Filter(filters Filters) ([]string, error) { +func (itf Selector) Filter(ctx context.Context, filters Filters) ([]string, error) { outputFn := InstanceTypesOutputFn(outputs.SimpleInstanceTypeOutput) - output, _, err := itf.FilterWithOutput(filters, outputFn) + output, _, err := itf.FilterWithOutput(ctx, filters, outputFn) return output, err } @@ -146,8 +158,8 @@ func (itf Selector) Filter(filters Filters) ([]string, error) { // // Deprecated: This function will be replaced with GetFilteredInstanceTypes() in the next // major version. -func (itf Selector) FilterVerbose(filters Filters) ([]*instancetypes.Details, error) { - instanceTypeInfoSlice, err := itf.rawFilter(filters) +func (itf Selector) FilterVerbose(ctx context.Context, filters Filters) ([]*instancetypes.Details, error) { + instanceTypeInfoSlice, err := itf.rawFilter(ctx, filters) if err != nil { return nil, err } @@ -160,8 +172,8 @@ func (itf Selector) FilterVerbose(filters Filters) ([]*instancetypes.Details, er // // Deprecated: This function will be replaced with GetFilteredInstanceTypes() and // OutputInstanceTypes() in the next major version. -func (itf Selector) FilterWithOutput(filters Filters, outputFn InstanceTypesOutput) ([]string, int, error) { - instanceTypeInfoSlice, err := itf.rawFilter(filters) +func (itf Selector) FilterWithOutput(ctx context.Context, filters Filters, outputFn InstanceTypesOutput) ([]string, int, error) { + instanceTypeInfoSlice, err := itf.rawFilter(ctx, filters) if err != nil { return nil, 0, err } @@ -182,7 +194,7 @@ func (itf Selector) truncateResults(maxResults *int, instanceTypeInfoSlice []*in } // AggregateFilterTransform takes higher level filters which are used to affect multiple raw filters in an opinionated way. -func (itf Selector) AggregateFilterTransform(filters Filters) (Filters, error) { +func (itf Selector) AggregateFilterTransform(ctx context.Context, filters Filters) (Filters, error) { transforms := []FiltersTransform{ TransformFn(itf.TransformBaseInstanceType), TransformFn(itf.TransformFlexible), @@ -190,7 +202,7 @@ func (itf Selector) AggregateFilterTransform(filters Filters) (Filters, error) { } var err error for _, transform := range transforms { - filters, err = transform.Transform(filters) + filters, err = transform.Transform(ctx, filters) if err != nil { return filters, err } @@ -200,18 +212,18 @@ func (itf Selector) AggregateFilterTransform(filters Filters) (Filters, error) { // rawFilter accepts a Filters struct which is used to select the available instance types // matching the criteria within Filters and returns the detailed specs of matching instance types -func (itf Selector) rawFilter(filters Filters) ([]*instancetypes.Details, error) { - filters, err := itf.AggregateFilterTransform(filters) +func (itf Selector) rawFilter(ctx context.Context, filters Filters) ([]*instancetypes.Details, error) { + filters, err := itf.AggregateFilterTransform(ctx, filters) if err != nil { return nil, err } var locations, availabilityZones []string if filters.CPUArchitecture != nil && *filters.CPUArchitecture == cpuArchitectureAMD64 { - *filters.CPUArchitecture = cpuArchitectureX8664 + *filters.CPUArchitecture = ec2types.ArchitectureTypeX8664 } if filters.VirtualizationType != nil && *filters.VirtualizationType == virtualizationTypePV { - *filters.VirtualizationType = virtualizationTypeParaVirtual + *filters.VirtualizationType = ec2types.VirtualizationTypeParavirtual } if filters.AvailabilityZones != nil { availabilityZones = *filters.AvailabilityZones @@ -219,12 +231,12 @@ func (itf Selector) rawFilter(filters Filters) ([]*instancetypes.Details, error) } else if filters.Region != nil { locations = []string{*filters.Region} } - locationInstanceOfferings, err := itf.RetrieveInstanceTypesSupportedInLocations(locations) + locationInstanceOfferings, err := itf.RetrieveInstanceTypesSupportedInLocations(ctx, locations) if err != nil { return nil, err } - instanceTypeDetails, err := itf.InstanceTypesProvider.Get(nil) + instanceTypeDetails, err := itf.InstanceTypesProvider.Get(ctx, nil) if err != nil { return nil, err } @@ -235,7 +247,7 @@ func (itf Selector) rawFilter(filters Filters) ([]*instancetypes.Details, error) wg.Add(1) go func(instanceTypeInfo instancetypes.Details) { defer wg.Done() - it, err := itf.prepareFilter(filters, instanceTypeInfo, availabilityZones, locationInstanceOfferings) + it, err := itf.prepareFilter(ctx, filters, instanceTypeInfo, availabilityZones, locationInstanceOfferings) if err != nil { log.Println(err) } @@ -252,14 +264,14 @@ func (itf Selector) rawFilter(filters Filters) ([]*instancetypes.Details, error) return sortInstanceTypeInfo(filteredInstanceTypes), nil } -func (itf Selector) prepareFilter(filters Filters, instanceTypeInfo instancetypes.Details, availabilityZones []string, locationInstanceOfferings map[string]string) (*instancetypes.Details, error) { - instanceTypeName := *instanceTypeInfo.InstanceType +func (itf Selector) prepareFilter(ctx context.Context, filters Filters, instanceTypeInfo instancetypes.Details, availabilityZones []string, locationInstanceOfferings map[ec2types.InstanceType]string) (*instancetypes.Details, error) { + instanceTypeName := instanceTypeInfo.InstanceType isFpga := instanceTypeInfo.FpgaInfo != nil var instanceTypeHourlyPriceForFilter float64 // Price used to filter based on usage class var instanceTypeHourlyPriceOnDemand, instanceTypeHourlyPriceSpot *float64 // If prices are fetched, populate the fields irrespective of the price filters if itf.EC2Pricing.OnDemandCacheCount() > 0 { - price, err := itf.EC2Pricing.GetOnDemandInstanceTypeCost(instanceTypeName) + price, err := itf.EC2Pricing.GetOnDemandInstanceTypeCost(ctx, instanceTypeName) if err != nil { log.Printf("Could not retrieve instantaneous hourly on-demand price for instance type %s - %s\n", instanceTypeName, err) } else { @@ -267,8 +279,16 @@ func (itf Selector) prepareFilter(filters Filters, instanceTypeInfo instancetype instanceTypeInfo.OndemandPricePerHour = instanceTypeHourlyPriceOnDemand } } - if itf.EC2Pricing.SpotCacheCount() > 0 && contains(instanceTypeInfo.SupportedUsageClasses, "spot") { - price, err := itf.EC2Pricing.GetSpotInstanceTypeNDayAvgCost(instanceTypeName, availabilityZones, 30) + + isSpotUsageClass := false + for _, it := range instanceTypeInfo.SupportedUsageClasses { + if it == ec2types.UsageClassTypeSpot { + isSpotUsageClass = true + } + } + + if itf.EC2Pricing.SpotCacheCount() > 0 && isSpotUsageClass { + price, err := itf.EC2Pricing.GetSpotInstanceTypeNDayAvgCost(ctx, instanceTypeName, availabilityZones, 30) if err != nil { log.Printf("Could not retrieve 30 day avg hourly spot price for instance type %s\n", instanceTypeName) } else { @@ -279,12 +299,14 @@ func (itf Selector) prepareFilter(filters Filters, instanceTypeInfo instancetype if filters.PricePerHour != nil { // If price filter is present, prices should be already fetched // If prices are not fetched, filter should fail and the corresponding error is already printed - if filters.UsageClass != nil && *filters.UsageClass == "spot" && instanceTypeHourlyPriceSpot != nil { + if filters.UsageClass != nil && *filters.UsageClass == ec2types.UsageClassTypeSpot && instanceTypeHourlyPriceSpot != nil { instanceTypeHourlyPriceForFilter = *instanceTypeHourlyPriceSpot } else if instanceTypeHourlyPriceOnDemand != nil { instanceTypeHourlyPriceForFilter = *instanceTypeHourlyPriceOnDemand } } + eneaSupport := string(instanceTypeInfo.NetworkInfo.EnaSupport) + ebsOptimizedSupport := string(instanceTypeInfo.EbsInfo.EbsOptimizedSupport) // filterToInstanceSpecMappingPairs is a map of filter name [key] to filter pair [value]. // A filter pair includes user input filter value and instance spec value retrieved from DescribeInstanceTypes @@ -304,7 +326,7 @@ func (itf Selector) prepareFilter(filters Filters, instanceTypeInfo instancetype baremetal: {filters.BareMetal, instanceTypeInfo.BareMetal}, burstable: {filters.Burstable, instanceTypeInfo.BurstablePerformanceSupported}, fpga: {filters.Fpga, &isFpga}, - enaSupport: {filters.EnaSupport, supportSyntaxToBool(instanceTypeInfo.NetworkInfo.EnaSupport)}, + enaSupport: {filters.EnaSupport, supportSyntaxToBool(&eneaSupport)}, efaSupport: {filters.EfaSupport, instanceTypeInfo.NetworkInfo.EfaSupported}, vcpusToMemoryRatio: {filters.VCpusToMemoryRatio, calculateVCpusToMemoryRatio(instanceTypeInfo.VCpuInfo.DefaultVCpus, instanceTypeInfo.MemoryInfo.SizeInMiB)}, currentGeneration: {filters.CurrentGeneration, instanceTypeInfo.CurrentGeneration}, @@ -318,7 +340,7 @@ func (itf Selector) prepareFilter(filters Filters, instanceTypeInfo instancetype instanceStorageRange: {filters.InstanceStorageRange, getInstanceStorage(instanceTypeInfo.InstanceStorageInfo)}, diskType: {filters.DiskType, getDiskType(instanceTypeInfo.InstanceStorageInfo)}, nvme: {filters.NVME, getNVMESupport(instanceTypeInfo.InstanceStorageInfo, instanceTypeInfo.EbsInfo)}, - ebsOptimized: {filters.EBSOptimized, supportSyntaxToBool(instanceTypeInfo.EbsInfo.EbsOptimizedSupport)}, + ebsOptimized: {filters.EBSOptimized, supportSyntaxToBool(&ebsOptimizedSupport)}, diskEncryption: {filters.DiskEncryption, getDiskEncryptionSupport(instanceTypeInfo.InstanceStorageInfo, instanceTypeInfo.EbsInfo)}, ebsOptimizedBaselineBandwidth: {filters.EBSOptimizedBaselineBandwidth, getEBSOptimizedBaselineBandwidth(instanceTypeInfo.EbsInfo)}, ebsOptimizedBaselineThroughput: {filters.EBSOptimizedBaselineThroughput, getEBSOptimizedBaselineThroughput(instanceTypeInfo.EbsInfo)}, @@ -341,7 +363,7 @@ func (itf Selector) prepareFilter(filters Filters, instanceTypeInfo instancetype } var isInstanceSupported bool - isInstanceSupported, err := itf.executeFilters(filterToInstanceSpecMappingPairs, instanceTypeName) + isInstanceSupported, err := itf.executeFilters(ctx, filterToInstanceSpecMappingPairs, instanceTypeName) if err != nil { return nil, err } @@ -359,17 +381,17 @@ func sortInstanceTypeInfo(instanceTypeInfoSlice []*instancetypes.Details) []*ins sort.Slice(instanceTypeInfoSlice, func(i, j int) bool { iInstanceInfo := instanceTypeInfoSlice[i] jInstanceInfo := instanceTypeInfoSlice[j] - return strings.Compare(aws.StringValue(iInstanceInfo.InstanceType), aws.StringValue(jInstanceInfo.InstanceType)) <= 0 + return strings.Compare(string(iInstanceInfo.InstanceType), string(jInstanceInfo.InstanceType)) <= 0 }) return instanceTypeInfoSlice } // executeFilters accepts a mapping of filter name to filter pairs which are iterated through // to determine if the instance type matches the filter values. -func (itf Selector) executeFilters(filterToInstanceSpecMapping map[string]filterPair, instanceType string) (bool, error) { - verdict := make(chan bool, len(filterToInstanceSpecMapping) + 1) +func (itf Selector) executeFilters(ctx context.Context, filterToInstanceSpecMapping map[string]filterPair, instanceType ec2types.InstanceType) (bool, error) { + verdict := make(chan bool, len(filterToInstanceSpecMapping)+1) errs := make(chan error) - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(ctx) defer cancel() var wg sync.WaitGroup for filterName, filter := range filterToInstanceSpecMapping { @@ -410,15 +432,16 @@ func (itf Selector) executeFilters(filterToInstanceSpecMapping map[string]filter } } -func exec(instanceType string, filterName string, filter filterPair) (bool, error) { +func exec(instanceType ec2types.InstanceType, filterName string, filter filterPair) (bool, error) { filterVal := filter.filterValue instanceSpec := filter.instanceSpec + filterValReflection := reflect.ValueOf(filterVal) // if filter is nil, user did not specify a filter, so skip evaluation - if reflect.ValueOf(filterVal).IsNil() { + if filterValReflection.IsNil() { return true, nil } instanceSpecType := reflect.ValueOf(instanceSpec).Type() - filterType := reflect.ValueOf(filterVal).Type() + filterType := filterValReflection.Type() filterDetailsMsg := fmt.Sprintf("filter (%s: %s => %s) corresponding to instance spec (%s => %s) for instance type %s", filterName, filterVal, filterType, instanceSpec, instanceSpecType, instanceType) invalidInstanceSpecTypeMsg := fmt.Sprintf("Unable to process for %s", filterDetailsMsg) @@ -459,6 +482,15 @@ func exec(instanceType string, filterName string, filter filterPair) (bool, erro default: return false, fmt.Errorf(invalidInstanceSpecTypeMsg) } + case *Int32RangeFilter: + switch iSpec := instanceSpec.(type) { + case *int32: + if !isSupportedWithRangeInt32(iSpec, filter) { + return false, nil + } + default: + return false, fmt.Errorf(invalidInstanceSpecTypeMsg) + } case *Float64RangeFilter: switch iSpec := instanceSpec.(type) { case *float64: @@ -507,6 +539,60 @@ func exec(instanceType string, filterName string, filter filterPair) (bool, erro default: return false, fmt.Errorf(invalidInstanceSpecTypeMsg) } + case *ec2types.ArchitectureType: + switch iSpec := instanceSpec.(type) { + case []ec2types.ArchitectureType: + if !isSupportedArchitectureType(iSpec, filter) { + return false, nil + } + default: + return false, fmt.Errorf(invalidInstanceSpecTypeMsg) + } + case *ec2types.UsageClassType: + switch iSpec := instanceSpec.(type) { + case []ec2types.UsageClassType: + if !isSupportedUsageClassType(iSpec, filter) { + return false, nil + } + default: + return false, fmt.Errorf(invalidInstanceSpecTypeMsg) + } + case *CPUManufacturer: + switch iSpec := instanceSpec.(type) { + case CPUManufacturer: + if !isMatchingCpuArchitecture(iSpec, filter) { + return false, nil + } + default: + return false, fmt.Errorf(invalidInstanceSpecTypeMsg) + } + case *ec2types.VirtualizationType: + switch iSpec := instanceSpec.(type) { + case []ec2types.VirtualizationType: + if !isSupportedVirtualizationType(iSpec, filter) { + return false, nil + } + default: + return false, fmt.Errorf(invalidInstanceSpecTypeMsg) + } + case *ec2types.InstanceTypeHypervisor: + switch iSpec := instanceSpec.(type) { + case ec2types.InstanceTypeHypervisor: + if !isSupportedInstanceTypeHypervisorType(iSpec, filter) { + return false, nil + } + default: + return false, fmt.Errorf(invalidInstanceSpecTypeMsg) + } + case *ec2types.RootDeviceType: + switch iSpec := instanceSpec.(type) { + case []ec2types.RootDeviceType: + if !isSupportedRootDeviceType(iSpec, filter) { + return false, nil + } + default: + return false, fmt.Errorf(invalidInstanceSpecTypeMsg) + } case *[]string: switch iSpec := instanceSpec.(type) { case *string: @@ -532,41 +618,45 @@ func exec(instanceType string, filterName string, filter filterPair) (bool, erro // RetrieveInstanceTypesSupportedInLocations returns a map of instance type -> AZ or Region for all instance types supported in the intersected locations passed in // The location can be a zone-id (ie. use1-az1), a zone-name (us-east-1a), or a region name (us-east-1). // Note that zone names are not necessarily the same across accounts -func (itf Selector) RetrieveInstanceTypesSupportedInLocations(locations []string) (map[string]string, error) { +func (itf Selector) RetrieveInstanceTypesSupportedInLocations(ctx context.Context, locations []string) (map[ec2types.InstanceType]string, error) { if len(locations) == 0 { return nil, nil } - availableInstanceTypes := map[string]int{} + availableInstanceTypes := map[ec2types.InstanceType]int{} for _, location := range locations { + locationType, err := itf.getLocationType(ctx, location) + if err != nil { + return nil, err + } + instanceTypeOfferingsInput := &ec2.DescribeInstanceTypeOfferingsInput{ - Filters: []*ec2.Filter{ + LocationType: locationType, + Filters: []ec2types.Filter{ { Name: aws.String(locationFilterKey), - Values: []*string{aws.String(location)}, + Values: []string{location}, }, }, } - locationType, err := itf.getLocationType(location) - if err != nil { - return nil, err - } - instanceTypeOfferingsInput.SetLocationType(locationType) - err = itf.EC2.DescribeInstanceTypeOfferingsPages(instanceTypeOfferingsInput, func(page *ec2.DescribeInstanceTypeOfferingsOutput, lastPage bool) bool { - for _, instanceType := range page.InstanceTypeOfferings { - if i, ok := availableInstanceTypes[*instanceType.InstanceType]; !ok { - availableInstanceTypes[*instanceType.InstanceType] = 1 + p := ec2.NewDescribeInstanceTypeOfferingsPaginator(itf.EC2, instanceTypeOfferingsInput) + + for p.HasMorePages() { + instanceTypeOfferings, err := p.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("Encountered an error when describing instance type offerings: %w", err) + } + + for _, instanceType := range instanceTypeOfferings.InstanceTypeOfferings { + if i, ok := availableInstanceTypes[instanceType.InstanceType]; !ok { + availableInstanceTypes[instanceType.InstanceType] = 1 } else { - availableInstanceTypes[*instanceType.InstanceType] = i + 1 + availableInstanceTypes[instanceType.InstanceType] = i + 1 } } - return true - }) - if err != nil { - return nil, fmt.Errorf("Encountered an error when describing instance type offerings: %w", err) } } - availableInstanceTypesAllLocations := map[string]string{} + availableInstanceTypesAllLocations := map[ec2types.InstanceType]string{} for instanceType, locationsSupported := range availableInstanceTypes { if locationsSupported == len(locations) { availableInstanceTypesAllLocations[instanceType] = "" @@ -576,8 +666,8 @@ func (itf Selector) RetrieveInstanceTypesSupportedInLocations(locations []string return availableInstanceTypesAllLocations, nil } -func (itf Selector) getLocationType(location string) (string, error) { - azs, err := itf.EC2.DescribeAvailabilityZones(&ec2.DescribeAvailabilityZonesInput{}) +func (itf Selector) getLocationType(ctx context.Context, location string) (ec2types.LocationType, error) { + azs, err := itf.EC2.DescribeAvailabilityZones(ctx, &ec2.DescribeAvailabilityZonesInput{}) if err != nil { return "", err } @@ -593,7 +683,7 @@ func (itf Selector) getLocationType(location string) (string, error) { return "", fmt.Errorf("The location passed in (%s) is not a valid zone-id, zone-name, or region name", location) } -func isSupportedInLocation(instanceOfferings map[string]string, instanceType string) bool { +func isSupportedInLocation(instanceOfferings map[ec2types.InstanceType]string, instanceType ec2types.InstanceType) bool { if instanceOfferings == nil { return true } @@ -601,22 +691,16 @@ func isSupportedInLocation(instanceOfferings map[string]string, instanceType str return ok } -func isInDenyList(denyRegex *regexp.Regexp, instanceTypeName string) bool { +func isInDenyList(denyRegex *regexp.Regexp, instanceTypeName ec2types.InstanceType) bool { if denyRegex == nil { return false } - return denyRegex.MatchString(instanceTypeName) + return denyRegex.MatchString(string(instanceTypeName)) } -func isInAllowList(allowRegex *regexp.Regexp, instanceTypeName string) bool { +func isInAllowList(allowRegex *regexp.Regexp, instanceTypeName ec2types.InstanceType) bool { if allowRegex == nil { return true } - return allowRegex.MatchString(instanceTypeName) -} - -func userAgentWith(sess *session.Session) *session.Session { - userAgentHandler := request.MakeAddToUserAgentFreeFormHandler(fmt.Sprintf("%s-%s", sdkName, versionID)) - sess.Handlers.Build.PushBack(userAgentHandler) - return sess + return allowRegex.MatchString(string(instanceTypeName)) } diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/services.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/services.go index b1ddf8af153b7..b46495c0ff767 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/services.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/services.go @@ -59,14 +59,13 @@ func (sr *ServiceRegistry) Register(name string, service Service) { // RegisterAWSServices registers the built-in AWS service filter transforms func (sr *ServiceRegistry) RegisterAWSServices() { - sr.Register("eks", &EKS{}) sr.Register("emr", &EMR{}) } // ExecuteTransforms will execute the ServiceRegistry's registered service filter transforms // Filters.Service will be parsed as - and passed to Service.Filters func (sr *ServiceRegistry) ExecuteTransforms(filters Filters) (Filters, error) { - if filters.Service == nil || *filters.Service == "" { + if filters.Service == nil || *filters.Service == "" || *filters.Service == "eks" { return filters, nil } serviceAndVersion := strings.ToLower(*filters.Service) diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/types.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/types.go index 95a634d6705b3..60395ebcbce3e 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/types.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector/types.go @@ -15,12 +15,13 @@ package selector import ( "encoding/json" + "github.com/aws/amazon-ec2-instance-selector/v2/pkg/awsapi" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "regexp" "github.com/aws/amazon-ec2-instance-selector/v2/pkg/bytequantity" "github.com/aws/amazon-ec2-instance-selector/v2/pkg/ec2pricing" "github.com/aws/amazon-ec2-instance-selector/v2/pkg/instancetypes" - "github.com/aws/aws-sdk-go/service/ec2/ec2iface" ) // InstanceTypesOutput can be implemented to provide custom output to instance type results @@ -39,7 +40,7 @@ func (fn InstanceTypesOutputFn) Output(instanceTypes []*instancetypes.Details) [ // Selector is used to filter instance type resource specs type Selector struct { - EC2 ec2iface.EC2API + EC2 awsapi.SelectorInterface EC2Pricing ec2pricing.EC2PricingIface InstanceTypesProvider *instancetypes.Provider ServiceRegistry ServiceRegistry @@ -52,6 +53,13 @@ type IntRangeFilter struct { LowerBound int } +// Int32RangeFilter holds an upper and lower bound int +// The lower and upper bound are used to range filter resource specs +type Int32RangeFilter struct { + UpperBound int32 + LowerBound int32 +} + // Uint64RangeFilter holds an upper and lower bound uint64 // The lower and upper bound are used to range filter resource specs type Uint64RangeFilter struct { @@ -122,11 +130,10 @@ type Filters struct { FreeTier *bool // CPUArchitecture of the EC2 instance type - // Possible values are: x86_64/amd64 or arm64 - CPUArchitecture *string + CPUArchitecture *ec2types.ArchitectureType // CPUManufacturer is used to filter instance types with a specific CPU manufacturer - CPUManufacturer *string + CPUManufacturer *CPUManufacturer // CurrentGeneration returns the latest generation of instance types CurrentGeneration *bool @@ -141,7 +148,7 @@ type Filters struct { Fpga *bool // GpusRange filter is a range of acceptable GPU count available to an EC2 instance type - GpusRange *IntRangeFilter + GpusRange *Int32RangeFilter // GpuMemoryRange filter is a range of acceptable GPU memory in Gibibytes (GiB) available to an EC2 instance type in aggreagte across all GPUs. GpuMemoryRange *ByteQuantityRangeFilter @@ -167,7 +174,7 @@ type Filters struct { // Hypervisor is used to return only a specific hypervisor backed instance type // Possibly values are: xen or nitro - Hypervisor *string + Hypervisor *ec2types.InstanceTypeHypervisor // MaxResults is the maximum number of instance types to return that match the filter criteria MaxResults *int @@ -176,7 +183,7 @@ type Filters struct { MemoryRange *ByteQuantityRangeFilter // NetworkInterfaces filter is a range of the number of ENI attachments an instance type can support - NetworkInterfaces *IntRangeFilter + NetworkInterfaces *Int32RangeFilter // NetworkPerformance filter is a range of network bandwidth an instance type can support NetworkPerformance *IntRangeFilter @@ -199,14 +206,14 @@ type Filters struct { // RootDeviceType is the backing device of the root storage volume // Possible values are: instance-store or ebs - RootDeviceType *string + RootDeviceType *ec2types.RootDeviceType // UsageClass of the instance EC2 instance type // Possible values are: spot or on-demand - UsageClass *string + UsageClass *ec2types.UsageClassType // VCpusRange filter is a range of acceptable VCpus for the instance type - VCpusRange *IntRangeFilter + VCpusRange *Int32RangeFilter // VcpusToMemoryRatio is a ratio of vcpus to memory expressed as a floating point VCpusToMemoryRatio *float64 @@ -232,7 +239,7 @@ type Filters struct { InstanceTypes *[]string // VirtualizationType is used to return instance types that match either hvm or pv virtualization types - VirtualizationType *string + VirtualizationType *ec2types.VirtualizationType // PricePerHour is used to return instance types that are equal to or cheaper than the specified price PricePerHour *Float64RangeFilter @@ -265,3 +272,33 @@ type Filters struct { // DedicatedHosts filters on instance types that support dedicated hosts tenancy DedicatedHosts *bool } + +type CPUManufacturer string + +// Enum values for CPUManufacturer +const ( + CPUManufacturerAWS CPUManufacturer = "aws" + CPUManufacturerAMD CPUManufacturer = "amd" + CPUManufacturerIntel CPUManufacturer = "intel" +) + +// Values returns all known values for CPUManufacturer. Note that this can be +// expanded in the future, and so it is only as up to date as the client. The +// ordering of this slice is not guaranteed to be stable across updates. +func (CPUManufacturer) Values() []CPUManufacturer { + return []CPUManufacturer{ + "aws", + "amd", + "intel", + } +} + +// ArchitectureTypeAMD64 is a legacy type we support for b/c that isn't in the API +const ( + ArchitectureTypeAMD64 ec2types.ArchitectureType = "amd64" +) + +// ArchitectureTypeAMD64 is a legacy type we support for b/c that isn't in the API +const ( + VirtualizationTypePv ec2types.VirtualizationType = "pv" +) diff --git a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/sorter/sorter.go b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/sorter/sorter.go index 829a74ca53fda..311145b0d9f62 100644 --- a/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/sorter/sorter.go +++ b/vendor/github.com/aws/amazon-ec2-instance-selector/v2/pkg/sorter/sorter.go @@ -21,7 +21,6 @@ import ( "strings" "github.com/aws/amazon-ec2-instance-selector/v2/pkg/instancetypes" - "github.com/aws/aws-sdk-go/aws" "github.com/oliveagle/jsonpath" ) @@ -349,34 +348,34 @@ func (s *sorter) instanceTypes() []*instancetypes.Details { // helper functions for special sorting fields // getTotalGpusCount calculates the number of gpus in the given instance type -func getTotalGpusCount(instanceType *instancetypes.Details) *int64 { +func getTotalGpusCount(instanceType *instancetypes.Details) *int32 { gpusInfo := instanceType.GpuInfo if gpusInfo == nil { return nil } - total := aws.Int64(0) + total := int32(0) for _, gpu := range gpusInfo.Gpus { - total = aws.Int64(*total + *gpu.Count) + total = total + *gpu.Count } - return total + return &total } // getTotalAcceleratorsCount calculates the total number of inference accelerators // in the given instance type -func getTotalAcceleratorsCount(instanceType *instancetypes.Details) *int64 { +func getTotalAcceleratorsCount(instanceType *instancetypes.Details) *int32 { acceleratorInfo := instanceType.InferenceAcceleratorInfo if acceleratorInfo == nil { return nil } - total := aws.Int64(0) + total := int32(0) for _, accel := range acceleratorInfo.Accelerators { - total = aws.Int64(*total + *accel.Count) + total = total + *accel.Count } - return total + return &total } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/CHANGELOG.md new file mode 100644 index 0000000000000..3519b3764470d --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/CHANGELOG.md @@ -0,0 +1,287 @@ +# v1.21.6 (2023-08-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.21.5 (2023-08-18) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.21.4 (2023-08-17) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.21.3 (2023-08-15) + +* No change notes available for this release. + +# v1.21.2 (2023-08-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.21.1 (2023-08-01) + +* No change notes available for this release. + +# v1.21.0 (2023-07-31) + +* **Feature**: Adds support for smithy-modeled endpoint resolution. A new rules-based endpoint resolution will be added to the SDK which will supercede and deprecate existing endpoint resolution. Specifically, EndpointResolver will be deprecated while BaseEndpoint and EndpointResolverV2 will take its place. For more information, please see the Endpoints section in our Developer Guide. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.20.2 (2023-07-28) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.20.1 (2023-07-13) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.20.0 (2023-06-19) + +* **Feature**: This release updates the PriceListArn regex pattern. + +# v1.19.8 (2023-06-15) + +* No change notes available for this release. + +# v1.19.7 (2023-06-13) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.6 (2023-05-04) + +* No change notes available for this release. + +# v1.19.5 (2023-04-24) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.4 (2023-04-10) + +* No change notes available for this release. + +# v1.19.3 (2023-04-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.2 (2023-03-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.1 (2023-03-10) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.0 (2023-03-01) + +* **Feature**: This release adds 2 new APIs - ListPriceLists which returns a list of applicable price lists, and GetPriceListFileUrl which outputs a URL to retrieve your price lists from the generated file from ListPriceLists + +# v1.18.4 (2023-02-22) + +* **Bug Fix**: Prevent nil pointer dereference when retrieving error codes. + +# v1.18.3 (2023-02-20) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.18.2 (2023-02-15) + +* **Announcement**: When receiving an error response in restJson-based services, an incorrect error type may have been returned based on the content of the response. This has been fixed via PR #2012 tracked in issue #1910. +* **Bug Fix**: Correct error type parsing for restJson services. + +# v1.18.1 (2023-02-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.18.0 (2023-01-05) + +* **Feature**: Add `ErrorCodeOverride` field to all error structs (aws/smithy-go#401). + +# v1.17.5 (2022-12-15) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.4 (2022-12-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.3 (2022-10-24) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.2 (2022-10-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.1 (2022-09-20) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.17.0 (2022-09-14) + +* **Feature**: Fixed a bug in the API client generation which caused some operation parameters to be incorrectly generated as value types instead of pointer types. The service API always required these affected parameters to be nilable. This fixes the SDK client to match the expectations of the the service API. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.16.8 (2022-09-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.16.7 (2022-08-31) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.16.6 (2022-08-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.16.5 (2022-08-11) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.16.4 (2022-08-09) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.16.3 (2022-08-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.16.2 (2022-08-01) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.16.1 (2022-07-05) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.16.0 (2022-06-30) + +* **Feature**: Documentation update for GetProducts Response. + +# v1.15.1 (2022-06-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.15.0 (2022-06-22) + +* **Feature**: This release introduces 1 update to the GetProducts API. The serviceCode attribute is now required when you use the GetProductsRequest. + +# v1.14.7 (2022-06-07) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.6 (2022-05-17) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.5 (2022-04-26) + +* **Documentation**: Documentation updates for Price List API + +# v1.14.4 (2022-04-25) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.3 (2022-03-30) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.2 (2022-03-24) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.1 (2022-03-23) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.14.0 (2022-03-08) + +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.13.0 (2022-02-24) + +* **Feature**: API client updated +* **Feature**: Adds RetryMaxAttempts and RetryMod to API client Options. This allows the API clients' default Retryer to be configured from the shared configuration files or environment variables. Adding a new Retry mode of `Adaptive`. `Adaptive` retry mode is an experimental mode, adding client rate limiting when throttles reponses are received from an API. See [retry.AdaptiveMode](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws/retry#AdaptiveMode) for more details, and configuration options. +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.12.0 (2022-01-14) + +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.11.0 (2022-01-07) + +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.10.0 (2021-12-21) + +* **Feature**: API Paginators now support specifying the initial starting token, and support stopping on empty string tokens. + +# v1.9.2 (2021-12-02) + +* **Bug Fix**: Fixes a bug that prevented aws.EndpointResolverWithOptions from being used by the service client. ([#1514](https://github.com/aws/aws-sdk-go-v2/pull/1514)) +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.9.1 (2021-11-19) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.9.0 (2021-11-12) + +* **Feature**: Service clients now support custom endpoints that have an initial URI path defined. + +# v1.8.0 (2021-11-06) + +* **Feature**: The SDK now supports configuration of FIPS and DualStack endpoints using environment variables, shared configuration, or programmatically. +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.7.0 (2021-10-21) + +* **Feature**: Updated to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.6.2 (2021-10-11) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.6.1 (2021-09-17) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.6.0 (2021-08-27) + +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.5.3 (2021-08-19) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.5.2 (2021-08-04) + +* **Dependency Update**: Updated `github.com/aws/smithy-go` to latest version. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.5.1 (2021-07-15) + +* **Documentation**: Updated service model to latest revision. +* **Dependency Update**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.5.0 (2021-06-25) + +* **Feature**: Updated `github.com/aws/smithy-go` to latest version +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.1 (2021-05-20) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.0 (2021-05-14) + +* **Feature**: Constant has been added to modules to enable runtime version inspection for reporting. +* **Dependency Update**: Updated to the latest SDK module versions + diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/LICENSE.txt b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_client.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_client.go new file mode 100644 index 0000000000000..c87fe28d3a487 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_client.go @@ -0,0 +1,526 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package pricing + +import ( + "context" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/defaults" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/aws/retry" + "github.com/aws/aws-sdk-go-v2/aws/signer/v4" + awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http" + internalConfig "github.com/aws/aws-sdk-go-v2/internal/configsources" + smithy "github.com/aws/smithy-go" + smithydocument "github.com/aws/smithy-go/document" + "github.com/aws/smithy-go/logging" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" + "net" + "net/http" + "time" +) + +const ServiceID = "Pricing" +const ServiceAPIVersion = "2017-10-15" + +// Client provides the API client to make operations call for AWS Price List +// Service. +type Client struct { + options Options +} + +// New returns an initialized Client based on the functional options. Provide +// additional functional options to further configure the behavior of the client, +// such as changing the client's endpoint or adding custom middleware behavior. +func New(options Options, optFns ...func(*Options)) *Client { + options = options.Copy() + + resolveDefaultLogger(&options) + + setResolvedDefaultsMode(&options) + + resolveRetryer(&options) + + resolveHTTPClient(&options) + + resolveHTTPSignerV4(&options) + + for _, fn := range optFns { + fn(&options) + } + + client := &Client{ + options: options, + } + + return client +} + +type Options struct { + // Set of options to modify how an operation is invoked. These apply to all + // operations invoked for this client. Use functional options on operation call to + // modify this list for per operation behavior. + APIOptions []func(*middleware.Stack) error + + // The optional application specific identifier appended to the User-Agent header. + AppID string + + // This endpoint will be given as input to an EndpointResolverV2. It is used for + // providing a custom base endpoint that is subject to modifications by the + // processing EndpointResolverV2. + BaseEndpoint *string + + // Configures the events that will be sent to the configured logger. + ClientLogMode aws.ClientLogMode + + // The credentials object to use when signing requests. + Credentials aws.CredentialsProvider + + // The configuration DefaultsMode that the SDK should use when constructing the + // clients initial default settings. + DefaultsMode aws.DefaultsMode + + // The endpoint options to be used when attempting to resolve an endpoint. + EndpointOptions EndpointResolverOptions + + // The service endpoint resolver. + // + // Deprecated: Deprecated: EndpointResolver and WithEndpointResolver. Providing a + // value for this field will likely prevent you from using any endpoint-related + // service features released after the introduction of EndpointResolverV2 and + // BaseEndpoint. To migrate an EndpointResolver implementation that uses a custom + // endpoint, set the client option BaseEndpoint instead. + EndpointResolver EndpointResolver + + // Resolves the endpoint used for a particular service. This should be used over + // the deprecated EndpointResolver + EndpointResolverV2 EndpointResolverV2 + + // Signature Version 4 (SigV4) Signer + HTTPSignerV4 HTTPSignerV4 + + // The logger writer interface to write logging messages to. + Logger logging.Logger + + // The region to send requests to. (Required) + Region string + + // RetryMaxAttempts specifies the maximum number attempts an API client will call + // an operation that fails with a retryable error. A value of 0 is ignored, and + // will not be used to configure the API client created default retryer, or modify + // per operation call's retry max attempts. When creating a new API Clients this + // member will only be used if the Retryer Options member is nil. This value will + // be ignored if Retryer is not nil. If specified in an operation call's functional + // options with a value that is different than the constructed client's Options, + // the Client's Retryer will be wrapped to use the operation's specific + // RetryMaxAttempts value. + RetryMaxAttempts int + + // RetryMode specifies the retry mode the API client will be created with, if + // Retryer option is not also specified. When creating a new API Clients this + // member will only be used if the Retryer Options member is nil. This value will + // be ignored if Retryer is not nil. Currently does not support per operation call + // overrides, may in the future. + RetryMode aws.RetryMode + + // Retryer guides how HTTP requests should be retried in case of recoverable + // failures. When nil the API client will use a default retryer. The kind of + // default retry created by the API client can be changed with the RetryMode + // option. + Retryer aws.Retryer + + // The RuntimeEnvironment configuration, only populated if the DefaultsMode is set + // to DefaultsModeAuto and is initialized using config.LoadDefaultConfig . You + // should not populate this structure programmatically, or rely on the values here + // within your applications. + RuntimeEnvironment aws.RuntimeEnvironment + + // The initial DefaultsMode used when the client options were constructed. If the + // DefaultsMode was set to aws.DefaultsModeAuto this will store what the resolved + // value was at that point in time. Currently does not support per operation call + // overrides, may in the future. + resolvedDefaultsMode aws.DefaultsMode + + // The HTTP client to invoke API calls with. Defaults to client's default HTTP + // implementation if nil. + HTTPClient HTTPClient +} + +// WithAPIOptions returns a functional option for setting the Client's APIOptions +// option. +func WithAPIOptions(optFns ...func(*middleware.Stack) error) func(*Options) { + return func(o *Options) { + o.APIOptions = append(o.APIOptions, optFns...) + } +} + +// Deprecated: EndpointResolver and WithEndpointResolver. Providing a value for +// this field will likely prevent you from using any endpoint-related service +// features released after the introduction of EndpointResolverV2 and BaseEndpoint. +// To migrate an EndpointResolver implementation that uses a custom endpoint, set +// the client option BaseEndpoint instead. +func WithEndpointResolver(v EndpointResolver) func(*Options) { + return func(o *Options) { + o.EndpointResolver = v + } +} + +// WithEndpointResolverV2 returns a functional option for setting the Client's +// EndpointResolverV2 option. +func WithEndpointResolverV2(v EndpointResolverV2) func(*Options) { + return func(o *Options) { + o.EndpointResolverV2 = v + } +} + +type HTTPClient interface { + Do(*http.Request) (*http.Response, error) +} + +// Copy creates a clone where the APIOptions list is deep copied. +func (o Options) Copy() Options { + to := o + to.APIOptions = make([]func(*middleware.Stack) error, len(o.APIOptions)) + copy(to.APIOptions, o.APIOptions) + + return to +} +func (c *Client) invokeOperation(ctx context.Context, opID string, params interface{}, optFns []func(*Options), stackFns ...func(*middleware.Stack, Options) error) (result interface{}, metadata middleware.Metadata, err error) { + ctx = middleware.ClearStackValues(ctx) + stack := middleware.NewStack(opID, smithyhttp.NewStackRequest) + options := c.options.Copy() + resolveEndpointResolverV2(&options) + + for _, fn := range optFns { + fn(&options) + } + + finalizeRetryMaxAttemptOptions(&options, *c) + + finalizeClientEndpointResolverOptions(&options) + + for _, fn := range stackFns { + if err := fn(stack, options); err != nil { + return nil, metadata, err + } + } + + for _, fn := range options.APIOptions { + if err := fn(stack); err != nil { + return nil, metadata, err + } + } + + handler := middleware.DecorateHandler(smithyhttp.NewClientHandler(options.HTTPClient), stack) + result, metadata, err = handler.Handle(ctx, params) + if err != nil { + err = &smithy.OperationError{ + ServiceID: ServiceID, + OperationName: opID, + Err: err, + } + } + return result, metadata, err +} + +type noSmithyDocumentSerde = smithydocument.NoSerde + +type legacyEndpointContextSetter struct { + LegacyResolver EndpointResolver +} + +func (*legacyEndpointContextSetter) ID() string { + return "legacyEndpointContextSetter" +} + +func (m *legacyEndpointContextSetter) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + if m.LegacyResolver != nil { + ctx = awsmiddleware.SetRequiresLegacyEndpoints(ctx, true) + } + + return next.HandleInitialize(ctx, in) + +} +func addlegacyEndpointContextSetter(stack *middleware.Stack, o Options) error { + return stack.Initialize.Add(&legacyEndpointContextSetter{ + LegacyResolver: o.EndpointResolver, + }, middleware.Before) +} + +func resolveDefaultLogger(o *Options) { + if o.Logger != nil { + return + } + o.Logger = logging.Nop{} +} + +func addSetLoggerMiddleware(stack *middleware.Stack, o Options) error { + return middleware.AddSetLoggerMiddleware(stack, o.Logger) +} + +func setResolvedDefaultsMode(o *Options) { + if len(o.resolvedDefaultsMode) > 0 { + return + } + + var mode aws.DefaultsMode + mode.SetFromString(string(o.DefaultsMode)) + + if mode == aws.DefaultsModeAuto { + mode = defaults.ResolveDefaultsModeAuto(o.Region, o.RuntimeEnvironment) + } + + o.resolvedDefaultsMode = mode +} + +// NewFromConfig returns a new client from the provided config. +func NewFromConfig(cfg aws.Config, optFns ...func(*Options)) *Client { + opts := Options{ + Region: cfg.Region, + DefaultsMode: cfg.DefaultsMode, + RuntimeEnvironment: cfg.RuntimeEnvironment, + HTTPClient: cfg.HTTPClient, + Credentials: cfg.Credentials, + APIOptions: cfg.APIOptions, + Logger: cfg.Logger, + ClientLogMode: cfg.ClientLogMode, + AppID: cfg.AppID, + } + resolveAWSRetryerProvider(cfg, &opts) + resolveAWSRetryMaxAttempts(cfg, &opts) + resolveAWSRetryMode(cfg, &opts) + resolveAWSEndpointResolver(cfg, &opts) + resolveUseDualStackEndpoint(cfg, &opts) + resolveUseFIPSEndpoint(cfg, &opts) + return New(opts, optFns...) +} + +func resolveHTTPClient(o *Options) { + var buildable *awshttp.BuildableClient + + if o.HTTPClient != nil { + var ok bool + buildable, ok = o.HTTPClient.(*awshttp.BuildableClient) + if !ok { + return + } + } else { + buildable = awshttp.NewBuildableClient() + } + + modeConfig, err := defaults.GetModeConfiguration(o.resolvedDefaultsMode) + if err == nil { + buildable = buildable.WithDialerOptions(func(dialer *net.Dialer) { + if dialerTimeout, ok := modeConfig.GetConnectTimeout(); ok { + dialer.Timeout = dialerTimeout + } + }) + + buildable = buildable.WithTransportOptions(func(transport *http.Transport) { + if tlsHandshakeTimeout, ok := modeConfig.GetTLSNegotiationTimeout(); ok { + transport.TLSHandshakeTimeout = tlsHandshakeTimeout + } + }) + } + + o.HTTPClient = buildable +} + +func resolveRetryer(o *Options) { + if o.Retryer != nil { + return + } + + if len(o.RetryMode) == 0 { + modeConfig, err := defaults.GetModeConfiguration(o.resolvedDefaultsMode) + if err == nil { + o.RetryMode = modeConfig.RetryMode + } + } + if len(o.RetryMode) == 0 { + o.RetryMode = aws.RetryModeStandard + } + + var standardOptions []func(*retry.StandardOptions) + if v := o.RetryMaxAttempts; v != 0 { + standardOptions = append(standardOptions, func(so *retry.StandardOptions) { + so.MaxAttempts = v + }) + } + + switch o.RetryMode { + case aws.RetryModeAdaptive: + var adaptiveOptions []func(*retry.AdaptiveModeOptions) + if len(standardOptions) != 0 { + adaptiveOptions = append(adaptiveOptions, func(ao *retry.AdaptiveModeOptions) { + ao.StandardOptions = append(ao.StandardOptions, standardOptions...) + }) + } + o.Retryer = retry.NewAdaptiveMode(adaptiveOptions...) + + default: + o.Retryer = retry.NewStandard(standardOptions...) + } +} + +func resolveAWSRetryerProvider(cfg aws.Config, o *Options) { + if cfg.Retryer == nil { + return + } + o.Retryer = cfg.Retryer() +} + +func resolveAWSRetryMode(cfg aws.Config, o *Options) { + if len(cfg.RetryMode) == 0 { + return + } + o.RetryMode = cfg.RetryMode +} +func resolveAWSRetryMaxAttempts(cfg aws.Config, o *Options) { + if cfg.RetryMaxAttempts == 0 { + return + } + o.RetryMaxAttempts = cfg.RetryMaxAttempts +} + +func finalizeRetryMaxAttemptOptions(o *Options, client Client) { + if v := o.RetryMaxAttempts; v == 0 || v == client.options.RetryMaxAttempts { + return + } + + o.Retryer = retry.AddWithMaxAttempts(o.Retryer, o.RetryMaxAttempts) +} + +func resolveAWSEndpointResolver(cfg aws.Config, o *Options) { + if cfg.EndpointResolver == nil && cfg.EndpointResolverWithOptions == nil { + return + } + o.EndpointResolver = withEndpointResolver(cfg.EndpointResolver, cfg.EndpointResolverWithOptions) +} + +func addClientUserAgent(stack *middleware.Stack, options Options) error { + if err := awsmiddleware.AddSDKAgentKeyValue(awsmiddleware.APIMetadata, "pricing", goModuleVersion)(stack); err != nil { + return err + } + + if len(options.AppID) > 0 { + return awsmiddleware.AddSDKAgentKey(awsmiddleware.ApplicationIdentifier, options.AppID)(stack) + } + + return nil +} + +func addHTTPSignerV4Middleware(stack *middleware.Stack, o Options) error { + mw := v4.NewSignHTTPRequestMiddleware(v4.SignHTTPRequestMiddlewareOptions{ + CredentialsProvider: o.Credentials, + Signer: o.HTTPSignerV4, + LogSigning: o.ClientLogMode.IsSigning(), + }) + return stack.Finalize.Add(mw, middleware.After) +} + +type HTTPSignerV4 interface { + SignHTTP(ctx context.Context, credentials aws.Credentials, r *http.Request, payloadHash string, service string, region string, signingTime time.Time, optFns ...func(*v4.SignerOptions)) error +} + +func resolveHTTPSignerV4(o *Options) { + if o.HTTPSignerV4 != nil { + return + } + o.HTTPSignerV4 = newDefaultV4Signer(*o) +} + +func newDefaultV4Signer(o Options) *v4.Signer { + return v4.NewSigner(func(so *v4.SignerOptions) { + so.Logger = o.Logger + so.LogSigning = o.ClientLogMode.IsSigning() + }) +} + +func addRetryMiddlewares(stack *middleware.Stack, o Options) error { + mo := retry.AddRetryMiddlewaresOptions{ + Retryer: o.Retryer, + LogRetryAttempts: o.ClientLogMode.IsRetries(), + } + return retry.AddRetryMiddlewares(stack, mo) +} + +// resolves dual-stack endpoint configuration +func resolveUseDualStackEndpoint(cfg aws.Config, o *Options) error { + if len(cfg.ConfigSources) == 0 { + return nil + } + value, found, err := internalConfig.ResolveUseDualStackEndpoint(context.Background(), cfg.ConfigSources) + if err != nil { + return err + } + if found { + o.EndpointOptions.UseDualStackEndpoint = value + } + return nil +} + +// resolves FIPS endpoint configuration +func resolveUseFIPSEndpoint(cfg aws.Config, o *Options) error { + if len(cfg.ConfigSources) == 0 { + return nil + } + value, found, err := internalConfig.ResolveUseFIPSEndpoint(context.Background(), cfg.ConfigSources) + if err != nil { + return err + } + if found { + o.EndpointOptions.UseFIPSEndpoint = value + } + return nil +} + +func addRequestIDRetrieverMiddleware(stack *middleware.Stack) error { + return awsmiddleware.AddRequestIDRetrieverMiddleware(stack) +} + +func addResponseErrorMiddleware(stack *middleware.Stack) error { + return awshttp.AddResponseErrorMiddleware(stack) +} + +func addRequestResponseLogging(stack *middleware.Stack, o Options) error { + return stack.Deserialize.Add(&smithyhttp.RequestResponseLogger{ + LogRequest: o.ClientLogMode.IsRequest(), + LogRequestWithBody: o.ClientLogMode.IsRequestWithBody(), + LogResponse: o.ClientLogMode.IsResponse(), + LogResponseWithBody: o.ClientLogMode.IsResponseWithBody(), + }, middleware.After) +} + +type endpointDisableHTTPSMiddleware struct { + EndpointDisableHTTPS bool +} + +func (*endpointDisableHTTPSMiddleware) ID() string { + return "endpointDisableHTTPSMiddleware" +} + +func (m *endpointDisableHTTPSMiddleware) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type %T", in.Request) + } + + if m.EndpointDisableHTTPS && !smithyhttp.GetHostnameImmutable(ctx) { + req.URL.Scheme = "http" + } + + return next.HandleSerialize(ctx, in) + +} +func addendpointDisableHTTPSMiddleware(stack *middleware.Stack, o Options) error { + return stack.Serialize.Insert(&endpointDisableHTTPSMiddleware{ + EndpointDisableHTTPS: o.EndpointOptions.DisableHTTPS, + }, "OperationSerializer", middleware.Before) +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_DescribeServices.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_DescribeServices.go new file mode 100644 index 0000000000000..f8b013612ad72 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_DescribeServices.go @@ -0,0 +1,370 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package pricing + +import ( + "context" + "errors" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/aws/signer/v4" + internalauth "github.com/aws/aws-sdk-go-v2/internal/auth" + "github.com/aws/aws-sdk-go-v2/service/pricing/types" + smithyendpoints "github.com/aws/smithy-go/endpoints" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns the metadata for one service or a list of the metadata for all +// services. Use this without a service code to get the service codes for all +// services. Use it with a service code, such as AmazonEC2 , to get information +// specific to that service, such as the attribute names available for that +// service. For example, some of the attribute names available for EC2 are +// volumeType , maxIopsVolume , operation , locationType , and +// instanceCapacity10xlarge . +func (c *Client) DescribeServices(ctx context.Context, params *DescribeServicesInput, optFns ...func(*Options)) (*DescribeServicesOutput, error) { + if params == nil { + params = &DescribeServicesInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DescribeServices", params, optFns, c.addOperationDescribeServicesMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DescribeServicesOutput) + out.ResultMetadata = metadata + return out, nil +} + +type DescribeServicesInput struct { + + // The format version that you want the response to be in. Valid values are: aws_v1 + FormatVersion *string + + // The maximum number of results that you want returned in the response. + MaxResults *int32 + + // The pagination token that indicates the next set of results that you want to + // retrieve. + NextToken *string + + // The code for the service whose information you want to retrieve, such as + // AmazonEC2 . You can use the ServiceCode to filter the results in a GetProducts + // call. To retrieve a list of all services, leave this blank. + ServiceCode *string + + noSmithyDocumentSerde +} + +type DescribeServicesOutput struct { + + // The format version of the response. For example, aws_v1 . + FormatVersion *string + + // The pagination token for the next set of retrievable results. + NextToken *string + + // The service metadata for the service or services in the response. + Services []types.Service + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDescribeServicesMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsjson11_serializeOpDescribeServices{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsjson11_deserializeOpDescribeServices{}, middleware.After) + if err != nil { + return err + } + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = awsmiddleware.AddClientRequestIDMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddComputeContentLengthMiddleware(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = v4.AddComputePayloadSHA256Middleware(stack); err != nil { + return err + } + if err = addRetryMiddlewares(stack, options); err != nil { + return err + } + if err = addHTTPSignerV4Middleware(stack, options); err != nil { + return err + } + if err = awsmiddleware.AddRawResponseToMetadata(stack); err != nil { + return err + } + if err = awsmiddleware.AddRecordResponseTiming(stack); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addDescribeServicesResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDescribeServices(options.Region), middleware.Before); err != nil { + return err + } + if err = awsmiddleware.AddRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addendpointDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + return nil +} + +// DescribeServicesAPIClient is a client that implements the DescribeServices +// operation. +type DescribeServicesAPIClient interface { + DescribeServices(context.Context, *DescribeServicesInput, ...func(*Options)) (*DescribeServicesOutput, error) +} + +var _ DescribeServicesAPIClient = (*Client)(nil) + +// DescribeServicesPaginatorOptions is the paginator options for DescribeServices +type DescribeServicesPaginatorOptions struct { + // The maximum number of results that you want returned in the response. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// DescribeServicesPaginator is a paginator for DescribeServices +type DescribeServicesPaginator struct { + options DescribeServicesPaginatorOptions + client DescribeServicesAPIClient + params *DescribeServicesInput + nextToken *string + firstPage bool +} + +// NewDescribeServicesPaginator returns a new DescribeServicesPaginator +func NewDescribeServicesPaginator(client DescribeServicesAPIClient, params *DescribeServicesInput, optFns ...func(*DescribeServicesPaginatorOptions)) *DescribeServicesPaginator { + if params == nil { + params = &DescribeServicesInput{} + } + + options := DescribeServicesPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &DescribeServicesPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *DescribeServicesPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next DescribeServices page. +func (p *DescribeServicesPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*DescribeServicesOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + result, err := p.client.DescribeServices(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +func newServiceMetadataMiddleware_opDescribeServices(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + SigningName: "pricing", + OperationName: "DescribeServices", + } +} + +type opDescribeServicesResolveEndpointMiddleware struct { + EndpointResolver EndpointResolverV2 + BuiltInResolver builtInParameterResolver +} + +func (*opDescribeServicesResolveEndpointMiddleware) ID() string { + return "ResolveEndpointV2" +} + +func (m *opDescribeServicesResolveEndpointMiddleware) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + if awsmiddleware.GetRequiresLegacyEndpoints(ctx) { + return next.HandleSerialize(ctx, in) + } + + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type %T", in.Request) + } + + if m.EndpointResolver == nil { + return out, metadata, fmt.Errorf("expected endpoint resolver to not be nil") + } + + params := EndpointParameters{} + + m.BuiltInResolver.ResolveBuiltIns(¶ms) + + var resolvedEndpoint smithyendpoints.Endpoint + resolvedEndpoint, err = m.EndpointResolver.ResolveEndpoint(ctx, params) + if err != nil { + return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err) + } + + req.URL = &resolvedEndpoint.URI + + for k := range resolvedEndpoint.Headers { + req.Header.Set( + k, + resolvedEndpoint.Headers.Get(k), + ) + } + + authSchemes, err := internalauth.GetAuthenticationSchemes(&resolvedEndpoint.Properties) + if err != nil { + var nfe *internalauth.NoAuthenticationSchemesFoundError + if errors.As(err, &nfe) { + // if no auth scheme is found, default to sigv4 + signingName := "pricing" + signingRegion := m.BuiltInResolver.(*builtInResolver).Region + ctx = awsmiddleware.SetSigningName(ctx, signingName) + ctx = awsmiddleware.SetSigningRegion(ctx, signingRegion) + + } + var ue *internalauth.UnSupportedAuthenticationSchemeSpecifiedError + if errors.As(err, &ue) { + return out, metadata, fmt.Errorf( + "This operation requests signer version(s) %v but the client only supports %v", + ue.UnsupportedSchemes, + internalauth.SupportedSchemes, + ) + } + } + + for _, authScheme := range authSchemes { + switch authScheme.(type) { + case *internalauth.AuthenticationSchemeV4: + v4Scheme, _ := authScheme.(*internalauth.AuthenticationSchemeV4) + var signingName, signingRegion string + if v4Scheme.SigningName == nil { + signingName = "pricing" + } else { + signingName = *v4Scheme.SigningName + } + if v4Scheme.SigningRegion == nil { + signingRegion = m.BuiltInResolver.(*builtInResolver).Region + } else { + signingRegion = *v4Scheme.SigningRegion + } + if v4Scheme.DisableDoubleEncoding != nil { + // The signer sets an equivalent value at client initialization time. + // Setting this context value will cause the signer to extract it + // and override the value set at client initialization time. + ctx = internalauth.SetDisableDoubleEncoding(ctx, *v4Scheme.DisableDoubleEncoding) + } + ctx = awsmiddleware.SetSigningName(ctx, signingName) + ctx = awsmiddleware.SetSigningRegion(ctx, signingRegion) + break + case *internalauth.AuthenticationSchemeV4A: + v4aScheme, _ := authScheme.(*internalauth.AuthenticationSchemeV4A) + if v4aScheme.SigningName == nil { + v4aScheme.SigningName = aws.String("pricing") + } + if v4aScheme.DisableDoubleEncoding != nil { + // The signer sets an equivalent value at client initialization time. + // Setting this context value will cause the signer to extract it + // and override the value set at client initialization time. + ctx = internalauth.SetDisableDoubleEncoding(ctx, *v4aScheme.DisableDoubleEncoding) + } + ctx = awsmiddleware.SetSigningName(ctx, *v4aScheme.SigningName) + ctx = awsmiddleware.SetSigningRegion(ctx, v4aScheme.SigningRegionSet[0]) + break + case *internalauth.AuthenticationSchemeNone: + break + } + } + + return next.HandleSerialize(ctx, in) +} + +func addDescribeServicesResolveEndpointMiddleware(stack *middleware.Stack, options Options) error { + return stack.Serialize.Insert(&opDescribeServicesResolveEndpointMiddleware{ + EndpointResolver: options.EndpointResolverV2, + BuiltInResolver: &builtInResolver{ + Region: options.Region, + UseDualStack: options.EndpointOptions.UseDualStackEndpoint, + UseFIPS: options.EndpointOptions.UseFIPSEndpoint, + Endpoint: options.BaseEndpoint, + }, + }, "ResolveEndpoint", middleware.After) +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_GetAttributeValues.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_GetAttributeValues.go new file mode 100644 index 0000000000000..9ac2206a95caa --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_GetAttributeValues.go @@ -0,0 +1,374 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package pricing + +import ( + "context" + "errors" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/aws/signer/v4" + internalauth "github.com/aws/aws-sdk-go-v2/internal/auth" + "github.com/aws/aws-sdk-go-v2/service/pricing/types" + smithyendpoints "github.com/aws/smithy-go/endpoints" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a list of attribute values. Attributes are similar to the details in a +// Price List API offer file. For a list of available attributes, see Offer File +// Definitions (https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/reading-an-offer.html#pps-defs) +// in the Billing and Cost Management User Guide (https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/billing-what-is.html) +// . +func (c *Client) GetAttributeValues(ctx context.Context, params *GetAttributeValuesInput, optFns ...func(*Options)) (*GetAttributeValuesOutput, error) { + if params == nil { + params = &GetAttributeValuesInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "GetAttributeValues", params, optFns, c.addOperationGetAttributeValuesMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*GetAttributeValuesOutput) + out.ResultMetadata = metadata + return out, nil +} + +type GetAttributeValuesInput struct { + + // The name of the attribute that you want to retrieve the values for, such as + // volumeType . + // + // This member is required. + AttributeName *string + + // The service code for the service whose attributes you want to retrieve. For + // example, if you want the retrieve an EC2 attribute, use AmazonEC2 . + // + // This member is required. + ServiceCode *string + + // The maximum number of results to return in response. + MaxResults *int32 + + // The pagination token that indicates the next set of results that you want to + // retrieve. + NextToken *string + + noSmithyDocumentSerde +} + +type GetAttributeValuesOutput struct { + + // The list of values for an attribute. For example, Throughput Optimized HDD and + // Provisioned IOPS are two available values for the AmazonEC2 volumeType . + AttributeValues []types.AttributeValue + + // The pagination token that indicates the next set of results to retrieve. + NextToken *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationGetAttributeValuesMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsjson11_serializeOpGetAttributeValues{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsjson11_deserializeOpGetAttributeValues{}, middleware.After) + if err != nil { + return err + } + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = awsmiddleware.AddClientRequestIDMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddComputeContentLengthMiddleware(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = v4.AddComputePayloadSHA256Middleware(stack); err != nil { + return err + } + if err = addRetryMiddlewares(stack, options); err != nil { + return err + } + if err = addHTTPSignerV4Middleware(stack, options); err != nil { + return err + } + if err = awsmiddleware.AddRawResponseToMetadata(stack); err != nil { + return err + } + if err = awsmiddleware.AddRecordResponseTiming(stack); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addGetAttributeValuesResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addOpGetAttributeValuesValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetAttributeValues(options.Region), middleware.Before); err != nil { + return err + } + if err = awsmiddleware.AddRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addendpointDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + return nil +} + +// GetAttributeValuesAPIClient is a client that implements the GetAttributeValues +// operation. +type GetAttributeValuesAPIClient interface { + GetAttributeValues(context.Context, *GetAttributeValuesInput, ...func(*Options)) (*GetAttributeValuesOutput, error) +} + +var _ GetAttributeValuesAPIClient = (*Client)(nil) + +// GetAttributeValuesPaginatorOptions is the paginator options for +// GetAttributeValues +type GetAttributeValuesPaginatorOptions struct { + // The maximum number of results to return in response. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// GetAttributeValuesPaginator is a paginator for GetAttributeValues +type GetAttributeValuesPaginator struct { + options GetAttributeValuesPaginatorOptions + client GetAttributeValuesAPIClient + params *GetAttributeValuesInput + nextToken *string + firstPage bool +} + +// NewGetAttributeValuesPaginator returns a new GetAttributeValuesPaginator +func NewGetAttributeValuesPaginator(client GetAttributeValuesAPIClient, params *GetAttributeValuesInput, optFns ...func(*GetAttributeValuesPaginatorOptions)) *GetAttributeValuesPaginator { + if params == nil { + params = &GetAttributeValuesInput{} + } + + options := GetAttributeValuesPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &GetAttributeValuesPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *GetAttributeValuesPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next GetAttributeValues page. +func (p *GetAttributeValuesPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*GetAttributeValuesOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + result, err := p.client.GetAttributeValues(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +func newServiceMetadataMiddleware_opGetAttributeValues(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + SigningName: "pricing", + OperationName: "GetAttributeValues", + } +} + +type opGetAttributeValuesResolveEndpointMiddleware struct { + EndpointResolver EndpointResolverV2 + BuiltInResolver builtInParameterResolver +} + +func (*opGetAttributeValuesResolveEndpointMiddleware) ID() string { + return "ResolveEndpointV2" +} + +func (m *opGetAttributeValuesResolveEndpointMiddleware) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + if awsmiddleware.GetRequiresLegacyEndpoints(ctx) { + return next.HandleSerialize(ctx, in) + } + + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type %T", in.Request) + } + + if m.EndpointResolver == nil { + return out, metadata, fmt.Errorf("expected endpoint resolver to not be nil") + } + + params := EndpointParameters{} + + m.BuiltInResolver.ResolveBuiltIns(¶ms) + + var resolvedEndpoint smithyendpoints.Endpoint + resolvedEndpoint, err = m.EndpointResolver.ResolveEndpoint(ctx, params) + if err != nil { + return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err) + } + + req.URL = &resolvedEndpoint.URI + + for k := range resolvedEndpoint.Headers { + req.Header.Set( + k, + resolvedEndpoint.Headers.Get(k), + ) + } + + authSchemes, err := internalauth.GetAuthenticationSchemes(&resolvedEndpoint.Properties) + if err != nil { + var nfe *internalauth.NoAuthenticationSchemesFoundError + if errors.As(err, &nfe) { + // if no auth scheme is found, default to sigv4 + signingName := "pricing" + signingRegion := m.BuiltInResolver.(*builtInResolver).Region + ctx = awsmiddleware.SetSigningName(ctx, signingName) + ctx = awsmiddleware.SetSigningRegion(ctx, signingRegion) + + } + var ue *internalauth.UnSupportedAuthenticationSchemeSpecifiedError + if errors.As(err, &ue) { + return out, metadata, fmt.Errorf( + "This operation requests signer version(s) %v but the client only supports %v", + ue.UnsupportedSchemes, + internalauth.SupportedSchemes, + ) + } + } + + for _, authScheme := range authSchemes { + switch authScheme.(type) { + case *internalauth.AuthenticationSchemeV4: + v4Scheme, _ := authScheme.(*internalauth.AuthenticationSchemeV4) + var signingName, signingRegion string + if v4Scheme.SigningName == nil { + signingName = "pricing" + } else { + signingName = *v4Scheme.SigningName + } + if v4Scheme.SigningRegion == nil { + signingRegion = m.BuiltInResolver.(*builtInResolver).Region + } else { + signingRegion = *v4Scheme.SigningRegion + } + if v4Scheme.DisableDoubleEncoding != nil { + // The signer sets an equivalent value at client initialization time. + // Setting this context value will cause the signer to extract it + // and override the value set at client initialization time. + ctx = internalauth.SetDisableDoubleEncoding(ctx, *v4Scheme.DisableDoubleEncoding) + } + ctx = awsmiddleware.SetSigningName(ctx, signingName) + ctx = awsmiddleware.SetSigningRegion(ctx, signingRegion) + break + case *internalauth.AuthenticationSchemeV4A: + v4aScheme, _ := authScheme.(*internalauth.AuthenticationSchemeV4A) + if v4aScheme.SigningName == nil { + v4aScheme.SigningName = aws.String("pricing") + } + if v4aScheme.DisableDoubleEncoding != nil { + // The signer sets an equivalent value at client initialization time. + // Setting this context value will cause the signer to extract it + // and override the value set at client initialization time. + ctx = internalauth.SetDisableDoubleEncoding(ctx, *v4aScheme.DisableDoubleEncoding) + } + ctx = awsmiddleware.SetSigningName(ctx, *v4aScheme.SigningName) + ctx = awsmiddleware.SetSigningRegion(ctx, v4aScheme.SigningRegionSet[0]) + break + case *internalauth.AuthenticationSchemeNone: + break + } + } + + return next.HandleSerialize(ctx, in) +} + +func addGetAttributeValuesResolveEndpointMiddleware(stack *middleware.Stack, options Options) error { + return stack.Serialize.Insert(&opGetAttributeValuesResolveEndpointMiddleware{ + EndpointResolver: options.EndpointResolverV2, + BuiltInResolver: &builtInResolver{ + Region: options.Region, + UseDualStack: options.EndpointOptions.UseDualStackEndpoint, + UseFIPS: options.EndpointOptions.UseFIPSEndpoint, + Endpoint: options.BaseEndpoint, + }, + }, "ResolveEndpoint", middleware.After) +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_GetPriceListFileUrl.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_GetPriceListFileUrl.go new file mode 100644 index 0000000000000..b9abd8fabdab2 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_GetPriceListFileUrl.go @@ -0,0 +1,275 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package pricing + +import ( + "context" + "errors" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/aws/signer/v4" + internalauth "github.com/aws/aws-sdk-go-v2/internal/auth" + smithyendpoints "github.com/aws/smithy-go/endpoints" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// This feature is in preview release and is subject to change. Your use of Amazon +// Web Services Price List API is subject to the Beta Service Participation terms +// of the Amazon Web Services Service Terms (https://aws.amazon.com/service-terms/) +// (Section 1.10). This returns the URL that you can retrieve your Price List file +// from. This URL is based on the PriceListArn and FileFormat that you retrieve +// from the ListPriceLists (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_ListPriceLists.html) +// response. +func (c *Client) GetPriceListFileUrl(ctx context.Context, params *GetPriceListFileUrlInput, optFns ...func(*Options)) (*GetPriceListFileUrlOutput, error) { + if params == nil { + params = &GetPriceListFileUrlInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "GetPriceListFileUrl", params, optFns, c.addOperationGetPriceListFileUrlMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*GetPriceListFileUrlOutput) + out.ResultMetadata = metadata + return out, nil +} + +type GetPriceListFileUrlInput struct { + + // The format that you want to retrieve your Price List files in. The FileFormat + // can be obtained from the ListPriceLists (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_ListPriceLists.html) + // response. + // + // This member is required. + FileFormat *string + + // The unique identifier that maps to where your Price List files are located. + // PriceListArn can be obtained from the ListPriceLists (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_ListPriceLists.html) + // response. + // + // This member is required. + PriceListArn *string + + noSmithyDocumentSerde +} + +type GetPriceListFileUrlOutput struct { + + // The URL to download your Price List file from. + Url *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationGetPriceListFileUrlMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsjson11_serializeOpGetPriceListFileUrl{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsjson11_deserializeOpGetPriceListFileUrl{}, middleware.After) + if err != nil { + return err + } + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = awsmiddleware.AddClientRequestIDMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddComputeContentLengthMiddleware(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = v4.AddComputePayloadSHA256Middleware(stack); err != nil { + return err + } + if err = addRetryMiddlewares(stack, options); err != nil { + return err + } + if err = addHTTPSignerV4Middleware(stack, options); err != nil { + return err + } + if err = awsmiddleware.AddRawResponseToMetadata(stack); err != nil { + return err + } + if err = awsmiddleware.AddRecordResponseTiming(stack); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addGetPriceListFileUrlResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addOpGetPriceListFileUrlValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetPriceListFileUrl(options.Region), middleware.Before); err != nil { + return err + } + if err = awsmiddleware.AddRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addendpointDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + return nil +} + +func newServiceMetadataMiddleware_opGetPriceListFileUrl(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + SigningName: "pricing", + OperationName: "GetPriceListFileUrl", + } +} + +type opGetPriceListFileUrlResolveEndpointMiddleware struct { + EndpointResolver EndpointResolverV2 + BuiltInResolver builtInParameterResolver +} + +func (*opGetPriceListFileUrlResolveEndpointMiddleware) ID() string { + return "ResolveEndpointV2" +} + +func (m *opGetPriceListFileUrlResolveEndpointMiddleware) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + if awsmiddleware.GetRequiresLegacyEndpoints(ctx) { + return next.HandleSerialize(ctx, in) + } + + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type %T", in.Request) + } + + if m.EndpointResolver == nil { + return out, metadata, fmt.Errorf("expected endpoint resolver to not be nil") + } + + params := EndpointParameters{} + + m.BuiltInResolver.ResolveBuiltIns(¶ms) + + var resolvedEndpoint smithyendpoints.Endpoint + resolvedEndpoint, err = m.EndpointResolver.ResolveEndpoint(ctx, params) + if err != nil { + return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err) + } + + req.URL = &resolvedEndpoint.URI + + for k := range resolvedEndpoint.Headers { + req.Header.Set( + k, + resolvedEndpoint.Headers.Get(k), + ) + } + + authSchemes, err := internalauth.GetAuthenticationSchemes(&resolvedEndpoint.Properties) + if err != nil { + var nfe *internalauth.NoAuthenticationSchemesFoundError + if errors.As(err, &nfe) { + // if no auth scheme is found, default to sigv4 + signingName := "pricing" + signingRegion := m.BuiltInResolver.(*builtInResolver).Region + ctx = awsmiddleware.SetSigningName(ctx, signingName) + ctx = awsmiddleware.SetSigningRegion(ctx, signingRegion) + + } + var ue *internalauth.UnSupportedAuthenticationSchemeSpecifiedError + if errors.As(err, &ue) { + return out, metadata, fmt.Errorf( + "This operation requests signer version(s) %v but the client only supports %v", + ue.UnsupportedSchemes, + internalauth.SupportedSchemes, + ) + } + } + + for _, authScheme := range authSchemes { + switch authScheme.(type) { + case *internalauth.AuthenticationSchemeV4: + v4Scheme, _ := authScheme.(*internalauth.AuthenticationSchemeV4) + var signingName, signingRegion string + if v4Scheme.SigningName == nil { + signingName = "pricing" + } else { + signingName = *v4Scheme.SigningName + } + if v4Scheme.SigningRegion == nil { + signingRegion = m.BuiltInResolver.(*builtInResolver).Region + } else { + signingRegion = *v4Scheme.SigningRegion + } + if v4Scheme.DisableDoubleEncoding != nil { + // The signer sets an equivalent value at client initialization time. + // Setting this context value will cause the signer to extract it + // and override the value set at client initialization time. + ctx = internalauth.SetDisableDoubleEncoding(ctx, *v4Scheme.DisableDoubleEncoding) + } + ctx = awsmiddleware.SetSigningName(ctx, signingName) + ctx = awsmiddleware.SetSigningRegion(ctx, signingRegion) + break + case *internalauth.AuthenticationSchemeV4A: + v4aScheme, _ := authScheme.(*internalauth.AuthenticationSchemeV4A) + if v4aScheme.SigningName == nil { + v4aScheme.SigningName = aws.String("pricing") + } + if v4aScheme.DisableDoubleEncoding != nil { + // The signer sets an equivalent value at client initialization time. + // Setting this context value will cause the signer to extract it + // and override the value set at client initialization time. + ctx = internalauth.SetDisableDoubleEncoding(ctx, *v4aScheme.DisableDoubleEncoding) + } + ctx = awsmiddleware.SetSigningName(ctx, *v4aScheme.SigningName) + ctx = awsmiddleware.SetSigningRegion(ctx, v4aScheme.SigningRegionSet[0]) + break + case *internalauth.AuthenticationSchemeNone: + break + } + } + + return next.HandleSerialize(ctx, in) +} + +func addGetPriceListFileUrlResolveEndpointMiddleware(stack *middleware.Stack, options Options) error { + return stack.Serialize.Insert(&opGetPriceListFileUrlResolveEndpointMiddleware{ + EndpointResolver: options.EndpointResolverV2, + BuiltInResolver: &builtInResolver{ + Region: options.Region, + UseDualStack: options.EndpointOptions.UseDualStackEndpoint, + UseFIPS: options.EndpointOptions.UseFIPSEndpoint, + Endpoint: options.BaseEndpoint, + }, + }, "ResolveEndpoint", middleware.After) +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_GetProducts.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_GetProducts.go new file mode 100644 index 0000000000000..837c38d93c987 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_GetProducts.go @@ -0,0 +1,371 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package pricing + +import ( + "context" + "errors" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/aws/signer/v4" + internalauth "github.com/aws/aws-sdk-go-v2/internal/auth" + "github.com/aws/aws-sdk-go-v2/service/pricing/types" + smithyendpoints "github.com/aws/smithy-go/endpoints" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Returns a list of all products that match the filter criteria. +func (c *Client) GetProducts(ctx context.Context, params *GetProductsInput, optFns ...func(*Options)) (*GetProductsOutput, error) { + if params == nil { + params = &GetProductsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "GetProducts", params, optFns, c.addOperationGetProductsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*GetProductsOutput) + out.ResultMetadata = metadata + return out, nil +} + +type GetProductsInput struct { + + // The code for the service whose products you want to retrieve. + // + // This member is required. + ServiceCode *string + + // The list of filters that limit the returned products. only products that match + // all filters are returned. + Filters []types.Filter + + // The format version that you want the response to be in. Valid values are: aws_v1 + FormatVersion *string + + // The maximum number of results to return in the response. + MaxResults *int32 + + // The pagination token that indicates the next set of results that you want to + // retrieve. + NextToken *string + + noSmithyDocumentSerde +} + +type GetProductsOutput struct { + + // The format version of the response. For example, aws_v1. + FormatVersion *string + + // The pagination token that indicates the next set of results to retrieve. + NextToken *string + + // The list of products that match your filters. The list contains both the + // product metadata and the price information. + PriceList []string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationGetProductsMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsjson11_serializeOpGetProducts{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsjson11_deserializeOpGetProducts{}, middleware.After) + if err != nil { + return err + } + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = awsmiddleware.AddClientRequestIDMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddComputeContentLengthMiddleware(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = v4.AddComputePayloadSHA256Middleware(stack); err != nil { + return err + } + if err = addRetryMiddlewares(stack, options); err != nil { + return err + } + if err = addHTTPSignerV4Middleware(stack, options); err != nil { + return err + } + if err = awsmiddleware.AddRawResponseToMetadata(stack); err != nil { + return err + } + if err = awsmiddleware.AddRecordResponseTiming(stack); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addGetProductsResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addOpGetProductsValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetProducts(options.Region), middleware.Before); err != nil { + return err + } + if err = awsmiddleware.AddRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addendpointDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + return nil +} + +// GetProductsAPIClient is a client that implements the GetProducts operation. +type GetProductsAPIClient interface { + GetProducts(context.Context, *GetProductsInput, ...func(*Options)) (*GetProductsOutput, error) +} + +var _ GetProductsAPIClient = (*Client)(nil) + +// GetProductsPaginatorOptions is the paginator options for GetProducts +type GetProductsPaginatorOptions struct { + // The maximum number of results to return in the response. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// GetProductsPaginator is a paginator for GetProducts +type GetProductsPaginator struct { + options GetProductsPaginatorOptions + client GetProductsAPIClient + params *GetProductsInput + nextToken *string + firstPage bool +} + +// NewGetProductsPaginator returns a new GetProductsPaginator +func NewGetProductsPaginator(client GetProductsAPIClient, params *GetProductsInput, optFns ...func(*GetProductsPaginatorOptions)) *GetProductsPaginator { + if params == nil { + params = &GetProductsInput{} + } + + options := GetProductsPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &GetProductsPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *GetProductsPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next GetProducts page. +func (p *GetProductsPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*GetProductsOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + result, err := p.client.GetProducts(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +func newServiceMetadataMiddleware_opGetProducts(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + SigningName: "pricing", + OperationName: "GetProducts", + } +} + +type opGetProductsResolveEndpointMiddleware struct { + EndpointResolver EndpointResolverV2 + BuiltInResolver builtInParameterResolver +} + +func (*opGetProductsResolveEndpointMiddleware) ID() string { + return "ResolveEndpointV2" +} + +func (m *opGetProductsResolveEndpointMiddleware) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + if awsmiddleware.GetRequiresLegacyEndpoints(ctx) { + return next.HandleSerialize(ctx, in) + } + + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type %T", in.Request) + } + + if m.EndpointResolver == nil { + return out, metadata, fmt.Errorf("expected endpoint resolver to not be nil") + } + + params := EndpointParameters{} + + m.BuiltInResolver.ResolveBuiltIns(¶ms) + + var resolvedEndpoint smithyendpoints.Endpoint + resolvedEndpoint, err = m.EndpointResolver.ResolveEndpoint(ctx, params) + if err != nil { + return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err) + } + + req.URL = &resolvedEndpoint.URI + + for k := range resolvedEndpoint.Headers { + req.Header.Set( + k, + resolvedEndpoint.Headers.Get(k), + ) + } + + authSchemes, err := internalauth.GetAuthenticationSchemes(&resolvedEndpoint.Properties) + if err != nil { + var nfe *internalauth.NoAuthenticationSchemesFoundError + if errors.As(err, &nfe) { + // if no auth scheme is found, default to sigv4 + signingName := "pricing" + signingRegion := m.BuiltInResolver.(*builtInResolver).Region + ctx = awsmiddleware.SetSigningName(ctx, signingName) + ctx = awsmiddleware.SetSigningRegion(ctx, signingRegion) + + } + var ue *internalauth.UnSupportedAuthenticationSchemeSpecifiedError + if errors.As(err, &ue) { + return out, metadata, fmt.Errorf( + "This operation requests signer version(s) %v but the client only supports %v", + ue.UnsupportedSchemes, + internalauth.SupportedSchemes, + ) + } + } + + for _, authScheme := range authSchemes { + switch authScheme.(type) { + case *internalauth.AuthenticationSchemeV4: + v4Scheme, _ := authScheme.(*internalauth.AuthenticationSchemeV4) + var signingName, signingRegion string + if v4Scheme.SigningName == nil { + signingName = "pricing" + } else { + signingName = *v4Scheme.SigningName + } + if v4Scheme.SigningRegion == nil { + signingRegion = m.BuiltInResolver.(*builtInResolver).Region + } else { + signingRegion = *v4Scheme.SigningRegion + } + if v4Scheme.DisableDoubleEncoding != nil { + // The signer sets an equivalent value at client initialization time. + // Setting this context value will cause the signer to extract it + // and override the value set at client initialization time. + ctx = internalauth.SetDisableDoubleEncoding(ctx, *v4Scheme.DisableDoubleEncoding) + } + ctx = awsmiddleware.SetSigningName(ctx, signingName) + ctx = awsmiddleware.SetSigningRegion(ctx, signingRegion) + break + case *internalauth.AuthenticationSchemeV4A: + v4aScheme, _ := authScheme.(*internalauth.AuthenticationSchemeV4A) + if v4aScheme.SigningName == nil { + v4aScheme.SigningName = aws.String("pricing") + } + if v4aScheme.DisableDoubleEncoding != nil { + // The signer sets an equivalent value at client initialization time. + // Setting this context value will cause the signer to extract it + // and override the value set at client initialization time. + ctx = internalauth.SetDisableDoubleEncoding(ctx, *v4aScheme.DisableDoubleEncoding) + } + ctx = awsmiddleware.SetSigningName(ctx, *v4aScheme.SigningName) + ctx = awsmiddleware.SetSigningRegion(ctx, v4aScheme.SigningRegionSet[0]) + break + case *internalauth.AuthenticationSchemeNone: + break + } + } + + return next.HandleSerialize(ctx, in) +} + +func addGetProductsResolveEndpointMiddleware(stack *middleware.Stack, options Options) error { + return stack.Serialize.Insert(&opGetProductsResolveEndpointMiddleware{ + EndpointResolver: options.EndpointResolverV2, + BuiltInResolver: &builtInResolver{ + Region: options.Region, + UseDualStack: options.EndpointOptions.UseDualStackEndpoint, + UseFIPS: options.EndpointOptions.UseFIPSEndpoint, + Endpoint: options.BaseEndpoint, + }, + }, "ResolveEndpoint", middleware.After) +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_ListPriceLists.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_ListPriceLists.go new file mode 100644 index 0000000000000..6b6b6c912c840 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/api_op_ListPriceLists.go @@ -0,0 +1,396 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package pricing + +import ( + "context" + "errors" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/aws/signer/v4" + internalauth "github.com/aws/aws-sdk-go-v2/internal/auth" + "github.com/aws/aws-sdk-go-v2/service/pricing/types" + smithyendpoints "github.com/aws/smithy-go/endpoints" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" + "time" +) + +// This feature is in preview release and is subject to change. Your use of Amazon +// Web Services Price List API is subject to the Beta Service Participation terms +// of the Amazon Web Services Service Terms (https://aws.amazon.com/service-terms/) +// (Section 1.10). This returns a list of Price List references that the requester +// if authorized to view, given a ServiceCode , CurrencyCode , and an EffectiveDate +// . Use without a RegionCode filter to list Price List references from all +// available Amazon Web Services Regions. Use with a RegionCode filter to get the +// Price List reference that's specific to a specific Amazon Web Services Region. +// You can use the PriceListArn from the response to get your preferred Price List +// files through the GetPriceListFileUrl (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_GetPriceListFileUrl.html) +// API. +func (c *Client) ListPriceLists(ctx context.Context, params *ListPriceListsInput, optFns ...func(*Options)) (*ListPriceListsOutput, error) { + if params == nil { + params = &ListPriceListsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListPriceLists", params, optFns, c.addOperationListPriceListsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ListPriceListsOutput) + out.ResultMetadata = metadata + return out, nil +} + +type ListPriceListsInput struct { + + // The three alphabetical character ISO-4217 currency code that the Price List + // files are denominated in. + // + // This member is required. + CurrencyCode *string + + // The date that the Price List file prices are effective from. + // + // This member is required. + EffectiveDate *time.Time + + // The service code or the Savings Plan service code for the attributes that you + // want to retrieve. For example, to get the list of applicable Amazon EC2 price + // lists, use AmazonEC2 . For a full list of service codes containing On-Demand and + // Reserved Instance (RI) pricing, use the DescribeServices (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_DescribeServices.html#awscostmanagement-pricing_DescribeServices-request-FormatVersion) + // API. To retrieve the Compute Savings Plan price lists, use ComputeSavingsPlans . + // To retrieve Machine Learning Savings Plans price lists, use + // MachineLearningSavingsPlans . + // + // This member is required. + ServiceCode *string + + // The maximum number of results to return in the response. + MaxResults *int32 + + // The pagination token that indicates the next set of results that you want to + // retrieve. + NextToken *string + + // This is used to filter the Price List by Amazon Web Services Region. For + // example, to get the price list only for the US East (N. Virginia) Region, use + // us-east-1 . If nothing is specified, you retrieve price lists for all applicable + // Regions. The available RegionCode list can be retrieved from GetAttributeValues (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_GetAttributeValues.html) + // API. + RegionCode *string + + noSmithyDocumentSerde +} + +type ListPriceListsOutput struct { + + // The pagination token that indicates the next set of results to retrieve. + NextToken *string + + // The type of price list references that match your request. + PriceLists []types.PriceList + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListPriceListsMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsAwsjson11_serializeOpListPriceLists{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsAwsjson11_deserializeOpListPriceLists{}, middleware.After) + if err != nil { + return err + } + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err = awsmiddleware.AddClientRequestIDMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddComputeContentLengthMiddleware(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = v4.AddComputePayloadSHA256Middleware(stack); err != nil { + return err + } + if err = addRetryMiddlewares(stack, options); err != nil { + return err + } + if err = addHTTPSignerV4Middleware(stack, options); err != nil { + return err + } + if err = awsmiddleware.AddRawResponseToMetadata(stack); err != nil { + return err + } + if err = awsmiddleware.AddRecordResponseTiming(stack); err != nil { + return err + } + if err = addClientUserAgent(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addListPriceListsResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addOpListPriceListsValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListPriceLists(options.Region), middleware.Before); err != nil { + return err + } + if err = awsmiddleware.AddRecursionDetection(stack); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addendpointDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + return nil +} + +// ListPriceListsAPIClient is a client that implements the ListPriceLists +// operation. +type ListPriceListsAPIClient interface { + ListPriceLists(context.Context, *ListPriceListsInput, ...func(*Options)) (*ListPriceListsOutput, error) +} + +var _ ListPriceListsAPIClient = (*Client)(nil) + +// ListPriceListsPaginatorOptions is the paginator options for ListPriceLists +type ListPriceListsPaginatorOptions struct { + // The maximum number of results to return in the response. + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// ListPriceListsPaginator is a paginator for ListPriceLists +type ListPriceListsPaginator struct { + options ListPriceListsPaginatorOptions + client ListPriceListsAPIClient + params *ListPriceListsInput + nextToken *string + firstPage bool +} + +// NewListPriceListsPaginator returns a new ListPriceListsPaginator +func NewListPriceListsPaginator(client ListPriceListsAPIClient, params *ListPriceListsInput, optFns ...func(*ListPriceListsPaginatorOptions)) *ListPriceListsPaginator { + if params == nil { + params = &ListPriceListsInput{} + } + + options := ListPriceListsPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &ListPriceListsPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *ListPriceListsPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next ListPriceLists page. +func (p *ListPriceListsPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*ListPriceListsOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + result, err := p.client.ListPriceLists(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +func newServiceMetadataMiddleware_opListPriceLists(region string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + SigningName: "pricing", + OperationName: "ListPriceLists", + } +} + +type opListPriceListsResolveEndpointMiddleware struct { + EndpointResolver EndpointResolverV2 + BuiltInResolver builtInParameterResolver +} + +func (*opListPriceListsResolveEndpointMiddleware) ID() string { + return "ResolveEndpointV2" +} + +func (m *opListPriceListsResolveEndpointMiddleware) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + if awsmiddleware.GetRequiresLegacyEndpoints(ctx) { + return next.HandleSerialize(ctx, in) + } + + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type %T", in.Request) + } + + if m.EndpointResolver == nil { + return out, metadata, fmt.Errorf("expected endpoint resolver to not be nil") + } + + params := EndpointParameters{} + + m.BuiltInResolver.ResolveBuiltIns(¶ms) + + var resolvedEndpoint smithyendpoints.Endpoint + resolvedEndpoint, err = m.EndpointResolver.ResolveEndpoint(ctx, params) + if err != nil { + return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err) + } + + req.URL = &resolvedEndpoint.URI + + for k := range resolvedEndpoint.Headers { + req.Header.Set( + k, + resolvedEndpoint.Headers.Get(k), + ) + } + + authSchemes, err := internalauth.GetAuthenticationSchemes(&resolvedEndpoint.Properties) + if err != nil { + var nfe *internalauth.NoAuthenticationSchemesFoundError + if errors.As(err, &nfe) { + // if no auth scheme is found, default to sigv4 + signingName := "pricing" + signingRegion := m.BuiltInResolver.(*builtInResolver).Region + ctx = awsmiddleware.SetSigningName(ctx, signingName) + ctx = awsmiddleware.SetSigningRegion(ctx, signingRegion) + + } + var ue *internalauth.UnSupportedAuthenticationSchemeSpecifiedError + if errors.As(err, &ue) { + return out, metadata, fmt.Errorf( + "This operation requests signer version(s) %v but the client only supports %v", + ue.UnsupportedSchemes, + internalauth.SupportedSchemes, + ) + } + } + + for _, authScheme := range authSchemes { + switch authScheme.(type) { + case *internalauth.AuthenticationSchemeV4: + v4Scheme, _ := authScheme.(*internalauth.AuthenticationSchemeV4) + var signingName, signingRegion string + if v4Scheme.SigningName == nil { + signingName = "pricing" + } else { + signingName = *v4Scheme.SigningName + } + if v4Scheme.SigningRegion == nil { + signingRegion = m.BuiltInResolver.(*builtInResolver).Region + } else { + signingRegion = *v4Scheme.SigningRegion + } + if v4Scheme.DisableDoubleEncoding != nil { + // The signer sets an equivalent value at client initialization time. + // Setting this context value will cause the signer to extract it + // and override the value set at client initialization time. + ctx = internalauth.SetDisableDoubleEncoding(ctx, *v4Scheme.DisableDoubleEncoding) + } + ctx = awsmiddleware.SetSigningName(ctx, signingName) + ctx = awsmiddleware.SetSigningRegion(ctx, signingRegion) + break + case *internalauth.AuthenticationSchemeV4A: + v4aScheme, _ := authScheme.(*internalauth.AuthenticationSchemeV4A) + if v4aScheme.SigningName == nil { + v4aScheme.SigningName = aws.String("pricing") + } + if v4aScheme.DisableDoubleEncoding != nil { + // The signer sets an equivalent value at client initialization time. + // Setting this context value will cause the signer to extract it + // and override the value set at client initialization time. + ctx = internalauth.SetDisableDoubleEncoding(ctx, *v4aScheme.DisableDoubleEncoding) + } + ctx = awsmiddleware.SetSigningName(ctx, *v4aScheme.SigningName) + ctx = awsmiddleware.SetSigningRegion(ctx, v4aScheme.SigningRegionSet[0]) + break + case *internalauth.AuthenticationSchemeNone: + break + } + } + + return next.HandleSerialize(ctx, in) +} + +func addListPriceListsResolveEndpointMiddleware(stack *middleware.Stack, options Options) error { + return stack.Serialize.Insert(&opListPriceListsResolveEndpointMiddleware{ + EndpointResolver: options.EndpointResolverV2, + BuiltInResolver: &builtInResolver{ + Region: options.Region, + UseDualStack: options.EndpointOptions.UseDualStackEndpoint, + UseFIPS: options.EndpointOptions.UseFIPSEndpoint, + Endpoint: options.BaseEndpoint, + }, + }, "ResolveEndpoint", middleware.After) +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/deserializers.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/deserializers.go new file mode 100644 index 0000000000000..2c7e7a8e61866 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/deserializers.go @@ -0,0 +1,1680 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package pricing + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws/protocol/restjson" + "github.com/aws/aws-sdk-go-v2/service/pricing/types" + smithy "github.com/aws/smithy-go" + smithyio "github.com/aws/smithy-go/io" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + smithyhttp "github.com/aws/smithy-go/transport/http" + "io" + "strings" +) + +type awsAwsjson11_deserializeOpDescribeServices struct { +} + +func (*awsAwsjson11_deserializeOpDescribeServices) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsjson11_deserializeOpDescribeServices) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsjson11_deserializeOpErrorDescribeServices(response, &metadata) + } + output := &DescribeServicesOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsAwsjson11_deserializeOpDocumentDescribeServicesOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsjson11_deserializeOpErrorDescribeServices(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("ExpiredNextTokenException", errorCode): + return awsAwsjson11_deserializeErrorExpiredNextTokenException(response, errorBody) + + case strings.EqualFold("InternalErrorException", errorCode): + return awsAwsjson11_deserializeErrorInternalErrorException(response, errorBody) + + case strings.EqualFold("InvalidNextTokenException", errorCode): + return awsAwsjson11_deserializeErrorInvalidNextTokenException(response, errorBody) + + case strings.EqualFold("InvalidParameterException", errorCode): + return awsAwsjson11_deserializeErrorInvalidParameterException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsAwsjson11_deserializeErrorNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsjson11_deserializeOpGetAttributeValues struct { +} + +func (*awsAwsjson11_deserializeOpGetAttributeValues) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsjson11_deserializeOpGetAttributeValues) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsjson11_deserializeOpErrorGetAttributeValues(response, &metadata) + } + output := &GetAttributeValuesOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsAwsjson11_deserializeOpDocumentGetAttributeValuesOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsjson11_deserializeOpErrorGetAttributeValues(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("ExpiredNextTokenException", errorCode): + return awsAwsjson11_deserializeErrorExpiredNextTokenException(response, errorBody) + + case strings.EqualFold("InternalErrorException", errorCode): + return awsAwsjson11_deserializeErrorInternalErrorException(response, errorBody) + + case strings.EqualFold("InvalidNextTokenException", errorCode): + return awsAwsjson11_deserializeErrorInvalidNextTokenException(response, errorBody) + + case strings.EqualFold("InvalidParameterException", errorCode): + return awsAwsjson11_deserializeErrorInvalidParameterException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsAwsjson11_deserializeErrorNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsjson11_deserializeOpGetPriceListFileUrl struct { +} + +func (*awsAwsjson11_deserializeOpGetPriceListFileUrl) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsjson11_deserializeOpGetPriceListFileUrl) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsjson11_deserializeOpErrorGetPriceListFileUrl(response, &metadata) + } + output := &GetPriceListFileUrlOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsAwsjson11_deserializeOpDocumentGetPriceListFileUrlOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsjson11_deserializeOpErrorGetPriceListFileUrl(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("AccessDeniedException", errorCode): + return awsAwsjson11_deserializeErrorAccessDeniedException(response, errorBody) + + case strings.EqualFold("InternalErrorException", errorCode): + return awsAwsjson11_deserializeErrorInternalErrorException(response, errorBody) + + case strings.EqualFold("InvalidParameterException", errorCode): + return awsAwsjson11_deserializeErrorInvalidParameterException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsAwsjson11_deserializeErrorNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsjson11_deserializeOpGetProducts struct { +} + +func (*awsAwsjson11_deserializeOpGetProducts) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsjson11_deserializeOpGetProducts) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsjson11_deserializeOpErrorGetProducts(response, &metadata) + } + output := &GetProductsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsAwsjson11_deserializeOpDocumentGetProductsOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsjson11_deserializeOpErrorGetProducts(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("ExpiredNextTokenException", errorCode): + return awsAwsjson11_deserializeErrorExpiredNextTokenException(response, errorBody) + + case strings.EqualFold("InternalErrorException", errorCode): + return awsAwsjson11_deserializeErrorInternalErrorException(response, errorBody) + + case strings.EqualFold("InvalidNextTokenException", errorCode): + return awsAwsjson11_deserializeErrorInvalidNextTokenException(response, errorBody) + + case strings.EqualFold("InvalidParameterException", errorCode): + return awsAwsjson11_deserializeErrorInvalidParameterException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsAwsjson11_deserializeErrorNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsAwsjson11_deserializeOpListPriceLists struct { +} + +func (*awsAwsjson11_deserializeOpListPriceLists) ID() string { + return "OperationDeserializer" +} + +func (m *awsAwsjson11_deserializeOpListPriceLists) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsAwsjson11_deserializeOpErrorListPriceLists(response, &metadata) + } + output := &ListPriceListsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsAwsjson11_deserializeOpDocumentListPriceListsOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + return out, metadata, err +} + +func awsAwsjson11_deserializeOpErrorListPriceLists(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("AccessDeniedException", errorCode): + return awsAwsjson11_deserializeErrorAccessDeniedException(response, errorBody) + + case strings.EqualFold("ExpiredNextTokenException", errorCode): + return awsAwsjson11_deserializeErrorExpiredNextTokenException(response, errorBody) + + case strings.EqualFold("InternalErrorException", errorCode): + return awsAwsjson11_deserializeErrorInternalErrorException(response, errorBody) + + case strings.EqualFold("InvalidNextTokenException", errorCode): + return awsAwsjson11_deserializeErrorInvalidNextTokenException(response, errorBody) + + case strings.EqualFold("InvalidParameterException", errorCode): + return awsAwsjson11_deserializeErrorInvalidParameterException(response, errorBody) + + case strings.EqualFold("NotFoundException", errorCode): + return awsAwsjson11_deserializeErrorNotFoundException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsAwsjson11_deserializeErrorAccessDeniedException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + output := &types.AccessDeniedException{} + err := awsAwsjson11_deserializeDocumentAccessDeniedException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + return output +} + +func awsAwsjson11_deserializeErrorExpiredNextTokenException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + output := &types.ExpiredNextTokenException{} + err := awsAwsjson11_deserializeDocumentExpiredNextTokenException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + return output +} + +func awsAwsjson11_deserializeErrorInternalErrorException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + output := &types.InternalErrorException{} + err := awsAwsjson11_deserializeDocumentInternalErrorException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + return output +} + +func awsAwsjson11_deserializeErrorInvalidNextTokenException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + output := &types.InvalidNextTokenException{} + err := awsAwsjson11_deserializeDocumentInvalidNextTokenException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + return output +} + +func awsAwsjson11_deserializeErrorInvalidParameterException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + output := &types.InvalidParameterException{} + err := awsAwsjson11_deserializeDocumentInvalidParameterException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + return output +} + +func awsAwsjson11_deserializeErrorNotFoundException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + output := &types.NotFoundException{} + err := awsAwsjson11_deserializeDocumentNotFoundException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + return output +} + +func awsAwsjson11_deserializeDocumentAccessDeniedException(v **types.AccessDeniedException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.AccessDeniedException + if *v == nil { + sv = &types.AccessDeniedException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected errorMessage to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsAwsjson11_deserializeDocumentAttributeNameList(v *[]string, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []string + if *v == nil { + cv = []string{} + } else { + cv = *v + } + + for _, value := range shape { + var col string + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + col = jtv + } + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsAwsjson11_deserializeDocumentAttributeValue(v **types.AttributeValue, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.AttributeValue + if *v == nil { + sv = &types.AttributeValue{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "Value": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.Value = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsAwsjson11_deserializeDocumentAttributeValueList(v *[]types.AttributeValue, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.AttributeValue + if *v == nil { + cv = []types.AttributeValue{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.AttributeValue + destAddr := &col + if err := awsAwsjson11_deserializeDocumentAttributeValue(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsAwsjson11_deserializeDocumentExpiredNextTokenException(v **types.ExpiredNextTokenException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ExpiredNextTokenException + if *v == nil { + sv = &types.ExpiredNextTokenException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected errorMessage to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsAwsjson11_deserializeDocumentFileFormats(v *[]string, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []string + if *v == nil { + cv = []string{} + } else { + cv = *v + } + + for _, value := range shape { + var col string + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected FileFormat to be of type string, got %T instead", value) + } + col = jtv + } + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsAwsjson11_deserializeDocumentInternalErrorException(v **types.InternalErrorException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.InternalErrorException + if *v == nil { + sv = &types.InternalErrorException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected errorMessage to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsAwsjson11_deserializeDocumentInvalidNextTokenException(v **types.InvalidNextTokenException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.InvalidNextTokenException + if *v == nil { + sv = &types.InvalidNextTokenException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected errorMessage to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsAwsjson11_deserializeDocumentInvalidParameterException(v **types.InvalidParameterException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.InvalidParameterException + if *v == nil { + sv = &types.InvalidParameterException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected errorMessage to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsAwsjson11_deserializeDocumentNotFoundException(v **types.NotFoundException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.NotFoundException + if *v == nil { + sv = &types.NotFoundException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected errorMessage to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsAwsjson11_deserializeDocumentPriceList(v **types.PriceList, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.PriceList + if *v == nil { + sv = &types.PriceList{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "CurrencyCode": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected CurrencyCode to be of type string, got %T instead", value) + } + sv.CurrencyCode = ptr.String(jtv) + } + + case "FileFormats": + if err := awsAwsjson11_deserializeDocumentFileFormats(&sv.FileFormats, value); err != nil { + return err + } + + case "PriceListArn": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected PriceListArn to be of type string, got %T instead", value) + } + sv.PriceListArn = ptr.String(jtv) + } + + case "RegionCode": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected RegionCode to be of type string, got %T instead", value) + } + sv.RegionCode = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsAwsjson11_deserializeDocumentPriceListJsonItems(v *[]string, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []string + if *v == nil { + cv = []string{} + } else { + cv = *v + } + + for _, value := range shape { + var col string + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected SynthesizedJsonPriceListJsonItem to be of type string, got %T instead", value) + } + col = jtv + } + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsAwsjson11_deserializeDocumentPriceLists(v *[]types.PriceList, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.PriceList + if *v == nil { + cv = []types.PriceList{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.PriceList + destAddr := &col + if err := awsAwsjson11_deserializeDocumentPriceList(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsAwsjson11_deserializeDocumentService(v **types.Service, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.Service + if *v == nil { + sv = &types.Service{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "AttributeNames": + if err := awsAwsjson11_deserializeDocumentAttributeNameList(&sv.AttributeNames, value); err != nil { + return err + } + + case "ServiceCode": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.ServiceCode = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsAwsjson11_deserializeDocumentServiceList(v *[]types.Service, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.Service + if *v == nil { + cv = []types.Service{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.Service + destAddr := &col + if err := awsAwsjson11_deserializeDocumentService(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsAwsjson11_deserializeOpDocumentDescribeServicesOutput(v **DescribeServicesOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DescribeServicesOutput + if *v == nil { + sv = &DescribeServicesOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "FormatVersion": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.FormatVersion = ptr.String(jtv) + } + + case "NextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + case "Services": + if err := awsAwsjson11_deserializeDocumentServiceList(&sv.Services, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsAwsjson11_deserializeOpDocumentGetAttributeValuesOutput(v **GetAttributeValuesOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *GetAttributeValuesOutput + if *v == nil { + sv = &GetAttributeValuesOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "AttributeValues": + if err := awsAwsjson11_deserializeDocumentAttributeValueList(&sv.AttributeValues, value); err != nil { + return err + } + + case "NextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsAwsjson11_deserializeOpDocumentGetPriceListFileUrlOutput(v **GetPriceListFileUrlOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *GetPriceListFileUrlOutput + if *v == nil { + sv = &GetPriceListFileUrlOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "Url": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.Url = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsAwsjson11_deserializeOpDocumentGetProductsOutput(v **GetProductsOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *GetProductsOutput + if *v == nil { + sv = &GetProductsOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "FormatVersion": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.FormatVersion = ptr.String(jtv) + } + + case "NextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + case "PriceList": + if err := awsAwsjson11_deserializeDocumentPriceListJsonItems(&sv.PriceList, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsAwsjson11_deserializeOpDocumentListPriceListsOutput(v **ListPriceListsOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListPriceListsOutput + if *v == nil { + sv = &ListPriceListsOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "NextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + case "PriceLists": + if err := awsAwsjson11_deserializeDocumentPriceLists(&sv.PriceLists, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/doc.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/doc.go new file mode 100644 index 0000000000000..f293831f91b20 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/doc.go @@ -0,0 +1,28 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +// Package pricing provides the API client, operations, and parameter types for +// AWS Price List Service. +// +// The Amazon Web Services Price List API is a centralized and convenient way to +// programmatically query Amazon Web Services for services, products, and pricing +// information. The Amazon Web Services Price List uses standardized product +// attributes such as Location , Storage Class , and Operating System , and +// provides prices at the SKU level. You can use the Amazon Web Services Price List +// to do the following: +// - Build cost control and scenario planning tools +// - Reconcile billing data +// - Forecast future spend for budgeting purposes +// - Provide cost benefit analysis that compare your internal workloads with +// Amazon Web Services +// +// Use GetServices without a service code to retrieve the service codes for all +// Amazon Web Services, then GetServices with a service code to retrieve the +// attribute names for that service. After you have the service code and attribute +// names, you can use GetAttributeValues to see what values are available for an +// attribute. With the service code and an attribute name and value, you can use +// GetProducts to find specific products that you're interested in, such as an +// AmazonEC2 instance, with a Provisioned IOPS volumeType . You can use the +// following endpoints for the Amazon Web Services Price List API: +// - https://api.pricing.us-east-1.amazonaws.com +// - https://api.pricing.ap-south-1.amazonaws.com +package pricing diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/endpoints.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/endpoints.go new file mode 100644 index 0000000000000..24ddb0ac038e1 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/endpoints.go @@ -0,0 +1,499 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package pricing + +import ( + "context" + "errors" + "fmt" + "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" + "github.com/aws/aws-sdk-go-v2/internal/endpoints/awsrulesfn" + internalendpoints "github.com/aws/aws-sdk-go-v2/service/pricing/internal/endpoints" + smithyendpoints "github.com/aws/smithy-go/endpoints" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + smithyhttp "github.com/aws/smithy-go/transport/http" + "net/http" + "net/url" + "strings" +) + +// EndpointResolverOptions is the service endpoint resolver options +type EndpointResolverOptions = internalendpoints.Options + +// EndpointResolver interface for resolving service endpoints. +type EndpointResolver interface { + ResolveEndpoint(region string, options EndpointResolverOptions) (aws.Endpoint, error) +} + +var _ EndpointResolver = &internalendpoints.Resolver{} + +// NewDefaultEndpointResolver constructs a new service endpoint resolver +func NewDefaultEndpointResolver() *internalendpoints.Resolver { + return internalendpoints.New() +} + +// EndpointResolverFunc is a helper utility that wraps a function so it satisfies +// the EndpointResolver interface. This is useful when you want to add additional +// endpoint resolving logic, or stub out specific endpoints with custom values. +type EndpointResolverFunc func(region string, options EndpointResolverOptions) (aws.Endpoint, error) + +func (fn EndpointResolverFunc) ResolveEndpoint(region string, options EndpointResolverOptions) (endpoint aws.Endpoint, err error) { + return fn(region, options) +} + +// EndpointResolverFromURL returns an EndpointResolver configured using the +// provided endpoint url. By default, the resolved endpoint resolver uses the +// client region as signing region, and the endpoint source is set to +// EndpointSourceCustom.You can provide functional options to configure endpoint +// values for the resolved endpoint. +func EndpointResolverFromURL(url string, optFns ...func(*aws.Endpoint)) EndpointResolver { + e := aws.Endpoint{URL: url, Source: aws.EndpointSourceCustom} + for _, fn := range optFns { + fn(&e) + } + + return EndpointResolverFunc( + func(region string, options EndpointResolverOptions) (aws.Endpoint, error) { + if len(e.SigningRegion) == 0 { + e.SigningRegion = region + } + return e, nil + }, + ) +} + +type ResolveEndpoint struct { + Resolver EndpointResolver + Options EndpointResolverOptions +} + +func (*ResolveEndpoint) ID() string { + return "ResolveEndpoint" +} + +func (m *ResolveEndpoint) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + if !awsmiddleware.GetRequiresLegacyEndpoints(ctx) { + return next.HandleSerialize(ctx, in) + } + + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type %T", in.Request) + } + + if m.Resolver == nil { + return out, metadata, fmt.Errorf("expected endpoint resolver to not be nil") + } + + eo := m.Options + eo.Logger = middleware.GetLogger(ctx) + + var endpoint aws.Endpoint + endpoint, err = m.Resolver.ResolveEndpoint(awsmiddleware.GetRegion(ctx), eo) + if err != nil { + nf := (&aws.EndpointNotFoundError{}) + if errors.As(err, &nf) { + ctx = awsmiddleware.SetRequiresLegacyEndpoints(ctx, false) + return next.HandleSerialize(ctx, in) + } + return out, metadata, fmt.Errorf("failed to resolve service endpoint, %w", err) + } + + req.URL, err = url.Parse(endpoint.URL) + if err != nil { + return out, metadata, fmt.Errorf("failed to parse endpoint URL: %w", err) + } + + if len(awsmiddleware.GetSigningName(ctx)) == 0 { + signingName := endpoint.SigningName + if len(signingName) == 0 { + signingName = "pricing" + } + ctx = awsmiddleware.SetSigningName(ctx, signingName) + } + ctx = awsmiddleware.SetEndpointSource(ctx, endpoint.Source) + ctx = smithyhttp.SetHostnameImmutable(ctx, endpoint.HostnameImmutable) + ctx = awsmiddleware.SetSigningRegion(ctx, endpoint.SigningRegion) + ctx = awsmiddleware.SetPartitionID(ctx, endpoint.PartitionID) + return next.HandleSerialize(ctx, in) +} +func addResolveEndpointMiddleware(stack *middleware.Stack, o Options) error { + return stack.Serialize.Insert(&ResolveEndpoint{ + Resolver: o.EndpointResolver, + Options: o.EndpointOptions, + }, "OperationSerializer", middleware.Before) +} + +func removeResolveEndpointMiddleware(stack *middleware.Stack) error { + _, err := stack.Serialize.Remove((&ResolveEndpoint{}).ID()) + return err +} + +type wrappedEndpointResolver struct { + awsResolver aws.EndpointResolverWithOptions +} + +func (w *wrappedEndpointResolver) ResolveEndpoint(region string, options EndpointResolverOptions) (endpoint aws.Endpoint, err error) { + return w.awsResolver.ResolveEndpoint(ServiceID, region, options) +} + +type awsEndpointResolverAdaptor func(service, region string) (aws.Endpoint, error) + +func (a awsEndpointResolverAdaptor) ResolveEndpoint(service, region string, options ...interface{}) (aws.Endpoint, error) { + return a(service, region) +} + +var _ aws.EndpointResolverWithOptions = awsEndpointResolverAdaptor(nil) + +// withEndpointResolver returns an aws.EndpointResolverWithOptions that first delegates endpoint resolution to the awsResolver. +// If awsResolver returns aws.EndpointNotFoundError error, the v1 resolver middleware will swallow the error, +// and set an appropriate context flag such that fallback will occur when EndpointResolverV2 is invoked +// via its middleware. +// +// If another error (besides aws.EndpointNotFoundError) is returned, then that error will be propagated. +func withEndpointResolver(awsResolver aws.EndpointResolver, awsResolverWithOptions aws.EndpointResolverWithOptions) EndpointResolver { + var resolver aws.EndpointResolverWithOptions + + if awsResolverWithOptions != nil { + resolver = awsResolverWithOptions + } else if awsResolver != nil { + resolver = awsEndpointResolverAdaptor(awsResolver.ResolveEndpoint) + } + + return &wrappedEndpointResolver{ + awsResolver: resolver, + } +} + +func finalizeClientEndpointResolverOptions(options *Options) { + options.EndpointOptions.LogDeprecated = options.ClientLogMode.IsDeprecatedUsage() + + if len(options.EndpointOptions.ResolvedRegion) == 0 { + const fipsInfix = "-fips-" + const fipsPrefix = "fips-" + const fipsSuffix = "-fips" + + if strings.Contains(options.Region, fipsInfix) || + strings.Contains(options.Region, fipsPrefix) || + strings.Contains(options.Region, fipsSuffix) { + options.EndpointOptions.ResolvedRegion = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll( + options.Region, fipsInfix, "-"), fipsPrefix, ""), fipsSuffix, "") + options.EndpointOptions.UseFIPSEndpoint = aws.FIPSEndpointStateEnabled + } + } + +} + +func resolveEndpointResolverV2(options *Options) { + if options.EndpointResolverV2 == nil { + options.EndpointResolverV2 = NewDefaultEndpointResolverV2() + } +} + +// Utility function to aid with translating pseudo-regions to classical regions +// with the appropriate setting indicated by the pseudo-region +func mapPseudoRegion(pr string) (region string, fips aws.FIPSEndpointState) { + const fipsInfix = "-fips-" + const fipsPrefix = "fips-" + const fipsSuffix = "-fips" + + if strings.Contains(pr, fipsInfix) || + strings.Contains(pr, fipsPrefix) || + strings.Contains(pr, fipsSuffix) { + region = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll( + pr, fipsInfix, "-"), fipsPrefix, ""), fipsSuffix, "") + fips = aws.FIPSEndpointStateEnabled + } else { + region = pr + } + + return region, fips +} + +// builtInParameterResolver is the interface responsible for resolving BuiltIn +// values during the sourcing of EndpointParameters +type builtInParameterResolver interface { + ResolveBuiltIns(*EndpointParameters) error +} + +// builtInResolver resolves modeled BuiltIn values using only the members defined +// below. +type builtInResolver struct { + // The AWS region used to dispatch the request. + Region string + + // Sourced BuiltIn value in a historical enabled or disabled state. + UseDualStack aws.DualStackEndpointState + + // Sourced BuiltIn value in a historical enabled or disabled state. + UseFIPS aws.FIPSEndpointState + + // Base endpoint that can potentially be modified during Endpoint resolution. + Endpoint *string +} + +// Invoked at runtime to resolve BuiltIn Values. Only resolution code specific to +// each BuiltIn value is generated. +func (b *builtInResolver) ResolveBuiltIns(params *EndpointParameters) error { + + region, _ := mapPseudoRegion(b.Region) + if len(region) == 0 { + return fmt.Errorf("Could not resolve AWS::Region") + } else { + params.Region = aws.String(region) + } + if b.UseDualStack == aws.DualStackEndpointStateEnabled { + params.UseDualStack = aws.Bool(true) + } else { + params.UseDualStack = aws.Bool(false) + } + if b.UseFIPS == aws.FIPSEndpointStateEnabled { + params.UseFIPS = aws.Bool(true) + } else { + params.UseFIPS = aws.Bool(false) + } + params.Endpoint = b.Endpoint + return nil +} + +// EndpointParameters provides the parameters that influence how endpoints are +// resolved. +type EndpointParameters struct { + // The AWS region used to dispatch the request. + // + // Parameter is + // required. + // + // AWS::Region + Region *string + + // When true, use the dual-stack endpoint. If the configured endpoint does not + // support dual-stack, dispatching the request MAY return an error. + // + // Defaults to + // false if no value is provided. + // + // AWS::UseDualStack + UseDualStack *bool + + // When true, send this request to the FIPS-compliant regional endpoint. If the + // configured endpoint does not have a FIPS compliant endpoint, dispatching the + // request will return an error. + // + // Defaults to false if no value is + // provided. + // + // AWS::UseFIPS + UseFIPS *bool + + // Override the endpoint used to send this request + // + // Parameter is + // required. + // + // SDK::Endpoint + Endpoint *string +} + +// ValidateRequired validates required parameters are set. +func (p EndpointParameters) ValidateRequired() error { + if p.UseDualStack == nil { + return fmt.Errorf("parameter UseDualStack is required") + } + + if p.UseFIPS == nil { + return fmt.Errorf("parameter UseFIPS is required") + } + + return nil +} + +// WithDefaults returns a shallow copy of EndpointParameterswith default values +// applied to members where applicable. +func (p EndpointParameters) WithDefaults() EndpointParameters { + if p.UseDualStack == nil { + p.UseDualStack = ptr.Bool(false) + } + + if p.UseFIPS == nil { + p.UseFIPS = ptr.Bool(false) + } + return p +} + +// EndpointResolverV2 provides the interface for resolving service endpoints. +type EndpointResolverV2 interface { + // ResolveEndpoint attempts to resolve the endpoint with the provided options, + // returning the endpoint if found. Otherwise an error is returned. + ResolveEndpoint(ctx context.Context, params EndpointParameters) ( + smithyendpoints.Endpoint, error, + ) +} + +// resolver provides the implementation for resolving endpoints. +type resolver struct{} + +func NewDefaultEndpointResolverV2() EndpointResolverV2 { + return &resolver{} +} + +// ResolveEndpoint attempts to resolve the endpoint with the provided options, +// returning the endpoint if found. Otherwise an error is returned. +func (r *resolver) ResolveEndpoint( + ctx context.Context, params EndpointParameters, +) ( + endpoint smithyendpoints.Endpoint, err error, +) { + params = params.WithDefaults() + if err = params.ValidateRequired(); err != nil { + return endpoint, fmt.Errorf("endpoint parameters are not valid, %w", err) + } + _UseDualStack := *params.UseDualStack + _UseFIPS := *params.UseFIPS + + if exprVal := params.Endpoint; exprVal != nil { + _Endpoint := *exprVal + _ = _Endpoint + if _UseFIPS == true { + return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported") + } + if _UseDualStack == true { + return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported") + } + uriString := _Endpoint + + uri, err := url.Parse(uriString) + if err != nil { + return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) + } + + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + } + if exprVal := params.Region; exprVal != nil { + _Region := *exprVal + _ = _Region + if exprVal := awsrulesfn.GetPartition(_Region); exprVal != nil { + _PartitionResult := *exprVal + _ = _PartitionResult + if _UseFIPS == true { + if _UseDualStack == true { + if true == _PartitionResult.SupportsFIPS { + if true == _PartitionResult.SupportsDualStack { + uriString := func() string { + var out strings.Builder + out.WriteString("https://api.pricing-fips.") + out.WriteString(_Region) + out.WriteString(".") + out.WriteString(_PartitionResult.DualStackDnsSuffix) + return out.String() + }() + + uri, err := url.Parse(uriString) + if err != nil { + return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) + } + + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + } + } + return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both") + } + } + if _UseFIPS == true { + if true == _PartitionResult.SupportsFIPS { + uriString := func() string { + var out strings.Builder + out.WriteString("https://api.pricing-fips.") + out.WriteString(_Region) + out.WriteString(".") + out.WriteString(_PartitionResult.DnsSuffix) + return out.String() + }() + + uri, err := url.Parse(uriString) + if err != nil { + return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) + } + + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + } + return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS") + } + if _UseDualStack == true { + if true == _PartitionResult.SupportsDualStack { + uriString := func() string { + var out strings.Builder + out.WriteString("https://api.pricing.") + out.WriteString(_Region) + out.WriteString(".") + out.WriteString(_PartitionResult.DualStackDnsSuffix) + return out.String() + }() + + uri, err := url.Parse(uriString) + if err != nil { + return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) + } + + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + } + return endpoint, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack") + } + if "aws" == _PartitionResult.Name { + uriString := func() string { + var out strings.Builder + out.WriteString("https://api.pricing.") + out.WriteString(_Region) + out.WriteString(".amazonaws.com") + return out.String() + }() + + uri, err := url.Parse(uriString) + if err != nil { + return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) + } + + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + } + uriString := func() string { + var out strings.Builder + out.WriteString("https://api.pricing.") + out.WriteString(_Region) + out.WriteString(".") + out.WriteString(_PartitionResult.DnsSuffix) + return out.String() + }() + + uri, err := url.Parse(uriString) + if err != nil { + return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) + } + + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + } + return endpoint, fmt.Errorf("Endpoint resolution failed. Invalid operation or environment input.") + } + return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region") +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/generated.json b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/generated.json new file mode 100644 index 0000000000000..42569732e0f70 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/generated.json @@ -0,0 +1,34 @@ +{ + "dependencies": { + "github.com/aws/aws-sdk-go-v2": "v1.4.0", + "github.com/aws/aws-sdk-go-v2/internal/configsources": "v0.0.0-00010101000000-000000000000", + "github.com/aws/aws-sdk-go-v2/internal/endpoints/v2": "v2.0.0-00010101000000-000000000000", + "github.com/aws/smithy-go": "v1.4.0", + "github.com/google/go-cmp": "v0.5.4" + }, + "files": [ + "api_client.go", + "api_client_test.go", + "api_op_DescribeServices.go", + "api_op_GetAttributeValues.go", + "api_op_GetPriceListFileUrl.go", + "api_op_GetProducts.go", + "api_op_ListPriceLists.go", + "deserializers.go", + "doc.go", + "endpoints.go", + "endpoints_test.go", + "generated.json", + "internal/endpoints/endpoints.go", + "internal/endpoints/endpoints_test.go", + "protocol_test.go", + "serializers.go", + "types/enums.go", + "types/errors.go", + "types/types.go", + "validators.go" + ], + "go": "1.15", + "module": "github.com/aws/aws-sdk-go-v2/service/pricing", + "unstable": false +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/go_module_metadata.go new file mode 100644 index 0000000000000..a2080f67a991b --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/go_module_metadata.go @@ -0,0 +1,6 @@ +// Code generated by internal/repotools/cmd/updatemodulemeta DO NOT EDIT. + +package pricing + +// goModuleVersion is the tagged release for this module +const goModuleVersion = "1.21.6" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/internal/endpoints/endpoints.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/internal/endpoints/endpoints.go new file mode 100644 index 0000000000000..430f908693b75 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/internal/endpoints/endpoints.go @@ -0,0 +1,319 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package endpoints + +import ( + "github.com/aws/aws-sdk-go-v2/aws" + endpoints "github.com/aws/aws-sdk-go-v2/internal/endpoints/v2" + "github.com/aws/smithy-go/logging" + "regexp" +) + +// Options is the endpoint resolver configuration options +type Options struct { + // Logger is a logging implementation that log events should be sent to. + Logger logging.Logger + + // LogDeprecated indicates that deprecated endpoints should be logged to the + // provided logger. + LogDeprecated bool + + // ResolvedRegion is used to override the region to be resolved, rather then the + // using the value passed to the ResolveEndpoint method. This value is used by the + // SDK to translate regions like fips-us-east-1 or us-east-1-fips to an alternative + // name. You must not set this value directly in your application. + ResolvedRegion string + + // DisableHTTPS informs the resolver to return an endpoint that does not use the + // HTTPS scheme. + DisableHTTPS bool + + // UseDualStackEndpoint specifies the resolver must resolve a dual-stack endpoint. + UseDualStackEndpoint aws.DualStackEndpointState + + // UseFIPSEndpoint specifies the resolver must resolve a FIPS endpoint. + UseFIPSEndpoint aws.FIPSEndpointState +} + +func (o Options) GetResolvedRegion() string { + return o.ResolvedRegion +} + +func (o Options) GetDisableHTTPS() bool { + return o.DisableHTTPS +} + +func (o Options) GetUseDualStackEndpoint() aws.DualStackEndpointState { + return o.UseDualStackEndpoint +} + +func (o Options) GetUseFIPSEndpoint() aws.FIPSEndpointState { + return o.UseFIPSEndpoint +} + +func transformToSharedOptions(options Options) endpoints.Options { + return endpoints.Options{ + Logger: options.Logger, + LogDeprecated: options.LogDeprecated, + ResolvedRegion: options.ResolvedRegion, + DisableHTTPS: options.DisableHTTPS, + UseDualStackEndpoint: options.UseDualStackEndpoint, + UseFIPSEndpoint: options.UseFIPSEndpoint, + } +} + +// Resolver Pricing endpoint resolver +type Resolver struct { + partitions endpoints.Partitions +} + +// ResolveEndpoint resolves the service endpoint for the given region and options +func (r *Resolver) ResolveEndpoint(region string, options Options) (endpoint aws.Endpoint, err error) { + if len(region) == 0 { + return endpoint, &aws.MissingRegionError{} + } + + opt := transformToSharedOptions(options) + return r.partitions.ResolveEndpoint(region, opt) +} + +// New returns a new Resolver +func New() *Resolver { + return &Resolver{ + partitions: defaultPartitions, + } +} + +var partitionRegexp = struct { + Aws *regexp.Regexp + AwsCn *regexp.Regexp + AwsIso *regexp.Regexp + AwsIsoB *regexp.Regexp + AwsIsoE *regexp.Regexp + AwsIsoF *regexp.Regexp + AwsUsGov *regexp.Regexp +}{ + + Aws: regexp.MustCompile("^(us|eu|ap|sa|ca|me|af|il)\\-\\w+\\-\\d+$"), + AwsCn: regexp.MustCompile("^cn\\-\\w+\\-\\d+$"), + AwsIso: regexp.MustCompile("^us\\-iso\\-\\w+\\-\\d+$"), + AwsIsoB: regexp.MustCompile("^us\\-isob\\-\\w+\\-\\d+$"), + AwsIsoE: regexp.MustCompile("^eu\\-isoe\\-\\w+\\-\\d+$"), + AwsIsoF: regexp.MustCompile("^us\\-isof\\-\\w+\\-\\d+$"), + AwsUsGov: regexp.MustCompile("^us\\-gov\\-\\w+\\-\\d+$"), +} + +var defaultPartitions = endpoints.Partitions{ + { + ID: "aws", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.DualStackVariant, + }: { + Hostname: "api.pricing.{region}.api.aws", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + CredentialScope: endpoints.CredentialScope{ + Service: "pricing", + }, + }, + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "api.pricing-fips.{region}.amazonaws.com", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + CredentialScope: endpoints.CredentialScope{ + Service: "pricing", + }, + }, + { + Variant: endpoints.FIPSVariant | endpoints.DualStackVariant, + }: { + Hostname: "api.pricing-fips.{region}.api.aws", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + CredentialScope: endpoints.CredentialScope{ + Service: "pricing", + }, + }, + { + Variant: 0, + }: { + Hostname: "api.pricing.{region}.amazonaws.com", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + CredentialScope: endpoints.CredentialScope{ + Service: "pricing", + }, + }, + }, + RegionRegex: partitionRegexp.Aws, + IsRegionalized: true, + Endpoints: endpoints.Endpoints{ + endpoints.EndpointKey{ + Region: "ap-south-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "eu-central-1", + }: endpoints.Endpoint{}, + endpoints.EndpointKey{ + Region: "us-east-1", + }: endpoints.Endpoint{}, + }, + }, + { + ID: "aws-cn", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.DualStackVariant, + }: { + Hostname: "api.pricing.{region}.api.amazonwebservices.com.cn", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "api.pricing-fips.{region}.amazonaws.com.cn", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant | endpoints.DualStackVariant, + }: { + Hostname: "api.pricing-fips.{region}.api.amazonwebservices.com.cn", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "api.pricing.{region}.amazonaws.com.cn", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsCn, + IsRegionalized: true, + }, + { + ID: "aws-iso", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "api.pricing-fips.{region}.c2s.ic.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "api.pricing.{region}.c2s.ic.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsIso, + IsRegionalized: true, + }, + { + ID: "aws-iso-b", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "api.pricing-fips.{region}.sc2s.sgov.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "api.pricing.{region}.sc2s.sgov.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsIsoB, + IsRegionalized: true, + }, + { + ID: "aws-iso-e", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "api.pricing-fips.{region}.cloud.adc-e.uk", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "api.pricing.{region}.cloud.adc-e.uk", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsIsoE, + IsRegionalized: true, + }, + { + ID: "aws-iso-f", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "api.pricing-fips.{region}.csp.hci.ic.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "api.pricing.{region}.csp.hci.ic.gov", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsIsoF, + IsRegionalized: true, + }, + { + ID: "aws-us-gov", + Defaults: map[endpoints.DefaultKey]endpoints.Endpoint{ + { + Variant: endpoints.DualStackVariant, + }: { + Hostname: "api.pricing.{region}.api.aws", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant, + }: { + Hostname: "api.pricing-fips.{region}.amazonaws.com", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: endpoints.FIPSVariant | endpoints.DualStackVariant, + }: { + Hostname: "api.pricing-fips.{region}.api.aws", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + { + Variant: 0, + }: { + Hostname: "api.pricing.{region}.amazonaws.com", + Protocols: []string{"https"}, + SignatureVersions: []string{"v4"}, + }, + }, + RegionRegex: partitionRegexp.AwsUsGov, + IsRegionalized: true, + }, +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/serializers.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/serializers.go new file mode 100644 index 0000000000000..3eaeadba5bc1e --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/serializers.go @@ -0,0 +1,468 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package pricing + +import ( + "bytes" + "context" + "fmt" + "github.com/aws/aws-sdk-go-v2/service/pricing/types" + smithy "github.com/aws/smithy-go" + "github.com/aws/smithy-go/encoding/httpbinding" + smithyjson "github.com/aws/smithy-go/encoding/json" + "github.com/aws/smithy-go/middleware" + smithytime "github.com/aws/smithy-go/time" + smithyhttp "github.com/aws/smithy-go/transport/http" + "path" +) + +type awsAwsjson11_serializeOpDescribeServices struct { +} + +func (*awsAwsjson11_serializeOpDescribeServices) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsjson11_serializeOpDescribeServices) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DescribeServicesInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-amz-json-1.1") + httpBindingEncoder.SetHeader("X-Amz-Target").String("AWSPriceListService.DescribeServices") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsAwsjson11_serializeOpDocumentDescribeServicesInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + return next.HandleSerialize(ctx, in) +} + +type awsAwsjson11_serializeOpGetAttributeValues struct { +} + +func (*awsAwsjson11_serializeOpGetAttributeValues) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsjson11_serializeOpGetAttributeValues) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*GetAttributeValuesInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-amz-json-1.1") + httpBindingEncoder.SetHeader("X-Amz-Target").String("AWSPriceListService.GetAttributeValues") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsAwsjson11_serializeOpDocumentGetAttributeValuesInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + return next.HandleSerialize(ctx, in) +} + +type awsAwsjson11_serializeOpGetPriceListFileUrl struct { +} + +func (*awsAwsjson11_serializeOpGetPriceListFileUrl) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsjson11_serializeOpGetPriceListFileUrl) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*GetPriceListFileUrlInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-amz-json-1.1") + httpBindingEncoder.SetHeader("X-Amz-Target").String("AWSPriceListService.GetPriceListFileUrl") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsAwsjson11_serializeOpDocumentGetPriceListFileUrlInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + return next.HandleSerialize(ctx, in) +} + +type awsAwsjson11_serializeOpGetProducts struct { +} + +func (*awsAwsjson11_serializeOpGetProducts) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsjson11_serializeOpGetProducts) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*GetProductsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-amz-json-1.1") + httpBindingEncoder.SetHeader("X-Amz-Target").String("AWSPriceListService.GetProducts") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsAwsjson11_serializeOpDocumentGetProductsInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + return next.HandleSerialize(ctx, in) +} + +type awsAwsjson11_serializeOpListPriceLists struct { +} + +func (*awsAwsjson11_serializeOpListPriceLists) ID() string { + return "OperationSerializer" +} + +func (m *awsAwsjson11_serializeOpListPriceLists) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListPriceListsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + operationPath := "/" + if len(request.Request.URL.Path) == 0 { + request.Request.URL.Path = operationPath + } else { + request.Request.URL.Path = path.Join(request.Request.URL.Path, operationPath) + if request.Request.URL.Path != "/" && operationPath[len(operationPath)-1] == '/' { + request.Request.URL.Path += "/" + } + } + request.Request.Method = "POST" + httpBindingEncoder, err := httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + httpBindingEncoder.SetHeader("Content-Type").String("application/x-amz-json-1.1") + httpBindingEncoder.SetHeader("X-Amz-Target").String("AWSPriceListService.ListPriceLists") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsAwsjson11_serializeOpDocumentListPriceListsInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = httpBindingEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + return next.HandleSerialize(ctx, in) +} +func awsAwsjson11_serializeDocumentFilter(v *types.Filter, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Field != nil { + ok := object.Key("Field") + ok.String(*v.Field) + } + + if len(v.Type) > 0 { + ok := object.Key("Type") + ok.String(string(v.Type)) + } + + if v.Value != nil { + ok := object.Key("Value") + ok.String(*v.Value) + } + + return nil +} + +func awsAwsjson11_serializeDocumentFilters(v []types.Filter, value smithyjson.Value) error { + array := value.Array() + defer array.Close() + + for i := range v { + av := array.Value() + if err := awsAwsjson11_serializeDocumentFilter(&v[i], av); err != nil { + return err + } + } + return nil +} + +func awsAwsjson11_serializeOpDocumentDescribeServicesInput(v *DescribeServicesInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.FormatVersion != nil { + ok := object.Key("FormatVersion") + ok.String(*v.FormatVersion) + } + + if v.MaxResults != nil { + ok := object.Key("MaxResults") + ok.Integer(*v.MaxResults) + } + + if v.NextToken != nil { + ok := object.Key("NextToken") + ok.String(*v.NextToken) + } + + if v.ServiceCode != nil { + ok := object.Key("ServiceCode") + ok.String(*v.ServiceCode) + } + + return nil +} + +func awsAwsjson11_serializeOpDocumentGetAttributeValuesInput(v *GetAttributeValuesInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.AttributeName != nil { + ok := object.Key("AttributeName") + ok.String(*v.AttributeName) + } + + if v.MaxResults != nil { + ok := object.Key("MaxResults") + ok.Integer(*v.MaxResults) + } + + if v.NextToken != nil { + ok := object.Key("NextToken") + ok.String(*v.NextToken) + } + + if v.ServiceCode != nil { + ok := object.Key("ServiceCode") + ok.String(*v.ServiceCode) + } + + return nil +} + +func awsAwsjson11_serializeOpDocumentGetPriceListFileUrlInput(v *GetPriceListFileUrlInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.FileFormat != nil { + ok := object.Key("FileFormat") + ok.String(*v.FileFormat) + } + + if v.PriceListArn != nil { + ok := object.Key("PriceListArn") + ok.String(*v.PriceListArn) + } + + return nil +} + +func awsAwsjson11_serializeOpDocumentGetProductsInput(v *GetProductsInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Filters != nil { + ok := object.Key("Filters") + if err := awsAwsjson11_serializeDocumentFilters(v.Filters, ok); err != nil { + return err + } + } + + if v.FormatVersion != nil { + ok := object.Key("FormatVersion") + ok.String(*v.FormatVersion) + } + + if v.MaxResults != nil { + ok := object.Key("MaxResults") + ok.Integer(*v.MaxResults) + } + + if v.NextToken != nil { + ok := object.Key("NextToken") + ok.String(*v.NextToken) + } + + if v.ServiceCode != nil { + ok := object.Key("ServiceCode") + ok.String(*v.ServiceCode) + } + + return nil +} + +func awsAwsjson11_serializeOpDocumentListPriceListsInput(v *ListPriceListsInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.CurrencyCode != nil { + ok := object.Key("CurrencyCode") + ok.String(*v.CurrencyCode) + } + + if v.EffectiveDate != nil { + ok := object.Key("EffectiveDate") + ok.Double(smithytime.FormatEpochSeconds(*v.EffectiveDate)) + } + + if v.MaxResults != nil { + ok := object.Key("MaxResults") + ok.Integer(*v.MaxResults) + } + + if v.NextToken != nil { + ok := object.Key("NextToken") + ok.String(*v.NextToken) + } + + if v.RegionCode != nil { + ok := object.Key("RegionCode") + ok.String(*v.RegionCode) + } + + if v.ServiceCode != nil { + ok := object.Key("ServiceCode") + ok.String(*v.ServiceCode) + } + + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/types/enums.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/types/enums.go new file mode 100644 index 0000000000000..6a3160d0e1e92 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/types/enums.go @@ -0,0 +1,19 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package types + +type FilterType string + +// Enum values for FilterType +const ( + FilterTypeTermMatch FilterType = "TERM_MATCH" +) + +// Values returns all known values for FilterType. Note that this can be expanded +// in the future, and so it is only as up to date as the client. The ordering of +// this slice is not guaranteed to be stable across updates. +func (FilterType) Values() []FilterType { + return []FilterType{ + "TERM_MATCH", + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/types/errors.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/types/errors.go new file mode 100644 index 0000000000000..30bf9391a4b2d --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/types/errors.go @@ -0,0 +1,165 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package types + +import ( + "fmt" + smithy "github.com/aws/smithy-go" +) + +// General authentication failure. The request wasn't signed correctly. +type AccessDeniedException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *AccessDeniedException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *AccessDeniedException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *AccessDeniedException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "AccessDeniedException" + } + return *e.ErrorCodeOverride +} +func (e *AccessDeniedException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// The pagination token expired. Try again without a pagination token. +type ExpiredNextTokenException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *ExpiredNextTokenException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *ExpiredNextTokenException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *ExpiredNextTokenException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "ExpiredNextTokenException" + } + return *e.ErrorCodeOverride +} +func (e *ExpiredNextTokenException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// An error on the server occurred during the processing of your request. Try +// again later. +type InternalErrorException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *InternalErrorException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *InternalErrorException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *InternalErrorException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "InternalErrorException" + } + return *e.ErrorCodeOverride +} +func (e *InternalErrorException) ErrorFault() smithy.ErrorFault { return smithy.FaultServer } + +// The pagination token is invalid. Try again without a pagination token. +type InvalidNextTokenException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *InvalidNextTokenException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *InvalidNextTokenException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *InvalidNextTokenException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "InvalidNextTokenException" + } + return *e.ErrorCodeOverride +} +func (e *InvalidNextTokenException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// One or more parameters had an invalid value. +type InvalidParameterException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *InvalidParameterException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *InvalidParameterException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *InvalidParameterException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "InvalidParameterException" + } + return *e.ErrorCodeOverride +} +func (e *InvalidParameterException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// The requested resource can't be found. +type NotFoundException struct { + Message *string + + ErrorCodeOverride *string + + noSmithyDocumentSerde +} + +func (e *NotFoundException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *NotFoundException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *NotFoundException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "NotFoundException" + } + return *e.ErrorCodeOverride +} +func (e *NotFoundException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/types/types.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/types/types.go new file mode 100644 index 0000000000000..3d358ed62c140 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/types/types.go @@ -0,0 +1,97 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package types + +import ( + smithydocument "github.com/aws/smithy-go/document" +) + +// The values of a given attribute, such as Throughput Optimized HDD or +// Provisioned IOPS for the Amazon EC2 volumeType attribute. +type AttributeValue struct { + + // The specific value of an attributeName . + Value *string + + noSmithyDocumentSerde +} + +// The constraints that you want all returned products to match. +type Filter struct { + + // The product metadata field that you want to filter on. You can filter by just + // the service code to see all products for a specific service, filter by just the + // attribute name to see a specific attribute for multiple services, or use both a + // service code and an attribute name to retrieve only products that match both + // fields. Valid values include: ServiceCode , and all attribute names For example, + // you can filter by the AmazonEC2 service code and the volumeType attribute name + // to get the prices for only Amazon EC2 volumes. + // + // This member is required. + Field *string + + // The type of filter that you want to use. Valid values are: TERM_MATCH . + // TERM_MATCH returns only products that match both the given filter field and the + // given value. + // + // This member is required. + Type FilterType + + // The service code or attribute value that you want to filter by. If you're + // filtering by service code this is the actual service code, such as AmazonEC2 . + // If you're filtering by attribute name, this is the attribute value that you want + // the returned products to match, such as a Provisioned IOPS volume. + // + // This member is required. + Value *string + + noSmithyDocumentSerde +} + +// This feature is in preview release and is subject to change. Your use of Amazon +// Web Services Price List API is subject to the Beta Service Participation terms +// of the Amazon Web Services Service Terms (https://aws.amazon.com/service-terms/) +// (Section 1.10). This is the type of price list references that match your +// request. +type PriceList struct { + + // The three alphabetical character ISO-4217 currency code the Price List files + // are denominated in. + CurrencyCode *string + + // The format you want to retrieve your Price List files. The FileFormat can be + // obtained from the ListPriceList (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_ListPriceLists.html) + // response. + FileFormats []string + + // The unique identifier that maps to where your Price List files are located. + // PriceListArn can be obtained from the ListPriceList (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_ListPriceLists.html) + // response. + PriceListArn *string + + // This is used to filter the Price List by Amazon Web Services Region. For + // example, to get the price list only for the US East (N. Virginia) Region, use + // us-east-1 . If nothing is specified, you retrieve price lists for all applicable + // Regions. The available RegionCode list can be retrieved from GetAttributeValues (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_GetAttributeValues.html) + // API. + RegionCode *string + + noSmithyDocumentSerde +} + +// The metadata for a service, such as the service code and available attribute +// names. +type Service struct { + + // The code for the Amazon Web Services service. + // + // This member is required. + ServiceCode *string + + // The attributes that are available for this service. + AttributeNames []string + + noSmithyDocumentSerde +} + +type noSmithyDocumentSerde = smithydocument.NoSerde diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/validators.go b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/validators.go new file mode 100644 index 0000000000000..434770aa24269 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/pricing/validators.go @@ -0,0 +1,222 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package pricing + +import ( + "context" + "fmt" + "github.com/aws/aws-sdk-go-v2/service/pricing/types" + smithy "github.com/aws/smithy-go" + "github.com/aws/smithy-go/middleware" +) + +type validateOpGetAttributeValues struct { +} + +func (*validateOpGetAttributeValues) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpGetAttributeValues) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*GetAttributeValuesInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpGetAttributeValuesInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpGetPriceListFileUrl struct { +} + +func (*validateOpGetPriceListFileUrl) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpGetPriceListFileUrl) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*GetPriceListFileUrlInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpGetPriceListFileUrlInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpGetProducts struct { +} + +func (*validateOpGetProducts) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpGetProducts) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*GetProductsInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpGetProductsInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpListPriceLists struct { +} + +func (*validateOpListPriceLists) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpListPriceLists) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*ListPriceListsInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpListPriceListsInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +func addOpGetAttributeValuesValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpGetAttributeValues{}, middleware.After) +} + +func addOpGetPriceListFileUrlValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpGetPriceListFileUrl{}, middleware.After) +} + +func addOpGetProductsValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpGetProducts{}, middleware.After) +} + +func addOpListPriceListsValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpListPriceLists{}, middleware.After) +} + +func validateFilter(v *types.Filter) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "Filter"} + if len(v.Type) == 0 { + invalidParams.Add(smithy.NewErrParamRequired("Type")) + } + if v.Field == nil { + invalidParams.Add(smithy.NewErrParamRequired("Field")) + } + if v.Value == nil { + invalidParams.Add(smithy.NewErrParamRequired("Value")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateFilters(v []types.Filter) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "Filters"} + for i := range v { + if err := validateFilter(&v[i]); err != nil { + invalidParams.AddNested(fmt.Sprintf("[%d]", i), err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpGetAttributeValuesInput(v *GetAttributeValuesInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "GetAttributeValuesInput"} + if v.ServiceCode == nil { + invalidParams.Add(smithy.NewErrParamRequired("ServiceCode")) + } + if v.AttributeName == nil { + invalidParams.Add(smithy.NewErrParamRequired("AttributeName")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpGetPriceListFileUrlInput(v *GetPriceListFileUrlInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "GetPriceListFileUrlInput"} + if v.PriceListArn == nil { + invalidParams.Add(smithy.NewErrParamRequired("PriceListArn")) + } + if v.FileFormat == nil { + invalidParams.Add(smithy.NewErrParamRequired("FileFormat")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpGetProductsInput(v *GetProductsInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "GetProductsInput"} + if v.ServiceCode == nil { + invalidParams.Add(smithy.NewErrParamRequired("ServiceCode")) + } + if v.Filters != nil { + if err := validateFilters(v.Filters); err != nil { + invalidParams.AddNested("Filters", err.(smithy.InvalidParamsError)) + } + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpListPriceListsInput(v *ListPriceListsInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "ListPriceListsInput"} + if v.ServiceCode == nil { + invalidParams.Add(smithy.NewErrParamRequired("ServiceCode")) + } + if v.EffectiveDate == nil { + invalidParams.Add(smithy.NewErrParamRequired("EffectiveDate")) + } + if v.CurrencyCode == nil { + invalidParams.Add(smithy.NewErrParamRequired("CurrencyCode")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} diff --git a/vendor/github.com/aws/aws-sdk-go/service/pricing/api.go b/vendor/github.com/aws/aws-sdk-go/service/pricing/api.go deleted file mode 100644 index 18f41ad9b68cd..0000000000000 --- a/vendor/github.com/aws/aws-sdk-go/service/pricing/api.go +++ /dev/null @@ -1,2182 +0,0 @@ -// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. - -package pricing - -import ( - "fmt" - "time" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awsutil" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/private/protocol" -) - -const opDescribeServices = "DescribeServices" - -// DescribeServicesRequest generates a "aws/request.Request" representing the -// client's request for the DescribeServices 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 DescribeServices for more information on using the DescribeServices -// 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 DescribeServicesRequest method. -// req, resp := client.DescribeServicesRequest(params) -// -// err := req.Send() -// if err == nil { // resp is now filled -// fmt.Println(resp) -// } -// -// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/DescribeServices -func (c *Pricing) DescribeServicesRequest(input *DescribeServicesInput) (req *request.Request, output *DescribeServicesOutput) { - op := &request.Operation{ - Name: opDescribeServices, - HTTPMethod: "POST", - HTTPPath: "/", - Paginator: &request.Paginator{ - InputTokens: []string{"NextToken"}, - OutputTokens: []string{"NextToken"}, - LimitToken: "MaxResults", - TruncationToken: "", - }, - } - - if input == nil { - input = &DescribeServicesInput{} - } - - output = &DescribeServicesOutput{} - req = c.newRequest(op, input, output) - return -} - -// DescribeServices API operation for AWS Price List Service. -// -// Returns the metadata for one service or a list of the metadata for all services. -// Use this without a service code to get the service codes for all services. -// Use it with a service code, such as AmazonEC2, to get information specific -// to that service, such as the attribute names available for that service. -// For example, some of the attribute names available for EC2 are volumeType, -// maxIopsVolume, operation, locationType, and instanceCapacity10xlarge. -// -// 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 Price List Service's -// API operation DescribeServices for usage and error information. -// -// Returned Error Types: -// -// - InvalidParameterException -// One or more parameters had an invalid value. -// -// - InvalidNextTokenException -// The pagination token is invalid. Try again without a pagination token. -// -// - NotFoundException -// The requested resource can't be found. -// -// - InternalErrorException -// An error on the server occurred during the processing of your request. Try -// again later. -// -// - ThrottlingException -// You've made too many requests exceeding service quotas. -// -// - ExpiredNextTokenException -// The pagination token expired. Try again without a pagination token. -// -// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/DescribeServices -func (c *Pricing) DescribeServices(input *DescribeServicesInput) (*DescribeServicesOutput, error) { - req, out := c.DescribeServicesRequest(input) - return out, req.Send() -} - -// DescribeServicesWithContext is the same as DescribeServices with the addition of -// the ability to pass a context and additional request options. -// -// See DescribeServices 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 *Pricing) DescribeServicesWithContext(ctx aws.Context, input *DescribeServicesInput, opts ...request.Option) (*DescribeServicesOutput, error) { - req, out := c.DescribeServicesRequest(input) - req.SetContext(ctx) - req.ApplyOptions(opts...) - return out, req.Send() -} - -// DescribeServicesPages iterates over the pages of a DescribeServices operation, -// calling the "fn" function with the response data for each page. To stop -// iterating, return false from the fn function. -// -// See DescribeServices 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 DescribeServices operation. -// pageNum := 0 -// err := client.DescribeServicesPages(params, -// func(page *pricing.DescribeServicesOutput, lastPage bool) bool { -// pageNum++ -// fmt.Println(page) -// return pageNum <= 3 -// }) -func (c *Pricing) DescribeServicesPages(input *DescribeServicesInput, fn func(*DescribeServicesOutput, bool) bool) error { - return c.DescribeServicesPagesWithContext(aws.BackgroundContext(), input, fn) -} - -// DescribeServicesPagesWithContext same as DescribeServicesPages 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 *Pricing) DescribeServicesPagesWithContext(ctx aws.Context, input *DescribeServicesInput, fn func(*DescribeServicesOutput, bool) bool, opts ...request.Option) error { - p := request.Pagination{ - NewRequest: func() (*request.Request, error) { - var inCpy *DescribeServicesInput - if input != nil { - tmp := *input - inCpy = &tmp - } - req, _ := c.DescribeServicesRequest(inCpy) - req.SetContext(ctx) - req.ApplyOptions(opts...) - return req, nil - }, - } - - for p.Next() { - if !fn(p.Page().(*DescribeServicesOutput), !p.HasNextPage()) { - break - } - } - - return p.Err() -} - -const opGetAttributeValues = "GetAttributeValues" - -// GetAttributeValuesRequest generates a "aws/request.Request" representing the -// client's request for the GetAttributeValues 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 GetAttributeValues for more information on using the GetAttributeValues -// 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 GetAttributeValuesRequest method. -// req, resp := client.GetAttributeValuesRequest(params) -// -// err := req.Send() -// if err == nil { // resp is now filled -// fmt.Println(resp) -// } -// -// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/GetAttributeValues -func (c *Pricing) GetAttributeValuesRequest(input *GetAttributeValuesInput) (req *request.Request, output *GetAttributeValuesOutput) { - op := &request.Operation{ - Name: opGetAttributeValues, - HTTPMethod: "POST", - HTTPPath: "/", - Paginator: &request.Paginator{ - InputTokens: []string{"NextToken"}, - OutputTokens: []string{"NextToken"}, - LimitToken: "MaxResults", - TruncationToken: "", - }, - } - - if input == nil { - input = &GetAttributeValuesInput{} - } - - output = &GetAttributeValuesOutput{} - req = c.newRequest(op, input, output) - return -} - -// GetAttributeValues API operation for AWS Price List Service. -// -// Returns a list of attribute values. Attributes are similar to the details -// in a Price List API offer file. For a list of available attributes, see Offer -// File Definitions (https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/reading-an-offer.html#pps-defs) -// in the Billing and Cost Management User Guide (https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/billing-what-is.html). -// -// 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 Price List Service's -// API operation GetAttributeValues for usage and error information. -// -// Returned Error Types: -// -// - InvalidParameterException -// One or more parameters had an invalid value. -// -// - InvalidNextTokenException -// The pagination token is invalid. Try again without a pagination token. -// -// - NotFoundException -// The requested resource can't be found. -// -// - InternalErrorException -// An error on the server occurred during the processing of your request. Try -// again later. -// -// - ThrottlingException -// You've made too many requests exceeding service quotas. -// -// - ExpiredNextTokenException -// The pagination token expired. Try again without a pagination token. -// -// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/GetAttributeValues -func (c *Pricing) GetAttributeValues(input *GetAttributeValuesInput) (*GetAttributeValuesOutput, error) { - req, out := c.GetAttributeValuesRequest(input) - return out, req.Send() -} - -// GetAttributeValuesWithContext is the same as GetAttributeValues with the addition of -// the ability to pass a context and additional request options. -// -// See GetAttributeValues 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 *Pricing) GetAttributeValuesWithContext(ctx aws.Context, input *GetAttributeValuesInput, opts ...request.Option) (*GetAttributeValuesOutput, error) { - req, out := c.GetAttributeValuesRequest(input) - req.SetContext(ctx) - req.ApplyOptions(opts...) - return out, req.Send() -} - -// GetAttributeValuesPages iterates over the pages of a GetAttributeValues operation, -// calling the "fn" function with the response data for each page. To stop -// iterating, return false from the fn function. -// -// See GetAttributeValues 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 GetAttributeValues operation. -// pageNum := 0 -// err := client.GetAttributeValuesPages(params, -// func(page *pricing.GetAttributeValuesOutput, lastPage bool) bool { -// pageNum++ -// fmt.Println(page) -// return pageNum <= 3 -// }) -func (c *Pricing) GetAttributeValuesPages(input *GetAttributeValuesInput, fn func(*GetAttributeValuesOutput, bool) bool) error { - return c.GetAttributeValuesPagesWithContext(aws.BackgroundContext(), input, fn) -} - -// GetAttributeValuesPagesWithContext same as GetAttributeValuesPages 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 *Pricing) GetAttributeValuesPagesWithContext(ctx aws.Context, input *GetAttributeValuesInput, fn func(*GetAttributeValuesOutput, bool) bool, opts ...request.Option) error { - p := request.Pagination{ - NewRequest: func() (*request.Request, error) { - var inCpy *GetAttributeValuesInput - if input != nil { - tmp := *input - inCpy = &tmp - } - req, _ := c.GetAttributeValuesRequest(inCpy) - req.SetContext(ctx) - req.ApplyOptions(opts...) - return req, nil - }, - } - - for p.Next() { - if !fn(p.Page().(*GetAttributeValuesOutput), !p.HasNextPage()) { - break - } - } - - return p.Err() -} - -const opGetPriceListFileUrl = "GetPriceListFileUrl" - -// GetPriceListFileUrlRequest generates a "aws/request.Request" representing the -// client's request for the GetPriceListFileUrl 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 GetPriceListFileUrl for more information on using the GetPriceListFileUrl -// 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 GetPriceListFileUrlRequest method. -// req, resp := client.GetPriceListFileUrlRequest(params) -// -// err := req.Send() -// if err == nil { // resp is now filled -// fmt.Println(resp) -// } -// -// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/GetPriceListFileUrl -func (c *Pricing) GetPriceListFileUrlRequest(input *GetPriceListFileUrlInput) (req *request.Request, output *GetPriceListFileUrlOutput) { - op := &request.Operation{ - Name: opGetPriceListFileUrl, - HTTPMethod: "POST", - HTTPPath: "/", - } - - if input == nil { - input = &GetPriceListFileUrlInput{} - } - - output = &GetPriceListFileUrlOutput{} - req = c.newRequest(op, input, output) - return -} - -// GetPriceListFileUrl API operation for AWS Price List Service. -// -// This feature is in preview release and is subject to change. Your use of -// Amazon Web Services Price List API is subject to the Beta Service Participation -// terms of the Amazon Web Services Service Terms (https://aws.amazon.com/service-terms/) -// (Section 1.10). -// -// This returns the URL that you can retrieve your Price List file from. This -// URL is based on the PriceListArn and FileFormat that you retrieve from the -// ListPriceLists (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_ListPriceLists.html) -// response. -// -// 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 Price List Service's -// API operation GetPriceListFileUrl for usage and error information. -// -// Returned Error Types: -// -// - InvalidParameterException -// One or more parameters had an invalid value. -// -// - NotFoundException -// The requested resource can't be found. -// -// - AccessDeniedException -// General authentication failure. The request wasn't signed correctly. -// -// - InternalErrorException -// An error on the server occurred during the processing of your request. Try -// again later. -// -// - ThrottlingException -// You've made too many requests exceeding service quotas. -// -// - ResourceNotFoundException -// The requested resource can't be found. -// -// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/GetPriceListFileUrl -func (c *Pricing) GetPriceListFileUrl(input *GetPriceListFileUrlInput) (*GetPriceListFileUrlOutput, error) { - req, out := c.GetPriceListFileUrlRequest(input) - return out, req.Send() -} - -// GetPriceListFileUrlWithContext is the same as GetPriceListFileUrl with the addition of -// the ability to pass a context and additional request options. -// -// See GetPriceListFileUrl 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 *Pricing) GetPriceListFileUrlWithContext(ctx aws.Context, input *GetPriceListFileUrlInput, opts ...request.Option) (*GetPriceListFileUrlOutput, error) { - req, out := c.GetPriceListFileUrlRequest(input) - req.SetContext(ctx) - req.ApplyOptions(opts...) - return out, req.Send() -} - -const opGetProducts = "GetProducts" - -// GetProductsRequest generates a "aws/request.Request" representing the -// client's request for the GetProducts 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 GetProducts for more information on using the GetProducts -// 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 GetProductsRequest method. -// req, resp := client.GetProductsRequest(params) -// -// err := req.Send() -// if err == nil { // resp is now filled -// fmt.Println(resp) -// } -// -// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/GetProducts -func (c *Pricing) GetProductsRequest(input *GetProductsInput) (req *request.Request, output *GetProductsOutput) { - op := &request.Operation{ - Name: opGetProducts, - HTTPMethod: "POST", - HTTPPath: "/", - Paginator: &request.Paginator{ - InputTokens: []string{"NextToken"}, - OutputTokens: []string{"NextToken"}, - LimitToken: "MaxResults", - TruncationToken: "", - }, - } - - if input == nil { - input = &GetProductsInput{} - } - - output = &GetProductsOutput{} - req = c.newRequest(op, input, output) - return -} - -// GetProducts API operation for AWS Price List Service. -// -// Returns a list of all products that match the filter criteria. -// -// 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 Price List Service's -// API operation GetProducts for usage and error information. -// -// Returned Error Types: -// -// - InvalidParameterException -// One or more parameters had an invalid value. -// -// - InvalidNextTokenException -// The pagination token is invalid. Try again without a pagination token. -// -// - NotFoundException -// The requested resource can't be found. -// -// - InternalErrorException -// An error on the server occurred during the processing of your request. Try -// again later. -// -// - ThrottlingException -// You've made too many requests exceeding service quotas. -// -// - ExpiredNextTokenException -// The pagination token expired. Try again without a pagination token. -// -// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/GetProducts -func (c *Pricing) GetProducts(input *GetProductsInput) (*GetProductsOutput, error) { - req, out := c.GetProductsRequest(input) - return out, req.Send() -} - -// GetProductsWithContext is the same as GetProducts with the addition of -// the ability to pass a context and additional request options. -// -// See GetProducts 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 *Pricing) GetProductsWithContext(ctx aws.Context, input *GetProductsInput, opts ...request.Option) (*GetProductsOutput, error) { - req, out := c.GetProductsRequest(input) - req.SetContext(ctx) - req.ApplyOptions(opts...) - return out, req.Send() -} - -// GetProductsPages iterates over the pages of a GetProducts operation, -// calling the "fn" function with the response data for each page. To stop -// iterating, return false from the fn function. -// -// See GetProducts 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 GetProducts operation. -// pageNum := 0 -// err := client.GetProductsPages(params, -// func(page *pricing.GetProductsOutput, lastPage bool) bool { -// pageNum++ -// fmt.Println(page) -// return pageNum <= 3 -// }) -func (c *Pricing) GetProductsPages(input *GetProductsInput, fn func(*GetProductsOutput, bool) bool) error { - return c.GetProductsPagesWithContext(aws.BackgroundContext(), input, fn) -} - -// GetProductsPagesWithContext same as GetProductsPages 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 *Pricing) GetProductsPagesWithContext(ctx aws.Context, input *GetProductsInput, fn func(*GetProductsOutput, bool) bool, opts ...request.Option) error { - p := request.Pagination{ - NewRequest: func() (*request.Request, error) { - var inCpy *GetProductsInput - if input != nil { - tmp := *input - inCpy = &tmp - } - req, _ := c.GetProductsRequest(inCpy) - req.SetContext(ctx) - req.ApplyOptions(opts...) - return req, nil - }, - } - - for p.Next() { - if !fn(p.Page().(*GetProductsOutput), !p.HasNextPage()) { - break - } - } - - return p.Err() -} - -const opListPriceLists = "ListPriceLists" - -// ListPriceListsRequest generates a "aws/request.Request" representing the -// client's request for the ListPriceLists 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 ListPriceLists for more information on using the ListPriceLists -// 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 ListPriceListsRequest method. -// req, resp := client.ListPriceListsRequest(params) -// -// err := req.Send() -// if err == nil { // resp is now filled -// fmt.Println(resp) -// } -// -// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/ListPriceLists -func (c *Pricing) ListPriceListsRequest(input *ListPriceListsInput) (req *request.Request, output *ListPriceListsOutput) { - op := &request.Operation{ - Name: opListPriceLists, - HTTPMethod: "POST", - HTTPPath: "/", - Paginator: &request.Paginator{ - InputTokens: []string{"NextToken"}, - OutputTokens: []string{"NextToken"}, - LimitToken: "MaxResults", - TruncationToken: "", - }, - } - - if input == nil { - input = &ListPriceListsInput{} - } - - output = &ListPriceListsOutput{} - req = c.newRequest(op, input, output) - return -} - -// ListPriceLists API operation for AWS Price List Service. -// -// This feature is in preview release and is subject to change. Your use of -// Amazon Web Services Price List API is subject to the Beta Service Participation -// terms of the Amazon Web Services Service Terms (https://aws.amazon.com/service-terms/) -// (Section 1.10). -// -// This returns a list of Price List references that the requester if authorized -// to view, given a ServiceCode, CurrencyCode, and an EffectiveDate. Use without -// a RegionCode filter to list Price List references from all available Amazon -// Web Services Regions. Use with a RegionCode filter to get the Price List -// reference that's specific to a specific Amazon Web Services Region. You can -// use the PriceListArn from the response to get your preferred Price List files -// through the GetPriceListFileUrl (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_GetPriceListFileUrl.html) -// API. -// -// 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 Price List Service's -// API operation ListPriceLists for usage and error information. -// -// Returned Error Types: -// -// - InvalidParameterException -// One or more parameters had an invalid value. -// -// - InvalidNextTokenException -// The pagination token is invalid. Try again without a pagination token. -// -// - NotFoundException -// The requested resource can't be found. -// -// - AccessDeniedException -// General authentication failure. The request wasn't signed correctly. -// -// - InternalErrorException -// An error on the server occurred during the processing of your request. Try -// again later. -// -// - ThrottlingException -// You've made too many requests exceeding service quotas. -// -// - ResourceNotFoundException -// The requested resource can't be found. -// -// - ExpiredNextTokenException -// The pagination token expired. Try again without a pagination token. -// -// See also, https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15/ListPriceLists -func (c *Pricing) ListPriceLists(input *ListPriceListsInput) (*ListPriceListsOutput, error) { - req, out := c.ListPriceListsRequest(input) - return out, req.Send() -} - -// ListPriceListsWithContext is the same as ListPriceLists with the addition of -// the ability to pass a context and additional request options. -// -// See ListPriceLists 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 *Pricing) ListPriceListsWithContext(ctx aws.Context, input *ListPriceListsInput, opts ...request.Option) (*ListPriceListsOutput, error) { - req, out := c.ListPriceListsRequest(input) - req.SetContext(ctx) - req.ApplyOptions(opts...) - return out, req.Send() -} - -// ListPriceListsPages iterates over the pages of a ListPriceLists operation, -// calling the "fn" function with the response data for each page. To stop -// iterating, return false from the fn function. -// -// See ListPriceLists 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 ListPriceLists operation. -// pageNum := 0 -// err := client.ListPriceListsPages(params, -// func(page *pricing.ListPriceListsOutput, lastPage bool) bool { -// pageNum++ -// fmt.Println(page) -// return pageNum <= 3 -// }) -func (c *Pricing) ListPriceListsPages(input *ListPriceListsInput, fn func(*ListPriceListsOutput, bool) bool) error { - return c.ListPriceListsPagesWithContext(aws.BackgroundContext(), input, fn) -} - -// ListPriceListsPagesWithContext same as ListPriceListsPages 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 *Pricing) ListPriceListsPagesWithContext(ctx aws.Context, input *ListPriceListsInput, fn func(*ListPriceListsOutput, bool) bool, opts ...request.Option) error { - p := request.Pagination{ - NewRequest: func() (*request.Request, error) { - var inCpy *ListPriceListsInput - if input != nil { - tmp := *input - inCpy = &tmp - } - req, _ := c.ListPriceListsRequest(inCpy) - req.SetContext(ctx) - req.ApplyOptions(opts...) - return req, nil - }, - } - - for p.Next() { - if !fn(p.Page().(*ListPriceListsOutput), !p.HasNextPage()) { - break - } - } - - return p.Err() -} - -// General authentication failure. The request wasn't signed correctly. -type AccessDeniedException struct { - _ struct{} `type:"structure"` - RespMetadata protocol.ResponseMetadata `json:"-" xml:"-"` - - Message_ *string `locationName:"Message" type:"string"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s AccessDeniedException) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s AccessDeniedException) GoString() string { - return s.String() -} - -func newErrorAccessDeniedException(v protocol.ResponseMetadata) error { - return &AccessDeniedException{ - RespMetadata: v, - } -} - -// Code returns the exception type name. -func (s *AccessDeniedException) Code() string { - return "AccessDeniedException" -} - -// Message returns the exception's message. -func (s *AccessDeniedException) Message() string { - if s.Message_ != nil { - return *s.Message_ - } - return "" -} - -// OrigErr always returns nil, satisfies awserr.Error interface. -func (s *AccessDeniedException) OrigErr() error { - return nil -} - -func (s *AccessDeniedException) Error() string { - return fmt.Sprintf("%s: %s", s.Code(), s.Message()) -} - -// Status code returns the HTTP status code for the request's response error. -func (s *AccessDeniedException) StatusCode() int { - return s.RespMetadata.StatusCode -} - -// RequestID returns the service's response RequestID for request. -func (s *AccessDeniedException) RequestID() string { - return s.RespMetadata.RequestID -} - -// The values of a given attribute, such as Throughput Optimized HDD or Provisioned -// IOPS for the Amazon EC2 volumeType attribute. -type AttributeValue struct { - _ struct{} `type:"structure"` - - // The specific value of an attributeName. - Value *string `type:"string"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s AttributeValue) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s AttributeValue) GoString() string { - return s.String() -} - -// SetValue sets the Value field's value. -func (s *AttributeValue) SetValue(v string) *AttributeValue { - s.Value = &v - return s -} - -type DescribeServicesInput struct { - _ struct{} `type:"structure"` - - // The format version that you want the response to be in. - // - // Valid values are: aws_v1 - FormatVersion *string `type:"string"` - - // The maximum number of results that you want returned in the response. - MaxResults *int64 `min:"1" type:"integer"` - - // The pagination token that indicates the next set of results that you want - // to retrieve. - NextToken *string `type:"string"` - - // The code for the service whose information you want to retrieve, such as - // AmazonEC2. You can use the ServiceCode to filter the results in a GetProducts - // call. To retrieve a list of all services, leave this blank. - ServiceCode *string `type:"string"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s DescribeServicesInput) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s DescribeServicesInput) GoString() string { - return s.String() -} - -// Validate inspects the fields of the type to determine if they are valid. -func (s *DescribeServicesInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "DescribeServicesInput"} - if s.MaxResults != nil && *s.MaxResults < 1 { - invalidParams.Add(request.NewErrParamMinValue("MaxResults", 1)) - } - - if invalidParams.Len() > 0 { - return invalidParams - } - return nil -} - -// SetFormatVersion sets the FormatVersion field's value. -func (s *DescribeServicesInput) SetFormatVersion(v string) *DescribeServicesInput { - s.FormatVersion = &v - return s -} - -// SetMaxResults sets the MaxResults field's value. -func (s *DescribeServicesInput) SetMaxResults(v int64) *DescribeServicesInput { - s.MaxResults = &v - return s -} - -// SetNextToken sets the NextToken field's value. -func (s *DescribeServicesInput) SetNextToken(v string) *DescribeServicesInput { - s.NextToken = &v - return s -} - -// SetServiceCode sets the ServiceCode field's value. -func (s *DescribeServicesInput) SetServiceCode(v string) *DescribeServicesInput { - s.ServiceCode = &v - return s -} - -type DescribeServicesOutput struct { - _ struct{} `type:"structure"` - - // The format version of the response. For example, aws_v1. - FormatVersion *string `type:"string"` - - // The pagination token for the next set of retrievable results. - NextToken *string `type:"string"` - - // The service metadata for the service or services in the response. - Services []*Service `type:"list"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s DescribeServicesOutput) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s DescribeServicesOutput) GoString() string { - return s.String() -} - -// SetFormatVersion sets the FormatVersion field's value. -func (s *DescribeServicesOutput) SetFormatVersion(v string) *DescribeServicesOutput { - s.FormatVersion = &v - return s -} - -// SetNextToken sets the NextToken field's value. -func (s *DescribeServicesOutput) SetNextToken(v string) *DescribeServicesOutput { - s.NextToken = &v - return s -} - -// SetServices sets the Services field's value. -func (s *DescribeServicesOutput) SetServices(v []*Service) *DescribeServicesOutput { - s.Services = v - return s -} - -// The pagination token expired. Try again without a pagination token. -type ExpiredNextTokenException struct { - _ struct{} `type:"structure"` - RespMetadata protocol.ResponseMetadata `json:"-" xml:"-"` - - Message_ *string `locationName:"Message" type:"string"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s ExpiredNextTokenException) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s ExpiredNextTokenException) GoString() string { - return s.String() -} - -func newErrorExpiredNextTokenException(v protocol.ResponseMetadata) error { - return &ExpiredNextTokenException{ - RespMetadata: v, - } -} - -// Code returns the exception type name. -func (s *ExpiredNextTokenException) Code() string { - return "ExpiredNextTokenException" -} - -// Message returns the exception's message. -func (s *ExpiredNextTokenException) Message() string { - if s.Message_ != nil { - return *s.Message_ - } - return "" -} - -// OrigErr always returns nil, satisfies awserr.Error interface. -func (s *ExpiredNextTokenException) OrigErr() error { - return nil -} - -func (s *ExpiredNextTokenException) Error() string { - return fmt.Sprintf("%s: %s", s.Code(), s.Message()) -} - -// Status code returns the HTTP status code for the request's response error. -func (s *ExpiredNextTokenException) StatusCode() int { - return s.RespMetadata.StatusCode -} - -// RequestID returns the service's response RequestID for request. -func (s *ExpiredNextTokenException) RequestID() string { - return s.RespMetadata.RequestID -} - -// The constraints that you want all returned products to match. -type Filter struct { - _ struct{} `type:"structure"` - - // The product metadata field that you want to filter on. You can filter by - // just the service code to see all products for a specific service, filter - // by just the attribute name to see a specific attribute for multiple services, - // or use both a service code and an attribute name to retrieve only products - // that match both fields. - // - // Valid values include: ServiceCode, and all attribute names - // - // For example, you can filter by the AmazonEC2 service code and the volumeType - // attribute name to get the prices for only Amazon EC2 volumes. - // - // Field is a required field - Field *string `type:"string" required:"true"` - - // The type of filter that you want to use. - // - // Valid values are: TERM_MATCH. TERM_MATCH returns only products that match - // both the given filter field and the given value. - // - // Type is a required field - Type *string `type:"string" required:"true" enum:"FilterType"` - - // The service code or attribute value that you want to filter by. If you're - // filtering by service code this is the actual service code, such as AmazonEC2. - // If you're filtering by attribute name, this is the attribute value that you - // want the returned products to match, such as a Provisioned IOPS volume. - // - // Value is a required field - Value *string `type:"string" required:"true"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s Filter) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s Filter) GoString() string { - return s.String() -} - -// Validate inspects the fields of the type to determine if they are valid. -func (s *Filter) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "Filter"} - if s.Field == nil { - invalidParams.Add(request.NewErrParamRequired("Field")) - } - if s.Type == nil { - invalidParams.Add(request.NewErrParamRequired("Type")) - } - if s.Value == nil { - invalidParams.Add(request.NewErrParamRequired("Value")) - } - - if invalidParams.Len() > 0 { - return invalidParams - } - return nil -} - -// SetField sets the Field field's value. -func (s *Filter) SetField(v string) *Filter { - s.Field = &v - return s -} - -// SetType sets the Type field's value. -func (s *Filter) SetType(v string) *Filter { - s.Type = &v - return s -} - -// SetValue sets the Value field's value. -func (s *Filter) SetValue(v string) *Filter { - s.Value = &v - return s -} - -type GetAttributeValuesInput struct { - _ struct{} `type:"structure"` - - // The name of the attribute that you want to retrieve the values for, such - // as volumeType. - // - // AttributeName is a required field - AttributeName *string `type:"string" required:"true"` - - // The maximum number of results to return in response. - MaxResults *int64 `min:"1" type:"integer"` - - // The pagination token that indicates the next set of results that you want - // to retrieve. - NextToken *string `type:"string"` - - // The service code for the service whose attributes you want to retrieve. For - // example, if you want the retrieve an EC2 attribute, use AmazonEC2. - // - // ServiceCode is a required field - ServiceCode *string `type:"string" required:"true"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s GetAttributeValuesInput) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s GetAttributeValuesInput) GoString() string { - return s.String() -} - -// Validate inspects the fields of the type to determine if they are valid. -func (s *GetAttributeValuesInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "GetAttributeValuesInput"} - if s.AttributeName == nil { - invalidParams.Add(request.NewErrParamRequired("AttributeName")) - } - if s.MaxResults != nil && *s.MaxResults < 1 { - invalidParams.Add(request.NewErrParamMinValue("MaxResults", 1)) - } - if s.ServiceCode == nil { - invalidParams.Add(request.NewErrParamRequired("ServiceCode")) - } - - if invalidParams.Len() > 0 { - return invalidParams - } - return nil -} - -// SetAttributeName sets the AttributeName field's value. -func (s *GetAttributeValuesInput) SetAttributeName(v string) *GetAttributeValuesInput { - s.AttributeName = &v - return s -} - -// SetMaxResults sets the MaxResults field's value. -func (s *GetAttributeValuesInput) SetMaxResults(v int64) *GetAttributeValuesInput { - s.MaxResults = &v - return s -} - -// SetNextToken sets the NextToken field's value. -func (s *GetAttributeValuesInput) SetNextToken(v string) *GetAttributeValuesInput { - s.NextToken = &v - return s -} - -// SetServiceCode sets the ServiceCode field's value. -func (s *GetAttributeValuesInput) SetServiceCode(v string) *GetAttributeValuesInput { - s.ServiceCode = &v - return s -} - -type GetAttributeValuesOutput struct { - _ struct{} `type:"structure"` - - // The list of values for an attribute. For example, Throughput Optimized HDD - // and Provisioned IOPS are two available values for the AmazonEC2 volumeType. - AttributeValues []*AttributeValue `type:"list"` - - // The pagination token that indicates the next set of results to retrieve. - NextToken *string `type:"string"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s GetAttributeValuesOutput) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s GetAttributeValuesOutput) GoString() string { - return s.String() -} - -// SetAttributeValues sets the AttributeValues field's value. -func (s *GetAttributeValuesOutput) SetAttributeValues(v []*AttributeValue) *GetAttributeValuesOutput { - s.AttributeValues = v - return s -} - -// SetNextToken sets the NextToken field's value. -func (s *GetAttributeValuesOutput) SetNextToken(v string) *GetAttributeValuesOutput { - s.NextToken = &v - return s -} - -type GetPriceListFileUrlInput struct { - _ struct{} `type:"structure"` - - // The format that you want to retrieve your Price List files in. The FileFormat - // can be obtained from the ListPriceLists (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_ListPriceLists.html) - // response. - // - // FileFormat is a required field - FileFormat *string `min:"1" type:"string" required:"true"` - - // The unique identifier that maps to where your Price List files are located. - // PriceListArn can be obtained from the ListPriceLists (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_ListPriceLists.html) - // response. - // - // PriceListArn is a required field - PriceListArn *string `min:"18" type:"string" required:"true"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s GetPriceListFileUrlInput) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s GetPriceListFileUrlInput) GoString() string { - return s.String() -} - -// Validate inspects the fields of the type to determine if they are valid. -func (s *GetPriceListFileUrlInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "GetPriceListFileUrlInput"} - if s.FileFormat == nil { - invalidParams.Add(request.NewErrParamRequired("FileFormat")) - } - if s.FileFormat != nil && len(*s.FileFormat) < 1 { - invalidParams.Add(request.NewErrParamMinLen("FileFormat", 1)) - } - if s.PriceListArn == nil { - invalidParams.Add(request.NewErrParamRequired("PriceListArn")) - } - if s.PriceListArn != nil && len(*s.PriceListArn) < 18 { - invalidParams.Add(request.NewErrParamMinLen("PriceListArn", 18)) - } - - if invalidParams.Len() > 0 { - return invalidParams - } - return nil -} - -// SetFileFormat sets the FileFormat field's value. -func (s *GetPriceListFileUrlInput) SetFileFormat(v string) *GetPriceListFileUrlInput { - s.FileFormat = &v - return s -} - -// SetPriceListArn sets the PriceListArn field's value. -func (s *GetPriceListFileUrlInput) SetPriceListArn(v string) *GetPriceListFileUrlInput { - s.PriceListArn = &v - return s -} - -type GetPriceListFileUrlOutput struct { - _ struct{} `type:"structure"` - - // The URL to download your Price List file from. - Url *string `type:"string"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s GetPriceListFileUrlOutput) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s GetPriceListFileUrlOutput) GoString() string { - return s.String() -} - -// SetUrl sets the Url field's value. -func (s *GetPriceListFileUrlOutput) SetUrl(v string) *GetPriceListFileUrlOutput { - s.Url = &v - return s -} - -type GetProductsInput struct { - _ struct{} `type:"structure"` - - // The list of filters that limit the returned products. only products that - // match all filters are returned. - Filters []*Filter `type:"list"` - - // The format version that you want the response to be in. - // - // Valid values are: aws_v1 - FormatVersion *string `type:"string"` - - // The maximum number of results to return in the response. - MaxResults *int64 `min:"1" type:"integer"` - - // The pagination token that indicates the next set of results that you want - // to retrieve. - NextToken *string `type:"string"` - - // The code for the service whose products you want to retrieve. - // - // ServiceCode is a required field - ServiceCode *string `type:"string" required:"true"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s GetProductsInput) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s GetProductsInput) GoString() string { - return s.String() -} - -// Validate inspects the fields of the type to determine if they are valid. -func (s *GetProductsInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "GetProductsInput"} - if s.MaxResults != nil && *s.MaxResults < 1 { - invalidParams.Add(request.NewErrParamMinValue("MaxResults", 1)) - } - if s.ServiceCode == nil { - invalidParams.Add(request.NewErrParamRequired("ServiceCode")) - } - if s.Filters != nil { - for i, v := range s.Filters { - if v == nil { - continue - } - if err := v.Validate(); err != nil { - invalidParams.AddNested(fmt.Sprintf("%s[%v]", "Filters", i), err.(request.ErrInvalidParams)) - } - } - } - - if invalidParams.Len() > 0 { - return invalidParams - } - return nil -} - -// SetFilters sets the Filters field's value. -func (s *GetProductsInput) SetFilters(v []*Filter) *GetProductsInput { - s.Filters = v - return s -} - -// SetFormatVersion sets the FormatVersion field's value. -func (s *GetProductsInput) SetFormatVersion(v string) *GetProductsInput { - s.FormatVersion = &v - return s -} - -// SetMaxResults sets the MaxResults field's value. -func (s *GetProductsInput) SetMaxResults(v int64) *GetProductsInput { - s.MaxResults = &v - return s -} - -// SetNextToken sets the NextToken field's value. -func (s *GetProductsInput) SetNextToken(v string) *GetProductsInput { - s.NextToken = &v - return s -} - -// SetServiceCode sets the ServiceCode field's value. -func (s *GetProductsInput) SetServiceCode(v string) *GetProductsInput { - s.ServiceCode = &v - return s -} - -type GetProductsOutput struct { - _ struct{} `type:"structure"` - - // The format version of the response. For example, aws_v1. - FormatVersion *string `type:"string"` - - // The pagination token that indicates the next set of results to retrieve. - NextToken *string `type:"string"` - - // The list of products that match your filters. The list contains both the - // product metadata and the price information. - PriceList []aws.JSONValue `type:"list"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s GetProductsOutput) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s GetProductsOutput) GoString() string { - return s.String() -} - -// SetFormatVersion sets the FormatVersion field's value. -func (s *GetProductsOutput) SetFormatVersion(v string) *GetProductsOutput { - s.FormatVersion = &v - return s -} - -// SetNextToken sets the NextToken field's value. -func (s *GetProductsOutput) SetNextToken(v string) *GetProductsOutput { - s.NextToken = &v - return s -} - -// SetPriceList sets the PriceList field's value. -func (s *GetProductsOutput) SetPriceList(v []aws.JSONValue) *GetProductsOutput { - s.PriceList = v - return s -} - -// An error on the server occurred during the processing of your request. Try -// again later. -type InternalErrorException struct { - _ struct{} `type:"structure"` - RespMetadata protocol.ResponseMetadata `json:"-" xml:"-"` - - Message_ *string `locationName:"Message" type:"string"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s InternalErrorException) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s InternalErrorException) GoString() string { - return s.String() -} - -func newErrorInternalErrorException(v protocol.ResponseMetadata) error { - return &InternalErrorException{ - RespMetadata: v, - } -} - -// Code returns the exception type name. -func (s *InternalErrorException) Code() string { - return "InternalErrorException" -} - -// Message returns the exception's message. -func (s *InternalErrorException) Message() string { - if s.Message_ != nil { - return *s.Message_ - } - return "" -} - -// OrigErr always returns nil, satisfies awserr.Error interface. -func (s *InternalErrorException) OrigErr() error { - return nil -} - -func (s *InternalErrorException) Error() string { - return fmt.Sprintf("%s: %s", s.Code(), s.Message()) -} - -// Status code returns the HTTP status code for the request's response error. -func (s *InternalErrorException) StatusCode() int { - return s.RespMetadata.StatusCode -} - -// RequestID returns the service's response RequestID for request. -func (s *InternalErrorException) RequestID() string { - return s.RespMetadata.RequestID -} - -// The pagination token is invalid. Try again without a pagination token. -type InvalidNextTokenException struct { - _ struct{} `type:"structure"` - RespMetadata protocol.ResponseMetadata `json:"-" xml:"-"` - - Message_ *string `locationName:"Message" type:"string"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s InvalidNextTokenException) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s InvalidNextTokenException) GoString() string { - return s.String() -} - -func newErrorInvalidNextTokenException(v protocol.ResponseMetadata) error { - return &InvalidNextTokenException{ - RespMetadata: v, - } -} - -// Code returns the exception type name. -func (s *InvalidNextTokenException) Code() string { - return "InvalidNextTokenException" -} - -// Message returns the exception's message. -func (s *InvalidNextTokenException) Message() string { - if s.Message_ != nil { - return *s.Message_ - } - return "" -} - -// OrigErr always returns nil, satisfies awserr.Error interface. -func (s *InvalidNextTokenException) OrigErr() error { - return nil -} - -func (s *InvalidNextTokenException) Error() string { - return fmt.Sprintf("%s: %s", s.Code(), s.Message()) -} - -// Status code returns the HTTP status code for the request's response error. -func (s *InvalidNextTokenException) StatusCode() int { - return s.RespMetadata.StatusCode -} - -// RequestID returns the service's response RequestID for request. -func (s *InvalidNextTokenException) RequestID() string { - return s.RespMetadata.RequestID -} - -// One or more parameters had an invalid value. -type InvalidParameterException struct { - _ struct{} `type:"structure"` - RespMetadata protocol.ResponseMetadata `json:"-" xml:"-"` - - Message_ *string `locationName:"Message" type:"string"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s InvalidParameterException) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s InvalidParameterException) GoString() string { - return s.String() -} - -func newErrorInvalidParameterException(v protocol.ResponseMetadata) error { - return &InvalidParameterException{ - RespMetadata: v, - } -} - -// Code returns the exception type name. -func (s *InvalidParameterException) Code() string { - return "InvalidParameterException" -} - -// Message returns the exception's message. -func (s *InvalidParameterException) Message() string { - if s.Message_ != nil { - return *s.Message_ - } - return "" -} - -// OrigErr always returns nil, satisfies awserr.Error interface. -func (s *InvalidParameterException) OrigErr() error { - return nil -} - -func (s *InvalidParameterException) Error() string { - return fmt.Sprintf("%s: %s", s.Code(), s.Message()) -} - -// Status code returns the HTTP status code for the request's response error. -func (s *InvalidParameterException) StatusCode() int { - return s.RespMetadata.StatusCode -} - -// RequestID returns the service's response RequestID for request. -func (s *InvalidParameterException) RequestID() string { - return s.RespMetadata.RequestID -} - -type ListPriceListsInput struct { - _ struct{} `type:"structure"` - - // The three alphabetical character ISO-4217 currency code that the Price List - // files are denominated in. - // - // CurrencyCode is a required field - CurrencyCode *string `type:"string" required:"true"` - - // The date that the Price List file prices are effective from. - // - // EffectiveDate is a required field - EffectiveDate *time.Time `type:"timestamp" required:"true"` - - // The maximum number of results to return in the response. - MaxResults *int64 `min:"1" type:"integer"` - - // The pagination token that indicates the next set of results that you want - // to retrieve. - NextToken *string `type:"string"` - - // This is used to filter the Price List by Amazon Web Services Region. For - // example, to get the price list only for the US East (N. Virginia) Region, - // use us-east-1. If nothing is specified, you retrieve price lists for all - // applicable Regions. The available RegionCode list can be retrieved from GetAttributeValues - // (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_GetAttributeValues.html) - // API. - RegionCode *string `min:"1" type:"string"` - - // The service code or the Savings Plan service code for the attributes that - // you want to retrieve. For example, to get the list of applicable Amazon EC2 - // price lists, use AmazonEC2. For a full list of service codes containing On-Demand - // and Reserved Instance (RI) pricing, use the DescribeServices (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_DescribeServices.html#awscostmanagement-pricing_DescribeServices-request-FormatVersion) - // API. - // - // To retrieve the Reserved Instance and Compute Savings Plan price lists, use - // ComputeSavingsPlans. - // - // To retrieve Machine Learning Savings Plans price lists, use MachineLearningSavingsPlans. - // - // ServiceCode is a required field - ServiceCode *string `min:"1" type:"string" required:"true"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s ListPriceListsInput) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s ListPriceListsInput) GoString() string { - return s.String() -} - -// Validate inspects the fields of the type to determine if they are valid. -func (s *ListPriceListsInput) Validate() error { - invalidParams := request.ErrInvalidParams{Context: "ListPriceListsInput"} - if s.CurrencyCode == nil { - invalidParams.Add(request.NewErrParamRequired("CurrencyCode")) - } - if s.EffectiveDate == nil { - invalidParams.Add(request.NewErrParamRequired("EffectiveDate")) - } - if s.MaxResults != nil && *s.MaxResults < 1 { - invalidParams.Add(request.NewErrParamMinValue("MaxResults", 1)) - } - if s.RegionCode != nil && len(*s.RegionCode) < 1 { - invalidParams.Add(request.NewErrParamMinLen("RegionCode", 1)) - } - if s.ServiceCode == nil { - invalidParams.Add(request.NewErrParamRequired("ServiceCode")) - } - if s.ServiceCode != nil && len(*s.ServiceCode) < 1 { - invalidParams.Add(request.NewErrParamMinLen("ServiceCode", 1)) - } - - if invalidParams.Len() > 0 { - return invalidParams - } - return nil -} - -// SetCurrencyCode sets the CurrencyCode field's value. -func (s *ListPriceListsInput) SetCurrencyCode(v string) *ListPriceListsInput { - s.CurrencyCode = &v - return s -} - -// SetEffectiveDate sets the EffectiveDate field's value. -func (s *ListPriceListsInput) SetEffectiveDate(v time.Time) *ListPriceListsInput { - s.EffectiveDate = &v - return s -} - -// SetMaxResults sets the MaxResults field's value. -func (s *ListPriceListsInput) SetMaxResults(v int64) *ListPriceListsInput { - s.MaxResults = &v - return s -} - -// SetNextToken sets the NextToken field's value. -func (s *ListPriceListsInput) SetNextToken(v string) *ListPriceListsInput { - s.NextToken = &v - return s -} - -// SetRegionCode sets the RegionCode field's value. -func (s *ListPriceListsInput) SetRegionCode(v string) *ListPriceListsInput { - s.RegionCode = &v - return s -} - -// SetServiceCode sets the ServiceCode field's value. -func (s *ListPriceListsInput) SetServiceCode(v string) *ListPriceListsInput { - s.ServiceCode = &v - return s -} - -type ListPriceListsOutput struct { - _ struct{} `type:"structure"` - - // The pagination token that indicates the next set of results to retrieve. - NextToken *string `type:"string"` - - // The type of price list references that match your request. - PriceLists []*PriceList `type:"list"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s ListPriceListsOutput) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s ListPriceListsOutput) GoString() string { - return s.String() -} - -// SetNextToken sets the NextToken field's value. -func (s *ListPriceListsOutput) SetNextToken(v string) *ListPriceListsOutput { - s.NextToken = &v - return s -} - -// SetPriceLists sets the PriceLists field's value. -func (s *ListPriceListsOutput) SetPriceLists(v []*PriceList) *ListPriceListsOutput { - s.PriceLists = v - return s -} - -// The requested resource can't be found. -type NotFoundException struct { - _ struct{} `type:"structure"` - RespMetadata protocol.ResponseMetadata `json:"-" xml:"-"` - - Message_ *string `locationName:"Message" type:"string"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s NotFoundException) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s NotFoundException) GoString() string { - return s.String() -} - -func newErrorNotFoundException(v protocol.ResponseMetadata) error { - return &NotFoundException{ - RespMetadata: v, - } -} - -// Code returns the exception type name. -func (s *NotFoundException) Code() string { - return "NotFoundException" -} - -// Message returns the exception's message. -func (s *NotFoundException) Message() string { - if s.Message_ != nil { - return *s.Message_ - } - return "" -} - -// OrigErr always returns nil, satisfies awserr.Error interface. -func (s *NotFoundException) OrigErr() error { - return nil -} - -func (s *NotFoundException) Error() string { - return fmt.Sprintf("%s: %s", s.Code(), s.Message()) -} - -// Status code returns the HTTP status code for the request's response error. -func (s *NotFoundException) StatusCode() int { - return s.RespMetadata.StatusCode -} - -// RequestID returns the service's response RequestID for request. -func (s *NotFoundException) RequestID() string { - return s.RespMetadata.RequestID -} - -// This feature is in preview release and is subject to change. Your use of -// Amazon Web Services Price List API is subject to the Beta Service Participation -// terms of the Amazon Web Services Service Terms (https://aws.amazon.com/service-terms/) -// (Section 1.10). -// -// This is the type of price list references that match your request. -type PriceList struct { - _ struct{} `type:"structure"` - - // The three alphabetical character ISO-4217 currency code the Price List files - // are denominated in. - CurrencyCode *string `type:"string"` - - // The format you want to retrieve your Price List files. The FileFormat can - // be obtained from the ListPriceList (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_ListPriceLists.html) - // response. - FileFormats []*string `type:"list"` - - // The unique identifier that maps to where your Price List files are located. - // PriceListArn can be obtained from the ListPriceList (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_ListPriceLists.html) - // response. - PriceListArn *string `min:"18" type:"string"` - - // This is used to filter the Price List by Amazon Web Services Region. For - // example, to get the price list only for the US East (N. Virginia) Region, - // use us-east-1. If nothing is specified, you retrieve price lists for all - // applicable Regions. The available RegionCode list can be retrieved from GetAttributeValues - // (https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_pricing_GetAttributeValues.html) - // API. - RegionCode *string `min:"1" type:"string"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s PriceList) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s PriceList) GoString() string { - return s.String() -} - -// SetCurrencyCode sets the CurrencyCode field's value. -func (s *PriceList) SetCurrencyCode(v string) *PriceList { - s.CurrencyCode = &v - return s -} - -// SetFileFormats sets the FileFormats field's value. -func (s *PriceList) SetFileFormats(v []*string) *PriceList { - s.FileFormats = v - return s -} - -// SetPriceListArn sets the PriceListArn field's value. -func (s *PriceList) SetPriceListArn(v string) *PriceList { - s.PriceListArn = &v - return s -} - -// SetRegionCode sets the RegionCode field's value. -func (s *PriceList) SetRegionCode(v string) *PriceList { - s.RegionCode = &v - return s -} - -// The requested resource can't be found. -type ResourceNotFoundException struct { - _ struct{} `type:"structure"` - RespMetadata protocol.ResponseMetadata `json:"-" xml:"-"` - - Message_ *string `locationName:"Message" type:"string"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s ResourceNotFoundException) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s ResourceNotFoundException) GoString() string { - return s.String() -} - -func newErrorResourceNotFoundException(v protocol.ResponseMetadata) error { - return &ResourceNotFoundException{ - RespMetadata: v, - } -} - -// Code returns the exception type name. -func (s *ResourceNotFoundException) Code() string { - return "ResourceNotFoundException" -} - -// Message returns the exception's message. -func (s *ResourceNotFoundException) Message() string { - if s.Message_ != nil { - return *s.Message_ - } - return "" -} - -// OrigErr always returns nil, satisfies awserr.Error interface. -func (s *ResourceNotFoundException) OrigErr() error { - return nil -} - -func (s *ResourceNotFoundException) Error() string { - return fmt.Sprintf("%s: %s", s.Code(), s.Message()) -} - -// Status code returns the HTTP status code for the request's response error. -func (s *ResourceNotFoundException) StatusCode() int { - return s.RespMetadata.StatusCode -} - -// RequestID returns the service's response RequestID for request. -func (s *ResourceNotFoundException) RequestID() string { - return s.RespMetadata.RequestID -} - -// The metadata for a service, such as the service code and available attribute -// names. -type Service struct { - _ struct{} `type:"structure"` - - // The attributes that are available for this service. - AttributeNames []*string `type:"list"` - - // The code for the Amazon Web Services service. - // - // ServiceCode is a required field - ServiceCode *string `type:"string" required:"true"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s Service) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s Service) GoString() string { - return s.String() -} - -// SetAttributeNames sets the AttributeNames field's value. -func (s *Service) SetAttributeNames(v []*string) *Service { - s.AttributeNames = v - return s -} - -// SetServiceCode sets the ServiceCode field's value. -func (s *Service) SetServiceCode(v string) *Service { - s.ServiceCode = &v - return s -} - -// You've made too many requests exceeding service quotas. -type ThrottlingException struct { - _ struct{} `type:"structure"` - RespMetadata protocol.ResponseMetadata `json:"-" xml:"-"` - - Message_ *string `locationName:"Message" type:"string"` -} - -// String returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s ThrottlingException) String() string { - return awsutil.Prettify(s) -} - -// GoString returns the string representation. -// -// API parameter values that are decorated as "sensitive" in the API will not -// be included in the string output. The member name will be present, but the -// value will be replaced with "sensitive". -func (s ThrottlingException) GoString() string { - return s.String() -} - -func newErrorThrottlingException(v protocol.ResponseMetadata) error { - return &ThrottlingException{ - RespMetadata: v, - } -} - -// Code returns the exception type name. -func (s *ThrottlingException) Code() string { - return "ThrottlingException" -} - -// Message returns the exception's message. -func (s *ThrottlingException) Message() string { - if s.Message_ != nil { - return *s.Message_ - } - return "" -} - -// OrigErr always returns nil, satisfies awserr.Error interface. -func (s *ThrottlingException) OrigErr() error { - return nil -} - -func (s *ThrottlingException) Error() string { - return fmt.Sprintf("%s: %s", s.Code(), s.Message()) -} - -// Status code returns the HTTP status code for the request's response error. -func (s *ThrottlingException) StatusCode() int { - return s.RespMetadata.StatusCode -} - -// RequestID returns the service's response RequestID for request. -func (s *ThrottlingException) RequestID() string { - return s.RespMetadata.RequestID -} - -const ( - // FilterTypeTermMatch is a FilterType enum value - FilterTypeTermMatch = "TERM_MATCH" -) - -// FilterType_Values returns all elements of the FilterType enum -func FilterType_Values() []string { - return []string{ - FilterTypeTermMatch, - } -} diff --git a/vendor/github.com/aws/aws-sdk-go/service/pricing/doc.go b/vendor/github.com/aws/aws-sdk-go/service/pricing/doc.go deleted file mode 100644 index a7f80b9f9916a..0000000000000 --- a/vendor/github.com/aws/aws-sdk-go/service/pricing/doc.go +++ /dev/null @@ -1,53 +0,0 @@ -// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. - -// Package pricing provides the client and types for making API -// requests to AWS Price List Service. -// -// The Amazon Web Services Price List API is a centralized and convenient way -// to programmatically query Amazon Web Services for services, products, and -// pricing information. The Amazon Web Services Price List uses standardized -// product attributes such as Location, Storage Class, and Operating System, -// and provides prices at the SKU level. You can use the Amazon Web Services -// Price List to do the following: -// -// - Build cost control and scenario planning tools -// -// - Reconcile billing data -// -// - Forecast future spend for budgeting purposes -// -// - Provide cost benefit analysis that compare your internal workloads with -// Amazon Web Services -// -// Use GetServices without a service code to retrieve the service codes for -// all Amazon Web Services, then GetServices with a service code to retrieve -// the attribute names for that service. After you have the service code and -// attribute names, you can use GetAttributeValues to see what values are available -// for an attribute. With the service code and an attribute name and value, -// you can use GetProducts to find specific products that you're interested -// in, such as an AmazonEC2 instance, with a Provisioned IOPS volumeType. -// -// For more information, see Using the Amazon Web Services Price List API (https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/price-changes.html) -// in the Billing User Guide. -// -// See https://docs.aws.amazon.com/goto/WebAPI/pricing-2017-10-15 for more information on this service. -// -// See pricing package documentation for more information. -// https://docs.aws.amazon.com/sdk-for-go/api/service/pricing/ -// -// # Using the Client -// -// To contact AWS Price List Service 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 Price List Service client Pricing for more -// information on creating client for this service. -// https://docs.aws.amazon.com/sdk-for-go/api/service/pricing/#New -package pricing diff --git a/vendor/github.com/aws/aws-sdk-go/service/pricing/errors.go b/vendor/github.com/aws/aws-sdk-go/service/pricing/errors.go deleted file mode 100644 index fa9f184a3aa02..0000000000000 --- a/vendor/github.com/aws/aws-sdk-go/service/pricing/errors.go +++ /dev/null @@ -1,70 +0,0 @@ -// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. - -package pricing - -import ( - "github.com/aws/aws-sdk-go/private/protocol" -) - -const ( - - // ErrCodeAccessDeniedException for service response error code - // "AccessDeniedException". - // - // General authentication failure. The request wasn't signed correctly. - ErrCodeAccessDeniedException = "AccessDeniedException" - - // ErrCodeExpiredNextTokenException for service response error code - // "ExpiredNextTokenException". - // - // The pagination token expired. Try again without a pagination token. - ErrCodeExpiredNextTokenException = "ExpiredNextTokenException" - - // ErrCodeInternalErrorException for service response error code - // "InternalErrorException". - // - // An error on the server occurred during the processing of your request. Try - // again later. - ErrCodeInternalErrorException = "InternalErrorException" - - // ErrCodeInvalidNextTokenException for service response error code - // "InvalidNextTokenException". - // - // The pagination token is invalid. Try again without a pagination token. - ErrCodeInvalidNextTokenException = "InvalidNextTokenException" - - // ErrCodeInvalidParameterException for service response error code - // "InvalidParameterException". - // - // One or more parameters had an invalid value. - ErrCodeInvalidParameterException = "InvalidParameterException" - - // ErrCodeNotFoundException for service response error code - // "NotFoundException". - // - // The requested resource can't be found. - ErrCodeNotFoundException = "NotFoundException" - - // ErrCodeResourceNotFoundException for service response error code - // "ResourceNotFoundException". - // - // The requested resource can't be found. - ErrCodeResourceNotFoundException = "ResourceNotFoundException" - - // ErrCodeThrottlingException for service response error code - // "ThrottlingException". - // - // You've made too many requests exceeding service quotas. - ErrCodeThrottlingException = "ThrottlingException" -) - -var exceptionFromCode = map[string]func(protocol.ResponseMetadata) error{ - "AccessDeniedException": newErrorAccessDeniedException, - "ExpiredNextTokenException": newErrorExpiredNextTokenException, - "InternalErrorException": newErrorInternalErrorException, - "InvalidNextTokenException": newErrorInvalidNextTokenException, - "InvalidParameterException": newErrorInvalidParameterException, - "NotFoundException": newErrorNotFoundException, - "ResourceNotFoundException": newErrorResourceNotFoundException, - "ThrottlingException": newErrorThrottlingException, -} diff --git a/vendor/github.com/aws/aws-sdk-go/service/pricing/pricingiface/interface.go b/vendor/github.com/aws/aws-sdk-go/service/pricing/pricingiface/interface.go deleted file mode 100644 index e0ddf9dbc6811..0000000000000 --- a/vendor/github.com/aws/aws-sdk-go/service/pricing/pricingiface/interface.go +++ /dev/null @@ -1,96 +0,0 @@ -// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. - -// Package pricingiface provides an interface to enable mocking the AWS Price List Service service client -// for testing your code. -// -// It is important to note that this interface will have breaking changes -// when the service model is updated and adds new API operations, paginators, -// and waiters. -package pricingiface - -import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/pricing" -) - -// PricingAPI provides an interface to enable mocking the -// pricing.Pricing service client's API operation, -// paginators, and waiters. This make unit testing your code that calls out -// to the SDK's service client's calls easier. -// -// The best way to use this interface is so the SDK's service client's calls -// can be stubbed out for unit testing your code with the SDK without needing -// to inject custom request handlers into the SDK's request pipeline. -// -// // myFunc uses an SDK service client to make a request to -// // AWS Price List Service. -// func myFunc(svc pricingiface.PricingAPI) bool { -// // Make svc.DescribeServices request -// } -// -// func main() { -// sess := session.New() -// svc := pricing.New(sess) -// -// myFunc(svc) -// } -// -// In your _test.go file: -// -// // Define a mock struct to be used in your unit tests of myFunc. -// type mockPricingClient struct { -// pricingiface.PricingAPI -// } -// func (m *mockPricingClient) DescribeServices(input *pricing.DescribeServicesInput) (*pricing.DescribeServicesOutput, error) { -// // mock response/functionality -// } -// -// func TestMyFunc(t *testing.T) { -// // Setup Test -// mockSvc := &mockPricingClient{} -// -// myfunc(mockSvc) -// -// // Verify myFunc's functionality -// } -// -// It is important to note that this interface will have breaking changes -// when the service model is updated and adds new API operations, paginators, -// and waiters. Its suggested to use the pattern above for testing, or using -// tooling to generate mocks to satisfy the interfaces. -type PricingAPI interface { - DescribeServices(*pricing.DescribeServicesInput) (*pricing.DescribeServicesOutput, error) - DescribeServicesWithContext(aws.Context, *pricing.DescribeServicesInput, ...request.Option) (*pricing.DescribeServicesOutput, error) - DescribeServicesRequest(*pricing.DescribeServicesInput) (*request.Request, *pricing.DescribeServicesOutput) - - DescribeServicesPages(*pricing.DescribeServicesInput, func(*pricing.DescribeServicesOutput, bool) bool) error - DescribeServicesPagesWithContext(aws.Context, *pricing.DescribeServicesInput, func(*pricing.DescribeServicesOutput, bool) bool, ...request.Option) error - - GetAttributeValues(*pricing.GetAttributeValuesInput) (*pricing.GetAttributeValuesOutput, error) - GetAttributeValuesWithContext(aws.Context, *pricing.GetAttributeValuesInput, ...request.Option) (*pricing.GetAttributeValuesOutput, error) - GetAttributeValuesRequest(*pricing.GetAttributeValuesInput) (*request.Request, *pricing.GetAttributeValuesOutput) - - GetAttributeValuesPages(*pricing.GetAttributeValuesInput, func(*pricing.GetAttributeValuesOutput, bool) bool) error - GetAttributeValuesPagesWithContext(aws.Context, *pricing.GetAttributeValuesInput, func(*pricing.GetAttributeValuesOutput, bool) bool, ...request.Option) error - - GetPriceListFileUrl(*pricing.GetPriceListFileUrlInput) (*pricing.GetPriceListFileUrlOutput, error) - GetPriceListFileUrlWithContext(aws.Context, *pricing.GetPriceListFileUrlInput, ...request.Option) (*pricing.GetPriceListFileUrlOutput, error) - GetPriceListFileUrlRequest(*pricing.GetPriceListFileUrlInput) (*request.Request, *pricing.GetPriceListFileUrlOutput) - - GetProducts(*pricing.GetProductsInput) (*pricing.GetProductsOutput, error) - GetProductsWithContext(aws.Context, *pricing.GetProductsInput, ...request.Option) (*pricing.GetProductsOutput, error) - GetProductsRequest(*pricing.GetProductsInput) (*request.Request, *pricing.GetProductsOutput) - - GetProductsPages(*pricing.GetProductsInput, func(*pricing.GetProductsOutput, bool) bool) error - GetProductsPagesWithContext(aws.Context, *pricing.GetProductsInput, func(*pricing.GetProductsOutput, bool) bool, ...request.Option) error - - ListPriceLists(*pricing.ListPriceListsInput) (*pricing.ListPriceListsOutput, error) - ListPriceListsWithContext(aws.Context, *pricing.ListPriceListsInput, ...request.Option) (*pricing.ListPriceListsOutput, error) - ListPriceListsRequest(*pricing.ListPriceListsInput) (*request.Request, *pricing.ListPriceListsOutput) - - ListPriceListsPages(*pricing.ListPriceListsInput, func(*pricing.ListPriceListsOutput, bool) bool) error - ListPriceListsPagesWithContext(aws.Context, *pricing.ListPriceListsInput, func(*pricing.ListPriceListsOutput, bool) bool, ...request.Option) error -} - -var _ PricingAPI = (*pricing.Pricing)(nil) diff --git a/vendor/github.com/aws/aws-sdk-go/service/pricing/service.go b/vendor/github.com/aws/aws-sdk-go/service/pricing/service.go deleted file mode 100644 index 5c552002c4782..0000000000000 --- a/vendor/github.com/aws/aws-sdk-go/service/pricing/service.go +++ /dev/null @@ -1,108 +0,0 @@ -// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT. - -package pricing - -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" - "github.com/aws/aws-sdk-go/private/protocol/jsonrpc" -) - -// Pricing provides the API operation methods for making requests to -// AWS Price List Service. See this package's package overview docs -// for details on the service. -// -// Pricing methods are safe to use concurrently. It is not safe to -// modify mutate any of the struct's properties though. -type Pricing 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 = "api.pricing" // Name of service. - EndpointsID = ServiceName // ID to lookup a service endpoint with. - ServiceID = "Pricing" // ServiceID is a unique identifier of a specific service. -) - -// New creates a new instance of the Pricing 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: -// -// mySession := session.Must(session.NewSession()) -// -// // Create a Pricing client from just a session. -// svc := pricing.New(mySession) -// -// // Create a Pricing client with additional configuration -// svc := pricing.New(mySession, aws.NewConfig().WithRegion("us-west-2")) -func New(p client.ConfigProvider, cfgs ...*aws.Config) *Pricing { - c := p.ClientConfig(EndpointsID, cfgs...) - if c.SigningNameDerived || len(c.SigningName) == 0 { - c.SigningName = "pricing" - } - return newClient(*c.Config, c.Handlers, c.PartitionID, c.Endpoint, c.SigningRegion, c.SigningName, c.ResolvedRegion) -} - -// newClient creates, initializes and returns a new service client instance. -func newClient(cfg aws.Config, handlers request.Handlers, partitionID, endpoint, signingRegion, signingName, resolvedRegion string) *Pricing { - svc := &Pricing{ - Client: client.New( - cfg, - metadata.ClientInfo{ - ServiceName: ServiceName, - ServiceID: ServiceID, - SigningName: signingName, - SigningRegion: signingRegion, - PartitionID: partitionID, - Endpoint: endpoint, - APIVersion: "2017-10-15", - ResolvedRegion: resolvedRegion, - JSONVersion: "1.1", - TargetPrefix: "AWSPriceListService", - }, - 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( - protocol.NewUnmarshalErrorHandler(jsonrpc.NewUnmarshalTypedError(exceptionFromCode)).NamedHandler(), - ) - - // Run custom client initialization if present - if initClient != nil { - initClient(svc.Client) - } - - return svc -} - -// newRequest creates a new request for a Pricing operation and runs any -// custom request initialization. -func (c *Pricing) 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 -} diff --git a/vendor/github.com/aymanbagabas/go-osc52/v2/LICENSE b/vendor/github.com/aymanbagabas/go-osc52/v2/LICENSE new file mode 100644 index 0000000000000..25cec1ed48873 --- /dev/null +++ b/vendor/github.com/aymanbagabas/go-osc52/v2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Ayman Bagabas + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/aymanbagabas/go-osc52/v2/README.md b/vendor/github.com/aymanbagabas/go-osc52/v2/README.md new file mode 100644 index 0000000000000..4de3a22d14c54 --- /dev/null +++ b/vendor/github.com/aymanbagabas/go-osc52/v2/README.md @@ -0,0 +1,83 @@ + +# go-osc52 + +

+ Latest Release + GoDoc +

+ +A Go library to work with the [ANSI OSC52](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands) terminal sequence. + +## Usage + +You can use this small library to construct an ANSI OSC52 sequence suitable for +your terminal. + + +### Example + +```go +import ( + "os" + "fmt" + + "github.com/aymanbagabas/go-osc52/v2" +) + +func main() { + s := "Hello World!" + + // Copy `s` to system clipboard + osc52.New(s).WriteTo(os.Stderr) + + // Copy `s` to primary clipboard (X11) + osc52.New(s).Primary().WriteTo(os.Stderr) + + // Query the clipboard + osc52.Query().WriteTo(os.Stderr) + + // Clear system clipboard + osc52.Clear().WriteTo(os.Stderr) + + // Use the fmt.Stringer interface to copy `s` to system clipboard + fmt.Fprint(os.Stderr, osc52.New(s)) + + // Or to primary clipboard + fmt.Fprint(os.Stderr, osc52.New(s).Primary()) +} +``` + +## SSH Example + +You can use this over SSH using [gliderlabs/ssh](https://github.com/gliderlabs/ssh) for instance: + +```go +var sshSession ssh.Session +seq := osc52.New("Hello awesome!") +// Check if term is screen or tmux +pty, _, _ := s.Pty() +if pty.Term == "screen" { + seq = seq.Screen() +} else if isTmux { + seq = seq.Tmux() +} +seq.WriteTo(sshSession.Stderr()) +``` + +## Tmux + +Make sure you have `set-clipboard on` in your config, otherwise, tmux won't +allow your application to access the clipboard [^1]. + +Using the tmux option, `osc52.TmuxMode` or `osc52.New(...).Tmux()`, wraps the +OSC52 sequence in a special tmux DCS sequence and pass it to the outer +terminal. This requires `allow-passthrough on` in your config. +`allow-passthrough` is no longer enabled by default +[since tmux 3.3a](https://github.com/tmux/tmux/issues/3218#issuecomment-1153089282) [^2]. + +[^1]: See [tmux clipboard](https://github.com/tmux/tmux/wiki/Clipboard) +[^2]: [What is allow-passthrough](https://github.com/tmux/tmux/wiki/FAQ#what-is-the-passthrough-escape-sequence-and-how-do-i-use-it) + +## Credits + +* [vim-oscyank](https://github.com/ojroques/vim-oscyank) this is heavily inspired by vim-oscyank. diff --git a/vendor/github.com/aymanbagabas/go-osc52/v2/osc52.go b/vendor/github.com/aymanbagabas/go-osc52/v2/osc52.go new file mode 100644 index 0000000000000..dc758d2869d5c --- /dev/null +++ b/vendor/github.com/aymanbagabas/go-osc52/v2/osc52.go @@ -0,0 +1,305 @@ +// OSC52 is a terminal escape sequence that allows copying text to the clipboard. +// +// The sequence consists of the following: +// +// OSC 52 ; Pc ; Pd BEL +// +// Pc is the clipboard choice: +// +// c: clipboard +// p: primary +// q: secondary (not supported) +// s: select (not supported) +// 0-7: cut-buffers (not supported) +// +// Pd is the data to copy to the clipboard. This string should be encoded in +// base64 (RFC-4648). +// +// If Pd is "?", the terminal replies to the host with the current contents of +// the clipboard. +// +// If Pd is neither a base64 string nor "?", the terminal clears the clipboard. +// +// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands +// where Ps = 52 => Manipulate Selection Data. +// +// Examples: +// +// // copy "hello world" to the system clipboard +// fmt.Fprint(os.Stderr, osc52.New("hello world")) +// +// // copy "hello world" to the primary Clipboard +// fmt.Fprint(os.Stderr, osc52.New("hello world").Primary()) +// +// // limit the size of the string to copy 10 bytes +// fmt.Fprint(os.Stderr, osc52.New("0123456789").Limit(10)) +// +// // escape the OSC52 sequence for screen using DCS sequences +// fmt.Fprint(os.Stderr, osc52.New("hello world").Screen()) +// +// // escape the OSC52 sequence for Tmux +// fmt.Fprint(os.Stderr, osc52.New("hello world").Tmux()) +// +// // query the system Clipboard +// fmt.Fprint(os.Stderr, osc52.Query()) +// +// // query the primary clipboard +// fmt.Fprint(os.Stderr, osc52.Query().Primary()) +// +// // clear the system Clipboard +// fmt.Fprint(os.Stderr, osc52.Clear()) +// +// // clear the primary Clipboard +// fmt.Fprint(os.Stderr, osc52.Clear().Primary()) +package osc52 + +import ( + "encoding/base64" + "fmt" + "io" + "strings" +) + +// Clipboard is the clipboard buffer to use. +type Clipboard rune + +const ( + // SystemClipboard is the system clipboard buffer. + SystemClipboard Clipboard = 'c' + // PrimaryClipboard is the primary clipboard buffer (X11). + PrimaryClipboard = 'p' +) + +// Mode is the mode to use for the OSC52 sequence. +type Mode uint + +const ( + // DefaultMode is the default OSC52 sequence mode. + DefaultMode Mode = iota + // ScreenMode escapes the OSC52 sequence for screen using DCS sequences. + ScreenMode + // TmuxMode escapes the OSC52 sequence for tmux. Not needed if tmux + // clipboard is set to `set-clipboard on` + TmuxMode +) + +// Operation is the OSC52 operation. +type Operation uint + +const ( + // SetOperation is the copy operation. + SetOperation Operation = iota + // QueryOperation is the query operation. + QueryOperation + // ClearOperation is the clear operation. + ClearOperation +) + +// Sequence is the OSC52 sequence. +type Sequence struct { + str string + limit int + op Operation + mode Mode + clipboard Clipboard +} + +var _ fmt.Stringer = Sequence{} + +var _ io.WriterTo = Sequence{} + +// String returns the OSC52 sequence. +func (s Sequence) String() string { + var seq strings.Builder + // mode escape sequences start + seq.WriteString(s.seqStart()) + // actual OSC52 sequence start + seq.WriteString(fmt.Sprintf("\x1b]52;%c;", s.clipboard)) + switch s.op { + case SetOperation: + str := s.str + if s.limit > 0 && len(str) > s.limit { + return "" + } + b64 := base64.StdEncoding.EncodeToString([]byte(str)) + switch s.mode { + case ScreenMode: + // Screen doesn't support OSC52 but will pass the contents of a DCS + // sequence to the outer terminal unchanged. + // + // Here, we split the encoded string into 76 bytes chunks and then + // join the chunks with sequences. Finally, + // wrap the whole thing in + // . + // s := strings.SplitN(b64, "", 76) + s := make([]string, 0, len(b64)/76+1) + for i := 0; i < len(b64); i += 76 { + end := i + 76 + if end > len(b64) { + end = len(b64) + } + s = append(s, b64[i:end]) + } + seq.WriteString(strings.Join(s, "\x1b\\\x1bP")) + default: + seq.WriteString(b64) + } + case QueryOperation: + // OSC52 queries the clipboard using "?" + seq.WriteString("?") + case ClearOperation: + // OSC52 clears the clipboard if the data is neither a base64 string nor "?" + // we're using "!" as a default + seq.WriteString("!") + } + // actual OSC52 sequence end + seq.WriteString("\x07") + // mode escape end + seq.WriteString(s.seqEnd()) + return seq.String() +} + +// WriteTo writes the OSC52 sequence to the writer. +func (s Sequence) WriteTo(out io.Writer) (int64, error) { + n, err := out.Write([]byte(s.String())) + return int64(n), err +} + +// Mode sets the mode for the OSC52 sequence. +func (s Sequence) Mode(m Mode) Sequence { + s.mode = m + return s +} + +// Tmux sets the mode to TmuxMode. +// Used to escape the OSC52 sequence for `tmux`. +// +// Note: this is not needed if tmux clipboard is set to `set-clipboard on`. If +// TmuxMode is used, tmux must have `allow-passthrough on` set. +// +// This is a syntactic sugar for s.Mode(TmuxMode). +func (s Sequence) Tmux() Sequence { + return s.Mode(TmuxMode) +} + +// Screen sets the mode to ScreenMode. +// Used to escape the OSC52 sequence for `screen`. +// +// This is a syntactic sugar for s.Mode(ScreenMode). +func (s Sequence) Screen() Sequence { + return s.Mode(ScreenMode) +} + +// Clipboard sets the clipboard buffer for the OSC52 sequence. +func (s Sequence) Clipboard(c Clipboard) Sequence { + s.clipboard = c + return s +} + +// Primary sets the clipboard buffer to PrimaryClipboard. +// This is the X11 primary clipboard. +// +// This is a syntactic sugar for s.Clipboard(PrimaryClipboard). +func (s Sequence) Primary() Sequence { + return s.Clipboard(PrimaryClipboard) +} + +// Limit sets the limit for the OSC52 sequence. +// The default limit is 0 (no limit). +// +// Strings longer than the limit get ignored. Settting the limit to 0 or a +// negative value disables the limit. Each terminal defines its own escapse +// sequence limit. +func (s Sequence) Limit(l int) Sequence { + if l < 0 { + s.limit = 0 + } else { + s.limit = l + } + return s +} + +// Operation sets the operation for the OSC52 sequence. +// The default operation is SetOperation. +func (s Sequence) Operation(o Operation) Sequence { + s.op = o + return s +} + +// Clear sets the operation to ClearOperation. +// This clears the clipboard. +// +// This is a syntactic sugar for s.Operation(ClearOperation). +func (s Sequence) Clear() Sequence { + return s.Operation(ClearOperation) +} + +// Query sets the operation to QueryOperation. +// This queries the clipboard contents. +// +// This is a syntactic sugar for s.Operation(QueryOperation). +func (s Sequence) Query() Sequence { + return s.Operation(QueryOperation) +} + +// SetString sets the string for the OSC52 sequence. Strings are joined with a +// space character. +func (s Sequence) SetString(strs ...string) Sequence { + s.str = strings.Join(strs, " ") + return s +} + +// New creates a new OSC52 sequence with the given string(s). Strings are +// joined with a space character. +func New(strs ...string) Sequence { + s := Sequence{ + str: strings.Join(strs, " "), + limit: 0, + mode: DefaultMode, + clipboard: SystemClipboard, + op: SetOperation, + } + return s +} + +// Query creates a new OSC52 sequence with the QueryOperation. +// This returns a new OSC52 sequence to query the clipboard contents. +// +// This is a syntactic sugar for New().Query(). +func Query() Sequence { + return New().Query() +} + +// Clear creates a new OSC52 sequence with the ClearOperation. +// This returns a new OSC52 sequence to clear the clipboard. +// +// This is a syntactic sugar for New().Clear(). +func Clear() Sequence { + return New().Clear() +} + +func (s Sequence) seqStart() string { + switch s.mode { + case TmuxMode: + // Write the start of a tmux escape sequence. + return "\x1bPtmux;\x1b" + case ScreenMode: + // Write the start of a DCS sequence. + return "\x1bP" + default: + return "" + } +} + +func (s Sequence) seqEnd() string { + switch s.mode { + case TmuxMode: + // Terminate the tmux escape sequence. + return "\x1b\\" + case ScreenMode: + // Write the end of a DCS sequence. + return "\x1b\x5c" + default: + return "" + } +} diff --git a/vendor/github.com/charmbracelet/bubbles/LICENSE b/vendor/github.com/charmbracelet/bubbles/LICENSE index e5892cfe9db85..31d76c1c6ea62 100644 --- a/vendor/github.com/charmbracelet/bubbles/LICENSE +++ b/vendor/github.com/charmbracelet/bubbles/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 Charmbracelet, Inc +Copyright (c) 2020-2023 Charmbracelet, Inc Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/charmbracelet/bubbles/cursor/cursor.go b/vendor/github.com/charmbracelet/bubbles/cursor/cursor.go new file mode 100644 index 0000000000000..5abda654e4634 --- /dev/null +++ b/vendor/github.com/charmbracelet/bubbles/cursor/cursor.go @@ -0,0 +1,207 @@ +package cursor + +import ( + "context" + "time" + + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" +) + +const defaultBlinkSpeed = time.Millisecond * 530 + +// initialBlinkMsg initializes cursor blinking. +type initialBlinkMsg struct{} + +// BlinkMsg signals that the cursor should blink. It contains metadata that +// allows us to tell if the blink message is the one we're expecting. +type BlinkMsg struct { + id int + tag int +} + +// blinkCanceled is sent when a blink operation is canceled. +type blinkCanceled struct{} + +// blinkCtx manages cursor blinking. +type blinkCtx struct { + ctx context.Context + cancel context.CancelFunc +} + +// Mode describes the behavior of the cursor. +type Mode int + +// Available cursor modes. +const ( + CursorBlink Mode = iota + CursorStatic + CursorHide +) + +// String returns the cursor mode in a human-readable format. This method is +// provisional and for informational purposes only. +func (c Mode) String() string { + return [...]string{ + "blink", + "static", + "hidden", + }[c] +} + +// Model is the Bubble Tea model for this cursor element. +type Model struct { + BlinkSpeed time.Duration + // Style for styling the cursor block. + Style lipgloss.Style + // TextStyle is the style used for the cursor when it is hidden (when blinking). + // I.e. displaying normal text. + TextStyle lipgloss.Style + + // char is the character under the cursor + char string + // The ID of this Model as it relates to other cursors + id int + // focus indicates whether the containing input is focused + focus bool + // Cursor Blink state. + Blink bool + // Used to manage cursor blink + blinkCtx *blinkCtx + // The ID of the blink message we're expecting to receive. + blinkTag int + // mode determines the behavior of the cursor + mode Mode +} + +// New creates a new model with default settings. +func New() Model { + return Model{ + BlinkSpeed: defaultBlinkSpeed, + + Blink: true, + mode: CursorBlink, + + blinkCtx: &blinkCtx{ + ctx: context.Background(), + }, + } +} + +// Update updates the cursor. +func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { + switch msg := msg.(type) { + case initialBlinkMsg: + // We accept all initialBlinkMsgs generated by the Blink command. + + if m.mode != CursorBlink || !m.focus { + return m, nil + } + + cmd := m.BlinkCmd() + return m, cmd + + case BlinkMsg: + // We're choosy about whether to accept blinkMsgs so that our cursor + // only exactly when it should. + + // Is this model blink-able? + if m.mode != CursorBlink || !m.focus { + return m, nil + } + + // Were we expecting this blink message? + if msg.id != m.id || msg.tag != m.blinkTag { + return m, nil + } + + var cmd tea.Cmd + if m.mode == CursorBlink { + m.Blink = !m.Blink + cmd = m.BlinkCmd() + } + return m, cmd + + case blinkCanceled: // no-op + return m, nil + } + return m, nil +} + +// Mode returns the model's cursor mode. For available cursor modes, see +// type Mode. +func (m Model) Mode() Mode { + return m.mode +} + +// SetMode sets the model's cursor mode. This method returns a command. +// +// For available cursor modes, see type CursorMode. +func (m *Model) SetMode(mode Mode) tea.Cmd { + m.mode = mode + m.Blink = m.mode == CursorHide || !m.focus + if mode == CursorBlink { + return Blink + } + return nil +} + +// BlinkCmd is a command used to manage cursor blinking. +func (m *Model) BlinkCmd() tea.Cmd { + if m.mode != CursorBlink { + return nil + } + + if m.blinkCtx != nil && m.blinkCtx.cancel != nil { + m.blinkCtx.cancel() + } + + ctx, cancel := context.WithTimeout(m.blinkCtx.ctx, m.BlinkSpeed) + m.blinkCtx.cancel = cancel + + m.blinkTag++ + + return func() tea.Msg { + defer cancel() + <-ctx.Done() + if ctx.Err() == context.DeadlineExceeded { + return BlinkMsg{id: m.id, tag: m.blinkTag} + } + return blinkCanceled{} + } +} + +// Blink is a command used to initialize cursor blinking. +func Blink() tea.Msg { + return initialBlinkMsg{} +} + +// Focus focuses the cursor to allow it to blink if desired. +func (m *Model) Focus() tea.Cmd { + m.focus = true + m.Blink = m.mode == CursorHide // show the cursor unless we've explicitly hidden it + + if m.mode == CursorBlink && m.focus { + return m.BlinkCmd() + } + return nil +} + +// Blur blurs the cursor. +func (m *Model) Blur() { + m.focus = false + m.Blink = true +} + +// SetChar sets the character under the cursor. +func (m *Model) SetChar(char string) { + m.char = char +} + +// View displays the cursor. +func (m Model) View() string { + if m.Blink { + return m.TextStyle.Inline(true).Render(m.char) + } + return m.Style.Inline(true).Reverse(true).Render(m.char) +} diff --git a/vendor/github.com/charmbracelet/bubbles/help/help.go b/vendor/github.com/charmbracelet/bubbles/help/help.go index 90971ac453e8c..8e5f77f1fd600 100644 --- a/vendor/github.com/charmbracelet/bubbles/help/help.go +++ b/vendor/github.com/charmbracelet/bubbles/help/help.go @@ -21,7 +21,7 @@ type KeyMap interface { // which the help items are returned here. ShortHelp() []key.Binding - // MoreHelp returns an extended group of help items, grouped by columns. + // FullHelp returns an extended group of help items, grouped by columns. // The help bubble will render the help in the order in which the help // items are returned here. FullHelp() [][]key.Binding @@ -92,11 +92,11 @@ func New() Model { // NewModel creates a new help view with some useful defaults. // -// Deprecated. Use New instead. +// Deprecated: use [New] instead. var NewModel = New // Update helps satisfy the Bubble Tea Model interface. It's a no-op. -func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { +func (m Model) Update(_ tea.Msg) (Model, tea.Cmd) { return m, nil } @@ -203,7 +203,7 @@ func (m Model) FullHelpView(groups [][]key.Binding) string { // Column totalWidth += lipgloss.Width(col) - if totalWidth > m.Width { + if m.Width > 0 && totalWidth > m.Width { break } @@ -212,7 +212,7 @@ func (m Model) FullHelpView(groups [][]key.Binding) string { // Separator if i < len(group)-1 { totalWidth += sepWidth - if totalWidth > m.Width { + if m.Width > 0 && totalWidth > m.Width { break } } diff --git a/vendor/github.com/charmbracelet/bubbles/key/key.go b/vendor/github.com/charmbracelet/bubbles/key/key.go index c35f7f02c8f6c..c7888fa772c60 100644 --- a/vendor/github.com/charmbracelet/bubbles/key/key.go +++ b/vendor/github.com/charmbracelet/bubbles/key/key.go @@ -2,35 +2,35 @@ // keymappings useful in Bubble Tea components. There are a few different ways // you can define a keymapping with this package. Here's one example: // -// type KeyMap struct { -// Up key.Binding -// Down key.Binding -// } +// type KeyMap struct { +// Up key.Binding +// Down key.Binding +// } // -// var DefaultKeyMap = KeyMap{ -// Up: key.NewBinding( -// key.WithKeys("k", "up"), // actual keybindings -// key.WithHelp("↑/k", "move up"), // corresponding help text -// ), -// Down: key.NewBinding( -// key.WithKeys("j", "down"), -// key.WithHelp("↓/j", "move down"), -// ), -// } +// var DefaultKeyMap = KeyMap{ +// Up: key.NewBinding( +// key.WithKeys("k", "up"), // actual keybindings +// key.WithHelp("↑/k", "move up"), // corresponding help text +// ), +// Down: key.NewBinding( +// key.WithKeys("j", "down"), +// key.WithHelp("↓/j", "move down"), +// ), +// } // -// func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { -// switch msg := msg.(type) { -// case tea.KeyMsg: -// switch { -// case key.Matches(msg, DefaultKeyMap.Up): -// // The user pressed up -// case key.Matches(msg, DefaultKeyMap.Down): -// // The user pressed down -// } -// } +// func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { +// switch msg := msg.(type) { +// case tea.KeyMsg: +// switch { +// case key.Matches(msg, DefaultKeyMap.Up): +// // The user pressed up +// case key.Matches(msg, DefaultKeyMap.Down): +// // The user pressed down +// } +// } // -// // ... -// } +// // ... +// } // // The help information, which is not used in the example above, can be used // to render help text for keystrokes in your views. @@ -106,7 +106,7 @@ func (b Binding) Help() Help { // keybindings won't be activated and won't show up in help. Keybindings are // enabled by default. func (b Binding) Enabled() bool { - return !b.disabled + return !b.disabled && b.keys != nil } // SetEnabled enables or disables the keybinding. @@ -130,9 +130,10 @@ type Help struct { // Matches checks if the given KeyMsg matches the given bindings. func Matches(k tea.KeyMsg, b ...Binding) bool { + keys := k.String() for _, binding := range b { for _, v := range binding.keys { - if k.String() == v && binding.Enabled() { + if keys == v && binding.Enabled() { return true } } diff --git a/vendor/github.com/charmbracelet/bubbles/list/README.md b/vendor/github.com/charmbracelet/bubbles/list/README.md new file mode 100644 index 0000000000000..303394de0f5ca --- /dev/null +++ b/vendor/github.com/charmbracelet/bubbles/list/README.md @@ -0,0 +1,72 @@ +# Frequently Asked Questions + +These are some of the most commonly asked questions regarding the `list` bubble. + +## Adding Custom Items + +There are a few things you need to do to create custom items. First off, they +need to implement the `list.Item` and `list.DefaultItem` interfaces. + +```go +// Item is an item that appears in the list. +type Item interface { + // FilterValue is the value we use when filtering against this item when + // we're filtering the list. + FilterValue() string +} +``` + +```go +// DefaultItem describes an items designed to work with DefaultDelegate. +type DefaultItem interface { + Item + Title() string + Description() string +} +``` + +You can see a working example in our [Kancli][kancli] project built +explicitly for a tutorial on lists and composite views in Bubble Tea. + +[VIDEO](https://youtu.be/ZA93qgdLUzM) + +## Customizing Styles + +Rendering (and behavior) for list items is done via the +[`ItemDelegate`][itemDelegate] +interface. It can be a little confusing at first, but it allows the list to be +very flexible and powerful. + +If you just want to alter the default style you could do something like: + +```go +import "github.com/charmbracelet/bubbles/list" + +// Create a new default delegate +d := list.NewDefaultDelegate() + +// Change colors +c := lipgloss.Color("#6f03fc") +d.Styles.SelectedTitle = d.Styles.SelectedTitle.Foreground(c).BorderLeftForeground(c) +d.Styles.SelectedDesc = d.Styles.SelectedTitle.Copy() // reuse the title style here + +// Initailize the list model with our delegate +width, height := 80, 40 +l := list.New(listItems, d, width, height) + +// You can also change the delegate on the fly +l.SetDelegate(d) +``` + +This code would replace [this line][replacedLine] in the [`list-default` +example][listDefault]. + +For full control over the way list items are rendered you can also define your +own `ItemDelegate` too ([example][customDelegate]). + + +[kancli]: https://github.com/charmbracelet/kancli/blob/main/main.go#L45 +[itemDelegate]: https://pkg.go.dev/github.com/charmbracelet/bubbles@v0.10.2/list#ItemDelegate +[replacedLine]: https://github.com/charmbracelet/bubbletea/blob/master/examples/list-default/main.go#L77 +[listDefault]: https://github.com/charmbracelet/bubbletea/tree/master/examples/list-default +[customDelegate]: https://github.com/charmbracelet/bubbletea/blob/a6f46172ec4436991b90c2270253b2d212de7ef3/examples/list-simple/main.go#L28-L49 diff --git a/vendor/github.com/charmbracelet/bubbles/list/defaultitem.go b/vendor/github.com/charmbracelet/bubbles/list/defaultitem.go index 74e4ee2252064..5474c0047dabb 100644 --- a/vendor/github.com/charmbracelet/bubbles/list/defaultitem.go +++ b/vendor/github.com/charmbracelet/bubbles/list/defaultitem.go @@ -26,7 +26,7 @@ type DefaultItemStyles struct { DimmedTitle lipgloss.Style DimmedDesc lipgloss.Style - // Charcters matching the current filter, if any. + // Characters matching the current filter, if any. FilterMatch lipgloss.Style } @@ -79,7 +79,7 @@ type DefaultItem interface { // ItemDelegate called, which is called when the list's Update function is // invoked. // -// Settings ShortHelpFunc and FullHelpFunc is optional. They can can be set to +// Settings ShortHelpFunc and FullHelpFunc is optional. They can be set to // include items in the list's default short and full help menus. type DefaultDelegate struct { ShowDescription bool @@ -116,7 +116,7 @@ func (d DefaultDelegate) Height() int { return 1 } -// SetSpacing set the delegate's spacing. +// SetSpacing sets the delegate's spacing. func (d *DefaultDelegate) SetSpacing(i int) { d.spacing = i } diff --git a/vendor/github.com/charmbracelet/bubbles/list/keys.go b/vendor/github.com/charmbracelet/bubbles/list/keys.go index 421a2476daa07..33220313dc7e6 100644 --- a/vendor/github.com/charmbracelet/bubbles/list/keys.go +++ b/vendor/github.com/charmbracelet/bubbles/list/keys.go @@ -3,7 +3,7 @@ package list import "github.com/charmbracelet/bubbles/key" // KeyMap defines keybindings. It satisfies to the help.KeyMap interface, which -// is used to render the menu menu. +// is used to render the menu. type KeyMap struct { // Keybindings used when browsing the list. CursorUp key.Binding diff --git a/vendor/github.com/charmbracelet/bubbles/list/list.go b/vendor/github.com/charmbracelet/bubbles/list/list.go index f755499e931ff..f286274c6d25b 100644 --- a/vendor/github.com/charmbracelet/bubbles/list/list.go +++ b/vendor/github.com/charmbracelet/bubbles/list/list.go @@ -24,7 +24,7 @@ import ( // Item is an item that appears in the list. type Item interface { - // Filter value is the value we use when filtering against this item when + // FilterValue is the value we use when filtering against this item when // we're filtering the list. FilterValue() string } @@ -132,8 +132,9 @@ type Model struct { itemNameSingular string itemNamePlural string - Title string - Styles Styles + Title string + Styles Styles + InfiniteScrolling bool // Key mappings for navigating the list. KeyMap KeyMap @@ -183,18 +184,18 @@ type Model struct { func New(items []Item, delegate ItemDelegate, width, height int) Model { styles := DefaultStyles() - sp := spinner.NewModel() + sp := spinner.New() sp.Spinner = spinner.Line sp.Style = styles.Spinner - filterInput := textinput.NewModel() + filterInput := textinput.New() filterInput.Prompt = "Filter: " filterInput.PromptStyle = styles.FilterPrompt - filterInput.CursorStyle = styles.FilterCursor + filterInput.Cursor.Style = styles.FilterCursor filterInput.CharLimit = 64 filterInput.Focus() - p := paginator.NewModel() + p := paginator.New() p.Type = paginator.Dots p.ActiveDot = styles.ActivePaginationDot.String() p.InactiveDot = styles.InactivePaginationDot.String() @@ -221,7 +222,7 @@ func New(items []Item, delegate ItemDelegate, width, height int) Model { items: items, Paginator: p, spinner: sp, - Help: help.NewModel(), + Help: help.New(), } m.updatePagination() @@ -231,7 +232,7 @@ func New(items []Item, delegate ItemDelegate, width, height int) Model { // NewModel returns a new model with sensible defaults. // -// Deprecated. Use New instead. +// Deprecated: use [New] instead. var NewModel = New // SetFilteringEnabled enables or disables filtering. Note that this is different @@ -291,7 +292,7 @@ func (m Model) ShowStatusBar() bool { return m.showStatusBar } -// SetStatusBarItemName defines a replacement for the items identifier. +// SetStatusBarItemName defines a replacement for the item's identifier. // Defaults to item/items. func (m *Model) SetStatusBarItemName(singular, plural string) { m.itemNameSingular = singular @@ -303,7 +304,7 @@ func (m Model) StatusBarItemName() (string, string) { return m.itemNameSingular, m.itemNamePlural } -// SetShowPagination hides or shoes the paginator. Note that pagination will +// SetShowPagination hides or shows the paginator. Note that pagination will // still be active, it simply won't be displayed. func (m *Model) SetShowPagination(v bool) { m.showPagination = v @@ -331,7 +332,7 @@ func (m Model) Items() []Item { return m.items } -// Set the items available in the list. This returns a command. +// SetItems sets the items available in the list. This returns a command. func (m *Model) SetItems(i []Item) tea.Cmd { var cmd tea.Cmd m.items = i @@ -362,7 +363,7 @@ func (m *Model) ResetFilter() { m.resetFiltering() } -// Replace an item at the given index. This returns a command. +// SetItem replaces an item at the given index. This returns a command. func (m *Model) SetItem(index int, item Item) tea.Cmd { var cmd tea.Cmd m.items[index] = item @@ -375,8 +376,8 @@ func (m *Model) SetItem(index int, item Item) tea.Cmd { return cmd } -// Insert an item at the given index. If index is out of the upper bound, the -// item will be appended. This returns a command. +// InsertItem inserts an item at the given index. If the index is out of the upper bound, +// the item will be appended. This returns a command. func (m *Model) InsertItem(index int, item Item) tea.Cmd { var cmd tea.Cmd m.items = insertItemIntoSlice(m.items, item, index) @@ -404,7 +405,7 @@ func (m *Model) RemoveItem(index int) { m.updatePagination() } -// Set the item delegate. +// SetDelegate sets the item delegate. func (m *Model) SetDelegate(d ItemDelegate) { m.delegate = d m.updatePagination() @@ -418,7 +419,7 @@ func (m Model) VisibleItems() []Item { return m.items } -// SelectedItems returns the current selected item in the list. +// SelectedItem returns the current selected item in the list. func (m Model) SelectedItem() Item { i := m.Index() @@ -459,6 +460,13 @@ func (m *Model) CursorUp() { // If we're at the start, stop if m.cursor < 0 && m.Paginator.Page == 0 { + // if infinite scrolling is enabled, go to the last item + if m.InfiniteScrolling { + m.Paginator.Page = m.Paginator.TotalPages - 1 + m.cursor = m.Paginator.ItemsOnPage(len(m.VisibleItems())) - 1 + return + } + m.cursor = 0 return } @@ -501,6 +509,12 @@ func (m *Model) CursorDown() { } m.cursor = itemsOnPage - 1 + + // if infinite scrolling is enabled, go to the first item + if m.InfiniteScrolling { + m.Paginator.Page = 0 + m.cursor = 0 + } } // PrevPage moves to the previous page, if available. @@ -526,7 +540,7 @@ func (m Model) FilterValue() string { // SettingFilter returns whether or not the user is currently editing the // filter value. It's purely a convenience method for the following: // -// m.FilterState() == Filtering +// m.FilterState() == Filtering // // It's included here because it's a common thing to check for when // implementing this component. @@ -534,6 +548,14 @@ func (m Model) SettingFilter() bool { return m.filterState == Filtering } +// IsFiltered returns whether or not the list is currently filtered. +// It's purely a convenience method for the following: +// +// m.FilterState() == FilterApplied +func (m Model) IsFiltered() bool { + return m.filterState == FilterApplied +} + // Width returns the current width setting. func (m Model) Width() int { return m.width @@ -549,7 +571,7 @@ func (m *Model) SetSpinner(spinner spinner.Spinner) { m.spinner.Spinner = spinner } -// Toggle the spinner. Note that this also returns a command. +// ToggleSpinner toggles the spinner. Note that this also returns a command. func (m *Model) ToggleSpinner() tea.Cmd { if !m.showSpinner { return m.StartSpinner() @@ -561,7 +583,7 @@ func (m *Model) ToggleSpinner() tea.Cmd { // StartSpinner starts the spinner. Note that this returns a command. func (m *Model) StartSpinner() tea.Cmd { m.showSpinner = true - return spinner.Tick + return m.spinner.Tick } // StopSpinner stops the spinner. @@ -569,8 +591,8 @@ func (m *Model) StopSpinner() { m.showSpinner = false } -// Helper for disabling the keybindings used for quitting, in case you want to -// handle this elsewhere in your application. +// DisableQuitKeybindings is a helper for disabling the keybindings used for quitting, +// in case you want to handle this elsewhere in your application. func (m *Model) DisableQuitKeybindings() { m.disableQuitKeybindings = true m.KeyMap.Quit.SetEnabled(false) @@ -638,7 +660,7 @@ func (m Model) itemsAsFilterItems() filteredItems { item: item, } } - return filteredItems(fi) + return fi } // Set keybindings according to the filter state. diff --git a/vendor/github.com/charmbracelet/bubbles/paginator/paginator.go b/vendor/github.com/charmbracelet/bubbles/paginator/paginator.go index a84819ac863d9..bb8aad508aced 100644 --- a/vendor/github.com/charmbracelet/bubbles/paginator/paginator.go +++ b/vendor/github.com/charmbracelet/bubbles/paginator/paginator.go @@ -1,4 +1,4 @@ -// Package paginator provides a Bubble Tea package for calulating pagination +// Package paginator provides a Bubble Tea package for calculating pagination // and rendering pagination info. Note that this package does not render actual // pages: it's purely for handling keystrokes related to pagination, and // rendering pagination status. @@ -7,6 +7,7 @@ package paginator import ( "fmt" + "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" ) @@ -19,24 +20,53 @@ const ( Dots ) +// KeyMap is the key bindings for different actions within the paginator. +type KeyMap struct { + PrevPage key.Binding + NextPage key.Binding +} + +// DefaultKeyMap is the default set of key bindings for navigating and acting +// upon the paginator. +var DefaultKeyMap = KeyMap{ + PrevPage: key.NewBinding(key.WithKeys("pgup", "left", "h")), + NextPage: key.NewBinding(key.WithKeys("pgdown", "right", "l")), +} + // Model is the Bubble Tea model for this user interface. type Model struct { - Type Type - Page int - PerPage int - TotalPages int - ActiveDot string - InactiveDot string - ArabicFormat string + // Type configures how the pagination is rendered (Arabic, Dots). + Type Type + // Page is the current page number. + Page int + // PerPage is the number of items per page. + PerPage int + // TotalPages is the total number of pages. + TotalPages int + // ActiveDot is used to mark the current page under the Dots display type. + ActiveDot string + // InactiveDot is used to mark inactive pages under the Dots display type. + InactiveDot string + // ArabicFormat is the printf-style format to use for the Arabic display type. + ArabicFormat string + + // KeyMap encodes the keybindings recognized by the widget. + KeyMap KeyMap + + // Deprecated: customize [KeyMap] instead. UsePgUpPgDownKeys bool - UseLeftRightKeys bool - UseUpDownKeys bool - UseHLKeys bool - UseJKKeys bool + // Deprecated: customize [KeyMap] instead. + UseLeftRightKeys bool + // Deprecated: customize [KeyMap] instead. + UseUpDownKeys bool + // Deprecated: customize [KeyMap] instead. + UseHLKeys bool + // Deprecated: customize [KeyMap] instead. + UseJKKeys bool } // SetTotalPages is a helper function for calculating the total number of pages -// from a given number of items. It's use is optional since this pager can be +// from a given number of items. Its use is optional since this pager can be // used for other things beyond navigating sets. Note that it both returns the // number of total pages and alters the model. func (m *Model) SetTotalPages(items int) int { @@ -51,8 +81,8 @@ func (m *Model) SetTotalPages(items int) int { return n } -// ItemsOnPage is a helper function for returning the numer of items on the -// current page given the total numer of items passed as an argument. +// ItemsOnPage is a helper function for returning the number of items on the +// current page given the total number of items passed as an argument. func (m Model) ItemsOnPage(totalItems int) int { if totalItems < 1 { return 0 @@ -63,19 +93,18 @@ func (m Model) ItemsOnPage(totalItems int) int { // GetSliceBounds is a helper function for paginating slices. Pass the length // of the slice you're rendering and you'll receive the start and end bounds -// corresponding the to pagination. For example: -// -// bunchOfStuff := []stuff{...} -// start, end := model.GetSliceBounds(len(bunchOfStuff)) -// sliceToRender := bunchOfStuff[start:end] +// corresponding to the pagination. For example: // +// bunchOfStuff := []stuff{...} +// start, end := model.GetSliceBounds(len(bunchOfStuff)) +// sliceToRender := bunchOfStuff[start:end] func (m *Model) GetSliceBounds(length int) (start int, end int) { start = m.Page * m.PerPage end = min(m.Page*m.PerPage+m.PerPage, length) return start, end } -// PrevPage is a number function for navigating one page backward. It will not +// PrevPage is a helper function for navigating one page backward. It will not // page beyond the first page (i.e. page 0). func (m *Model) PrevPage() { if m.Page > 0 { @@ -99,69 +128,31 @@ func (m Model) OnLastPage() bool { // New creates a new model with defaults. func New() Model { return Model{ - Type: Arabic, - Page: 0, - PerPage: 1, - TotalPages: 1, - ActiveDot: "•", - InactiveDot: "○", - ArabicFormat: "%d/%d", - UsePgUpPgDownKeys: true, - UseLeftRightKeys: true, - UseUpDownKeys: false, - UseHLKeys: true, - UseJKKeys: false, + Type: Arabic, + Page: 0, + PerPage: 1, + TotalPages: 1, + KeyMap: DefaultKeyMap, + ActiveDot: "•", + InactiveDot: "○", + ArabicFormat: "%d/%d", } } // NewModel creates a new model with defaults. // -// Deprecated. Use New instead. +// Deprecated: use [New] instead. var NewModel = New // Update is the Tea update function which binds keystrokes to pagination. func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: - if m.UsePgUpPgDownKeys { - switch msg.String() { - case "pgup": - m.PrevPage() - case "pgdown": - m.NextPage() - } - } - if m.UseLeftRightKeys { - switch msg.String() { - case "left": - m.PrevPage() - case "right": - m.NextPage() - } - } - if m.UseUpDownKeys { - switch msg.String() { - case "up": - m.PrevPage() - case "down": - m.NextPage() - } - } - if m.UseHLKeys { - switch msg.String() { - case "h": - m.PrevPage() - case "l": - m.NextPage() - } - } - if m.UseJKKeys { - switch msg.String() { - case "j": - m.PrevPage() - case "k": - m.NextPage() - } + switch { + case key.Matches(msg, m.KeyMap.NextPage): + m.NextPage() + case key.Matches(msg, m.KeyMap.PrevPage): + m.PrevPage() } } diff --git a/vendor/github.com/charmbracelet/bubbles/runeutil/runeutil.go b/vendor/github.com/charmbracelet/bubbles/runeutil/runeutil.go new file mode 100644 index 0000000000000..82ea90a2edcea --- /dev/null +++ b/vendor/github.com/charmbracelet/bubbles/runeutil/runeutil.go @@ -0,0 +1,102 @@ +// Package runeutil provides a utility function for use in Bubbles +// that can process Key messages containing runes. +package runeutil + +import ( + "unicode" + "unicode/utf8" +) + +// Sanitizer is a helper for bubble widgets that want to process +// Runes from input key messages. +type Sanitizer interface { + // Sanitize removes control characters from runes in a KeyRunes + // message, and optionally replaces newline/carriage return/tabs by a + // specified character. + // + // The rune array is modified in-place if possible. In that case, the + // returned slice is the original slice shortened after the control + // characters have been removed/translated. + Sanitize(runes []rune) []rune +} + +// NewSanitizer constructs a rune sanitizer. +func NewSanitizer(opts ...Option) Sanitizer { + s := sanitizer{ + replaceNewLine: []rune("\n"), + replaceTab: []rune(" "), + } + for _, o := range opts { + s = o(s) + } + return &s +} + +// Option is the type of option that can be passed to Sanitize(). +type Option func(sanitizer) sanitizer + +// ReplaceTabs replaces tabs by the specified string. +func ReplaceTabs(tabRepl string) Option { + return func(s sanitizer) sanitizer { + s.replaceTab = []rune(tabRepl) + return s + } +} + +// ReplaceNewlines replaces newline characters by the specified string. +func ReplaceNewlines(nlRepl string) Option { + return func(s sanitizer) sanitizer { + s.replaceNewLine = []rune(nlRepl) + return s + } +} + +func (s *sanitizer) Sanitize(runes []rune) []rune { + // dstrunes are where we are storing the result. + dstrunes := runes[:0:len(runes)] + // copied indicates whether dstrunes is an alias of runes + // or a copy. We need a copy when dst moves past src. + // We use this as an optimization to avoid allocating + // a new rune slice in the common case where the output + // is smaller or equal to the input. + copied := false + + for src := 0; src < len(runes); src++ { + r := runes[src] + switch { + case r == utf8.RuneError: + // skip + + case r == '\r' || r == '\n': + if len(dstrunes)+len(s.replaceNewLine) > src && !copied { + dst := len(dstrunes) + dstrunes = make([]rune, dst, len(runes)+len(s.replaceNewLine)) + copy(dstrunes, runes[:dst]) + copied = true + } + dstrunes = append(dstrunes, s.replaceNewLine...) + + case r == '\t': + if len(dstrunes)+len(s.replaceTab) > src && !copied { + dst := len(dstrunes) + dstrunes = make([]rune, dst, len(runes)+len(s.replaceTab)) + copy(dstrunes, runes[:dst]) + copied = true + } + dstrunes = append(dstrunes, s.replaceTab...) + + case unicode.IsControl(r): + // Other control characters: skip. + + default: + // Keep the character. + dstrunes = append(dstrunes, runes[src]) + } + } + return dstrunes +} + +type sanitizer struct { + replaceNewLine []rune + replaceTab []rune +} diff --git a/vendor/github.com/charmbracelet/bubbles/spinner/spinner.go b/vendor/github.com/charmbracelet/bubbles/spinner/spinner.go index e0341122b3241..bb53597fefd1f 100644 --- a/vendor/github.com/charmbracelet/bubbles/spinner/spinner.go +++ b/vendor/github.com/charmbracelet/bubbles/spinner/spinner.go @@ -83,9 +83,13 @@ var ( Frames: []string{"☱", "☲", "☴", "☲"}, FPS: time.Second / 3, //nolint:gomnd } + Ellipsis = Spinner{ + Frames: []string{"", ".", "..", "..."}, + FPS: time.Second / 3, //nolint:gomnd + } ) -// Model contains the state for the spinner. Use NewModel to create new models +// Model contains the state for the spinner. Use New to create new models // rather than using Model as a struct literal. type Model struct { // Spinner settings to use. See type Spinner. @@ -124,7 +128,7 @@ func New(opts ...Option) Model { // NewModel returns a model with default values. // -// Deprecated. Use New instead. +// Deprecated: use [New] instead. var NewModel = New // TickMsg indicates that the timer has ticked and we should render a frame. @@ -201,15 +205,14 @@ func (m Model) tick(id, tag int) tea.Cmd { // Tick is the command used to advance the spinner one frame. Use this command // to effectively start the spinner. // -// This method is deprecated. Use Model.Tick instead. +// Deprecated: Use [Model.Tick] instead. func Tick() tea.Msg { return TickMsg{Time: time.Now()} } // Option is used to set options in New. For example: // -// spinner := New(WithSpinner(Dot)) -// +// spinner := New(WithSpinner(Dot)) type Option func(*Model) // WithSpinner is an option to set the spinner. diff --git a/vendor/github.com/charmbracelet/bubbles/textinput/textinput.go b/vendor/github.com/charmbracelet/bubbles/textinput/textinput.go index eb7d4148e4d85..f8990f66c9ab5 100644 --- a/vendor/github.com/charmbracelet/bubbles/textinput/textinput.go +++ b/vendor/github.com/charmbracelet/bubbles/textinput/textinput.go @@ -1,48 +1,19 @@ package textinput import ( - "context" "strings" - "sync" "time" "unicode" "github.com/atotto/clipboard" + "github.com/charmbracelet/bubbles/cursor" + "github.com/charmbracelet/bubbles/key" + "github.com/charmbracelet/bubbles/runeutil" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" rw "github.com/mattn/go-runewidth" ) -const defaultBlinkSpeed = time.Millisecond * 530 - -// Internal ID management for text inputs. Necessary for blink integrity when -// multiple text inputs are involved. -var ( - lastID int - idMtx sync.Mutex -) - -// Return the next ID we should use on the Model. -func nextID() int { - idMtx.Lock() - defer idMtx.Unlock() - lastID++ - return lastID -} - -// initialBlinkMsg initializes cursor blinking. -type initialBlinkMsg struct{} - -// blinkMsg signals that the cursor should blink. It contains metadata that -// allows us to tell if the blink message is the one we're expecting. -type blinkMsg struct { - id int - tag int -} - -// blinkCanceled is sent when a blink operation is canceled. -type blinkCanceled struct{} - // Internal messages for clipboard operations. type pasteMsg string type pasteErrMsg struct{ error } @@ -55,7 +26,7 @@ const ( EchoNormal EchoMode = iota // EchoPassword displays the EchoCharacter mask instead of actual - // characters. This is commonly used for password fields. + // characters. This is commonly used for password fields. EchoPassword // EchoNone displays nothing as characters are entered. This is commonly @@ -65,35 +36,44 @@ const ( // EchoOnEdit. ) -// blinkCtx manages cursor blinking. -type blinkCtx struct { - ctx context.Context - cancel context.CancelFunc -} - -// CursorMode describes the behavior of the cursor. -type CursorMode int - -// Available cursor modes. -const ( - CursorBlink CursorMode = iota - CursorStatic - CursorHide -) - -// String returns a the cursor mode in a human-readable format. This method is -// provisional and for informational purposes only. -func (c CursorMode) String() string { - return [...]string{ - "blink", - "static", - "hidden", - }[c] -} - // ValidateFunc is a function that returns an error if the input is invalid. type ValidateFunc func(string) error +// KeyMap is the key bindings for different actions within the textinput. +type KeyMap struct { + CharacterForward key.Binding + CharacterBackward key.Binding + WordForward key.Binding + WordBackward key.Binding + DeleteWordBackward key.Binding + DeleteWordForward key.Binding + DeleteAfterCursor key.Binding + DeleteBeforeCursor key.Binding + DeleteCharacterBackward key.Binding + DeleteCharacterForward key.Binding + LineStart key.Binding + LineEnd key.Binding + Paste key.Binding +} + +// DefaultKeyMap is the default set of key bindings for navigating and acting +// upon the textinput. +var DefaultKeyMap = KeyMap{ + CharacterForward: key.NewBinding(key.WithKeys("right", "ctrl+f")), + CharacterBackward: key.NewBinding(key.WithKeys("left", "ctrl+b")), + WordForward: key.NewBinding(key.WithKeys("alt+right", "alt+f")), + WordBackward: key.NewBinding(key.WithKeys("alt+left", "alt+b")), + DeleteWordBackward: key.NewBinding(key.WithKeys("alt+backspace", "ctrl+w")), + DeleteWordForward: key.NewBinding(key.WithKeys("alt+delete", "alt+d")), + DeleteAfterCursor: key.NewBinding(key.WithKeys("ctrl+k")), + DeleteBeforeCursor: key.NewBinding(key.WithKeys("ctrl+u")), + DeleteCharacterBackward: key.NewBinding(key.WithKeys("backspace", "ctrl+h")), + DeleteCharacterForward: key.NewBinding(key.WithKeys("delete", "ctrl+d")), + LineStart: key.NewBinding(key.WithKeys("home", "ctrl+a")), + LineEnd: key.NewBinding(key.WithKeys("end", "ctrl+e")), + Paste: key.NewBinding(key.WithKeys("ctrl+v")), +} + // Model is the Bubble Tea model for this text input element. type Model struct { Err error @@ -101,9 +81,12 @@ type Model struct { // General settings. Prompt string Placeholder string - BlinkSpeed time.Duration EchoMode EchoMode EchoCharacter rune + Cursor cursor.Model + + // Deprecated: use [cursor.BlinkSpeed] instead. + BlinkSpeed time.Duration // Styles. These will be applied as inline styles. // @@ -111,9 +94,10 @@ type Model struct { // https://github.com/charmbracelet/lipgloss PromptStyle lipgloss.Style TextStyle lipgloss.Style - BackgroundStyle lipgloss.Style PlaceholderStyle lipgloss.Style - CursorStyle lipgloss.Style + + // Deprecated: use Cursor.Style instead. + CursorStyle lipgloss.Style // CharLimit is the maximum amount of characters this input element will // accept. If 0 or less, there's no limit. @@ -124,11 +108,8 @@ type Model struct { // viewport. If 0 or less this setting is ignored. Width int - // The ID of this Model as it relates to other textinput Models. - id int - - // The ID of the blink message we're expecting to receive. - blinkTag int + // KeyMap encodes the keybindings recognized by the widget. + KeyMap KeyMap // Underlying text value. value []rune @@ -137,9 +118,6 @@ type Model struct { // component. When false, ignore keyboard input and hide the cursor. focus bool - // Cursor blink state. - blink bool - // Cursor position. pos int @@ -148,65 +126,63 @@ type Model struct { offset int offsetRight int - // Used to manage cursor blink - blinkCtx *blinkCtx - - // cursorMode determines the behavior of the cursor - cursorMode CursorMode - // Validate is a function that checks whether or not the text within the // input is valid. If it is not valid, the `Err` field will be set to the // error returned by the function. If the function is not defined, all // input is considered valid. Validate ValidateFunc + + // rune sanitizer for input. + rsan runeutil.Sanitizer } // New creates a new model with default settings. func New() Model { return Model{ Prompt: "> ", - BlinkSpeed: defaultBlinkSpeed, EchoCharacter: '*', CharLimit: 0, PlaceholderStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("240")), + Cursor: cursor.New(), + KeyMap: DefaultKeyMap, - id: nextID(), - value: nil, - focus: false, - blink: true, - pos: 0, - cursorMode: CursorBlink, - - blinkCtx: &blinkCtx{ - ctx: context.Background(), - }, + value: nil, + focus: false, + pos: 0, } } // NewModel creates a new model with default settings. // -// Deprecated. Use New instead. +// Deprecated: Use [New] instead. var NewModel = New // SetValue sets the value of the text input. func (m *Model) SetValue(s string) { + // Clean up any special characters in the input provided by the + // caller. This avoids bugs due to e.g. tab characters and whatnot. + runes := m.san().Sanitize([]rune(s)) + m.setValueInternal(runes) +} + +func (m *Model) setValueInternal(runes []rune) { if m.Validate != nil { - if err := m.Validate(s); err != nil { + if err := m.Validate(string(runes)); err != nil { m.Err = err return } } + empty := len(m.value) == 0 m.Err = nil - runes := []rune(s) if m.CharLimit > 0 && len(runes) > m.CharLimit { m.value = runes[:m.CharLimit] } else { m.value = runes } - if m.pos == 0 || m.pos > len(m.value) { - m.setCursor(len(m.value)) + if (m.pos == 0 && empty) || m.pos > len(m.value) { + m.SetCursor(len(m.value)) } m.handleOverflow() } @@ -216,74 +192,26 @@ func (m Model) Value() string { return string(m.value) } -// Cursor returns the cursor position. -func (m Model) Cursor() int { +// Position returns the cursor position. +func (m Model) Position() int { return m.pos } -// Blink returns whether or not to draw the cursor. -func (m Model) Blink() bool { - return m.blink -} - // SetCursor moves the cursor to the given position. If the position is // out of bounds the cursor will be moved to the start or end accordingly. func (m *Model) SetCursor(pos int) { - m.setCursor(pos) -} - -// setCursor moves the cursor to the given position and returns whether or not -// the cursor blink should be reset. If the position is out of bounds the -// cursor will be moved to the start or end accordingly. -func (m *Model) setCursor(pos int) bool { m.pos = clamp(pos, 0, len(m.value)) m.handleOverflow() - - // Show the cursor unless it's been explicitly hidden - m.blink = m.cursorMode == CursorHide - - // Reset cursor blink if necessary - return m.cursorMode == CursorBlink } // CursorStart moves the cursor to the start of the input field. func (m *Model) CursorStart() { - m.cursorStart() -} - -// cursorStart moves the cursor to the start of the input field and returns -// whether or not the curosr blink should be reset. -func (m *Model) cursorStart() bool { - return m.setCursor(0) + m.SetCursor(0) } // CursorEnd moves the cursor to the end of the input field. func (m *Model) CursorEnd() { - m.cursorEnd() -} - -// CursorMode returns the model's cursor mode. For available cursor modes, see -// type CursorMode. -func (m Model) CursorMode() CursorMode { - return m.cursorMode -} - -// SetCursorMode sets the model's cursor mode. This method returns a command. -// -// For available cursor modes, see type CursorMode. -func (m *Model) SetCursorMode(mode CursorMode) tea.Cmd { - m.cursorMode = mode - m.blink = m.cursorMode == CursorHide || !m.focus - if mode == CursorBlink { - return Blink - } - return nil -} - -// cursorEnd moves the cursor to the end of the input field and returns whether -// the cursor should blink should reset. -func (m *Model) cursorEnd() bool { - return m.setCursor(len(m.value)) + m.SetCursor(len(m.value)) } // Focused returns the focus state on the model. @@ -292,50 +220,56 @@ func (m Model) Focused() bool { } // Focus sets the focus state on the model. When the model is in focus it can -// receive keyboard input and the cursor will be hidden. +// receive keyboard input and the cursor will be shown. func (m *Model) Focus() tea.Cmd { m.focus = true - m.blink = m.cursorMode == CursorHide // show the cursor unless we've explicitly hidden it - - if m.cursorMode == CursorBlink && m.focus { - return m.blinkCmd() - } - return nil + return m.Cursor.Focus() } // Blur removes the focus state on the model. When the model is blurred it can // not receive keyboard input and the cursor will be hidden. func (m *Model) Blur() { m.focus = false - m.blink = true + m.Cursor.Blur() } -// Reset sets the input to its default state with no input. Returns whether -// or not the cursor blink should reset. -func (m *Model) Reset() bool { +// Reset sets the input to its default state with no input. +func (m *Model) Reset() { m.value = nil - return m.setCursor(0) + m.SetCursor(0) } -// handle a clipboard paste event, if supported. Returns whether or not the -// cursor blink should reset. -func (m *Model) handlePaste(v string) bool { - paste := []rune(v) +// rsan initializes or retrieves the rune sanitizer. +func (m *Model) san() runeutil.Sanitizer { + if m.rsan == nil { + // Textinput has all its input on a single line so collapse + // newlines/tabs to single spaces. + m.rsan = runeutil.NewSanitizer( + runeutil.ReplaceTabs(" "), runeutil.ReplaceNewlines(" ")) + } + return m.rsan +} + +func (m *Model) insertRunesFromUserInput(v []rune) { + // Clean up any special characters in the input provided by the + // clipboard. This avoids bugs due to e.g. tab characters and + // whatnot. + paste := m.san().Sanitize(v) var availSpace int if m.CharLimit > 0 { availSpace = m.CharLimit - len(m.value) - } - // If the char limit's been reached cancel - if m.CharLimit > 0 && availSpace <= 0 { - return false - } + // If the char limit's been reached, cancel. + if availSpace <= 0 { + return + } - // If there's not enough space to paste the whole thing cut the pasted - // runes down so they'll fit - if m.CharLimit > 0 && availSpace < len(paste) { - paste = paste[:len(paste)-availSpace] + // If there's not enough space to paste the whole thing cut the pasted + // runes down so they'll fit. + if availSpace < len(paste) { + paste = paste[:len(paste)-availSpace] + } } // Stuff before and after the cursor @@ -360,14 +294,11 @@ func (m *Model) handlePaste(v string) bool { // Put it all back together value := append(head, tail...) - m.SetValue(string(value)) + m.setValueInternal(value) if m.Err != nil { m.pos = oldPos } - - // Reset blink state if necessary and run overflow checks - return m.setCursor(m.pos) } // If a max width is defined, perform some logic to treat the visible area @@ -415,31 +346,30 @@ func (m *Model) handleOverflow() { } } -// deleteBeforeCursor deletes all text before the cursor. Returns whether or -// not the cursor blink should be reset. -func (m *Model) deleteBeforeCursor() bool { +// deleteBeforeCursor deletes all text before the cursor. +func (m *Model) deleteBeforeCursor() { m.value = m.value[m.pos:] m.offset = 0 - return m.setCursor(0) + m.SetCursor(0) } -// deleteAfterCursor deletes all text after the cursor. Returns whether or not -// the cursor blink should be reset. If input is masked delete everything after -// the cursor so as not to reveal word breaks in the masked input. -func (m *Model) deleteAfterCursor() bool { +// deleteAfterCursor deletes all text after the cursor. If input is masked +// delete everything after the cursor so as not to reveal word breaks in the +// masked input. +func (m *Model) deleteAfterCursor() { m.value = m.value[:m.pos] - return m.setCursor(len(m.value)) + m.SetCursor(len(m.value)) } -// deleteWordLeft deletes the word left to the cursor. Returns whether or not -// the cursor blink should be reset. -func (m *Model) deleteWordLeft() bool { +// deleteWordBackward deletes the word left to the cursor. +func (m *Model) deleteWordBackward() { if m.pos == 0 || len(m.value) == 0 { - return false + return } if m.EchoMode != EchoNormal { - return m.deleteBeforeCursor() + m.deleteBeforeCursor() + return } // Linter note: it's critical that we acquire the initial cursor position @@ -447,22 +377,22 @@ func (m *Model) deleteWordLeft() bool { // call into the corresponding if clause does not apply here. oldPos := m.pos //nolint:ifshort - blink := m.setCursor(m.pos - 1) + m.SetCursor(m.pos - 1) for unicode.IsSpace(m.value[m.pos]) { if m.pos <= 0 { break } // ignore series of whitespace before cursor - blink = m.setCursor(m.pos - 1) + m.SetCursor(m.pos - 1) } for m.pos > 0 { if !unicode.IsSpace(m.value[m.pos]) { - blink = m.setCursor(m.pos - 1) + m.SetCursor(m.pos - 1) } else { if m.pos > 0 { // keep the previous space - blink = m.setCursor(m.pos + 1) + m.SetCursor(m.pos + 1) } break } @@ -473,27 +403,26 @@ func (m *Model) deleteWordLeft() bool { } else { m.value = append(m.value[:m.pos], m.value[oldPos:]...) } - - return blink } -// deleteWordRight deletes the word right to the cursor. Returns whether or not -// the cursor blink should be reset. If input is masked delete everything after -// the cursor so as not to reveal word breaks in the masked input. -func (m *Model) deleteWordRight() bool { +// deleteWordForward deletes the word right to the cursor. If input is masked +// delete everything after the cursor so as not to reveal word breaks in the +// masked input. +func (m *Model) deleteWordForward() { if m.pos >= len(m.value) || len(m.value) == 0 { - return false + return } if m.EchoMode != EchoNormal { - return m.deleteAfterCursor() + m.deleteAfterCursor() + return } oldPos := m.pos - m.setCursor(m.pos + 1) + m.SetCursor(m.pos + 1) for unicode.IsSpace(m.value[m.pos]) { // ignore series of whitespace after cursor - m.setCursor(m.pos + 1) + m.SetCursor(m.pos + 1) if m.pos >= len(m.value) { break @@ -502,7 +431,7 @@ func (m *Model) deleteWordRight() bool { for m.pos < len(m.value) { if !unicode.IsSpace(m.value[m.pos]) { - m.setCursor(m.pos + 1) + m.SetCursor(m.pos + 1) } else { break } @@ -514,26 +443,25 @@ func (m *Model) deleteWordRight() bool { m.value = append(m.value[:oldPos], m.value[m.pos:]...) } - return m.setCursor(oldPos) + m.SetCursor(oldPos) } -// wordLeft moves the cursor one word to the left. Returns whether or not the -// cursor blink should be reset. If input is masked, move input to the start -// so as not to reveal word breaks in the masked input. -func (m *Model) wordLeft() bool { +// wordBackward moves the cursor one word to the left. If input is masked, move +// input to the start so as not to reveal word breaks in the masked input. +func (m *Model) wordBackward() { if m.pos == 0 || len(m.value) == 0 { - return false + return } if m.EchoMode != EchoNormal { - return m.cursorStart() + m.CursorStart() + return } - blink := false i := m.pos - 1 for i >= 0 { if unicode.IsSpace(m.value[i]) { - blink = m.setCursor(m.pos - 1) + m.SetCursor(m.pos - 1) i-- } else { break @@ -542,33 +470,30 @@ func (m *Model) wordLeft() bool { for i >= 0 { if !unicode.IsSpace(m.value[i]) { - blink = m.setCursor(m.pos - 1) + m.SetCursor(m.pos - 1) i-- } else { break } } - - return blink } -// wordRight moves the cursor one word to the right. Returns whether or not the -// cursor blink should be reset. If the input is masked, move input to the end -// so as not to reveal word breaks in the masked input. -func (m *Model) wordRight() bool { +// wordForward moves the cursor one word to the right. If the input is masked, +// move input to the end so as not to reveal word breaks in the masked input. +func (m *Model) wordForward() { if m.pos >= len(m.value) || len(m.value) == 0 { - return false + return } if m.EchoMode != EchoNormal { - return m.cursorEnd() + m.CursorEnd() + return } - blink := false i := m.pos for i < len(m.value) { if unicode.IsSpace(m.value[i]) { - blink = m.setCursor(m.pos + 1) + m.SetCursor(m.pos + 1) i++ } else { break @@ -577,14 +502,12 @@ func (m *Model) wordRight() bool { for i < len(m.value) { if !unicode.IsSpace(m.value[i]) { - blink = m.setCursor(m.pos + 1) + m.SetCursor(m.pos + 1) i++ } else { break } } - - return blink } func (m Model) echoTransform(v string) string { @@ -593,7 +516,8 @@ func (m Model) echoTransform(v string) string { return strings.Repeat(string(m.EchoCharacter), rw.StringWidth(v)) case EchoNone: return "" - + case EchoNormal: + return v default: return v } @@ -602,138 +526,82 @@ func (m Model) echoTransform(v string) string { // Update is the Bubble Tea update loop. func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { if !m.focus { - m.blink = true return m, nil } - var resetBlink bool + // Let's remember where the position of the cursor currently is so that if + // the cursor position changes, we can reset the blink. + oldPos := m.pos //nolint switch msg := msg.(type) { case tea.KeyMsg: - switch msg.Type { - case tea.KeyBackspace: // delete character before cursor + switch { + case key.Matches(msg, m.KeyMap.DeleteWordBackward): m.Err = nil - - if msg.Alt { - resetBlink = m.deleteWordLeft() - } else { - if len(m.value) > 0 { - m.value = append(m.value[:max(0, m.pos-1)], m.value[m.pos:]...) - if m.pos > 0 { - resetBlink = m.setCursor(m.pos - 1) - } + m.deleteWordBackward() + case key.Matches(msg, m.KeyMap.DeleteCharacterBackward): + m.Err = nil + if len(m.value) > 0 { + m.value = append(m.value[:max(0, m.pos-1)], m.value[m.pos:]...) + if m.pos > 0 { + m.SetCursor(m.pos - 1) } } - case tea.KeyLeft, tea.KeyCtrlB: - if msg.Alt { // alt+left arrow, back one word - resetBlink = m.wordLeft() - break - } - if m.pos > 0 { // left arrow, ^F, back one character - resetBlink = m.setCursor(m.pos - 1) - } - case tea.KeyRight, tea.KeyCtrlF: - if msg.Alt { // alt+right arrow, forward one word - resetBlink = m.wordRight() - break + case key.Matches(msg, m.KeyMap.WordBackward): + m.wordBackward() + case key.Matches(msg, m.KeyMap.CharacterBackward): + if m.pos > 0 { + m.SetCursor(m.pos - 1) } - if m.pos < len(m.value) { // right arrow, ^F, forward one character - resetBlink = m.setCursor(m.pos + 1) + case key.Matches(msg, m.KeyMap.WordForward): + m.wordForward() + case key.Matches(msg, m.KeyMap.CharacterForward): + if m.pos < len(m.value) { + m.SetCursor(m.pos + 1) } - case tea.KeyCtrlW: // ^W, delete word left of cursor - resetBlink = m.deleteWordLeft() - case tea.KeyHome, tea.KeyCtrlA: // ^A, go to beginning - resetBlink = m.cursorStart() - case tea.KeyDelete, tea.KeyCtrlD: // ^D, delete char under cursor + case key.Matches(msg, m.KeyMap.DeleteWordBackward): + m.deleteWordBackward() + case key.Matches(msg, m.KeyMap.LineStart): + m.CursorStart() + case key.Matches(msg, m.KeyMap.DeleteCharacterForward): if len(m.value) > 0 && m.pos < len(m.value) { m.value = append(m.value[:m.pos], m.value[m.pos+1:]...) } - case tea.KeyCtrlE, tea.KeyEnd: // ^E, go to end - resetBlink = m.cursorEnd() - case tea.KeyCtrlK: // ^K, kill text after cursor - resetBlink = m.deleteAfterCursor() - case tea.KeyCtrlU: // ^U, kill text before cursor - resetBlink = m.deleteBeforeCursor() - case tea.KeyCtrlV: // ^V paste + case key.Matches(msg, m.KeyMap.LineEnd): + m.CursorEnd() + case key.Matches(msg, m.KeyMap.DeleteAfterCursor): + m.deleteAfterCursor() + case key.Matches(msg, m.KeyMap.DeleteBeforeCursor): + m.deleteBeforeCursor() + case key.Matches(msg, m.KeyMap.Paste): return m, Paste - case tea.KeyRunes, tea.KeySpace: // input regular characters - if msg.Alt && len(msg.Runes) == 1 { - if msg.Runes[0] == 'd' { // alt+d, delete word right of cursor - resetBlink = m.deleteWordRight() - break - } - if msg.Runes[0] == 'b' { // alt+b, back one word - resetBlink = m.wordLeft() - break - } - if msg.Runes[0] == 'f' { // alt+f, forward one word - resetBlink = m.wordRight() - break - } - } - - // Input a regular character - if m.CharLimit <= 0 || len(m.value) < m.CharLimit { - runes := msg.Runes - - value := make([]rune, len(m.value)) - copy(value, m.value) - value = append(value[:m.pos], append(runes, value[m.pos:]...)...) - m.SetValue(string(value)) - if m.Err == nil { - resetBlink = m.setCursor(m.pos + len(runes)) - } - } - } - - case initialBlinkMsg: - // We accept all initialBlinkMsgs genrated by the Blink command. - - if m.cursorMode != CursorBlink || !m.focus { - return m, nil - } - - cmd := m.blinkCmd() - return m, cmd - - case blinkMsg: - // We're choosy about whether to accept blinkMsgs so that our cursor - // only exactly when it should. - - // Is this model blinkable? - if m.cursorMode != CursorBlink || !m.focus { - return m, nil - } - - // Were we expecting this blink message? - if msg.id != m.id || msg.tag != m.blinkTag { - return m, nil - } - - var cmd tea.Cmd - if m.cursorMode == CursorBlink { - m.blink = !m.blink - cmd = m.blinkCmd() + case key.Matches(msg, m.KeyMap.DeleteWordForward): + m.deleteWordForward() + default: + // Input one or more regular characters. + m.insertRunesFromUserInput(msg.Runes) } - return m, cmd - - case blinkCanceled: // no-op - return m, nil case pasteMsg: - resetBlink = m.handlePaste(string(msg)) + m.insertRunesFromUserInput([]rune(msg)) case pasteErrMsg: m.Err = msg } + var cmds []tea.Cmd var cmd tea.Cmd - if resetBlink { - cmd = m.blinkCmd() + + m.Cursor, cmd = m.Cursor.Update(msg) + cmds = append(cmds, cmd) + + if oldPos != m.pos && m.Cursor.Mode() == cursor.CursorBlink { + m.Cursor.Blink = false + cmds = append(cmds, m.Cursor.BlinkCmd()) } m.handleOverflow() - return m, cmd + return m, tea.Batch(cmds...) } // View renders the textinput in its current state. @@ -750,10 +618,13 @@ func (m Model) View() string { v := styleText(m.echoTransform(string(value[:pos]))) if pos < len(value) { - v += m.cursorView(m.echoTransform(string(value[pos]))) // cursor and text under it + char := m.echoTransform(string(value[pos])) + m.Cursor.SetChar(char) + v += m.Cursor.View() // cursor and text under it v += styleText(m.echoTransform(string(value[pos+1:]))) // text after cursor } else { - v += m.cursorView(" ") + m.Cursor.SetChar(" ") + v += m.Cursor.View() } // If a max width and background color were set fill the empty spaces with @@ -778,12 +649,9 @@ func (m Model) placeholderView() string { style = m.PlaceholderStyle.Inline(true).Render ) - // Cursor - if m.blink { - v += m.cursorView(style(p[:1])) - } else { - v += m.cursorView(p[:1]) - } + m.Cursor.TextStyle = m.PlaceholderStyle + m.Cursor.SetChar(p[:1]) + v += m.Cursor.View() // The rest of the placeholder text v += style(p[1:]) @@ -791,42 +659,9 @@ func (m Model) placeholderView() string { return m.PromptStyle.Render(m.Prompt) + v } -// cursorView styles the cursor. -func (m Model) cursorView(v string) string { - if m.blink { - return m.TextStyle.Render(v) - } - return m.CursorStyle.Inline(true).Reverse(true).Render(v) -} - -// blinkCmd is an internal command used to manage cursor blinking. -func (m *Model) blinkCmd() tea.Cmd { - if m.cursorMode != CursorBlink { - return nil - } - - if m.blinkCtx != nil && m.blinkCtx.cancel != nil { - m.blinkCtx.cancel() - } - - ctx, cancel := context.WithTimeout(m.blinkCtx.ctx, m.BlinkSpeed) - m.blinkCtx.cancel = cancel - - m.blinkTag++ - - return func() tea.Msg { - defer cancel() - <-ctx.Done() - if ctx.Err() == context.DeadlineExceeded { - return blinkMsg{id: m.id, tag: m.blinkTag} - } - return blinkCanceled{} - } -} - // Blink is a command used to initialize cursor blinking. func Blink() tea.Msg { - return initialBlinkMsg{} + return cursor.Blink() } // Paste is a command for pasting from the clipboard into the text input. @@ -858,3 +693,31 @@ func max(a, b int) int { } return b } + +// Deprecated. + +// Deprecated: use cursor.Mode. +type CursorMode int + +const ( + // Deprecated: use cursor.CursorBlink. + CursorBlink = CursorMode(cursor.CursorBlink) + // Deprecated: use cursor.CursorStatic. + CursorStatic = CursorMode(cursor.CursorStatic) + // Deprecated: use cursor.CursorHide. + CursorHide = CursorMode(cursor.CursorHide) +) + +func (c CursorMode) String() string { + return cursor.Mode(c).String() +} + +// Deprecated: use cursor.Mode(). +func (m Model) CursorMode() CursorMode { + return CursorMode(m.Cursor.Mode()) +} + +// Deprecated: use cursor.SetMode(). +func (m *Model) SetCursorMode(mode CursorMode) tea.Cmd { + return m.Cursor.SetMode(cursor.Mode(mode)) +} diff --git a/vendor/github.com/charmbracelet/bubbles/viewport/viewport.go b/vendor/github.com/charmbracelet/bubbles/viewport/viewport.go index fd77300024ab2..b13e33c0fdec0 100644 --- a/vendor/github.com/charmbracelet/bubbles/viewport/viewport.go +++ b/vendor/github.com/charmbracelet/bubbles/viewport/viewport.go @@ -10,7 +10,7 @@ import ( ) // New returns a new model with the given width and height as well as default -// keymappings. +// key mappings. func New(width, height int) (m Model) { m.Width = width m.Height = height @@ -68,7 +68,7 @@ func (m Model) Init() tea.Cmd { return nil } -// AtTop returns whether or not the viewport is in the very top position. +// AtTop returns whether or not the viewport is at the very top position. func (m Model) AtTop() bool { return m.YOffset <= 0 } @@ -147,8 +147,7 @@ func (m *Model) ViewDown() []string { return nil } - m.SetYOffset(m.YOffset + m.Height) - return m.visibleLines() + return m.LineDown(m.Height) } // ViewUp moves the view up by one height of the viewport. Basically, "page up". @@ -157,8 +156,7 @@ func (m *Model) ViewUp() []string { return nil } - m.SetYOffset(m.YOffset - m.Height) - return m.visibleLines() + return m.LineUp(m.Height) } // HalfViewDown moves the view down by half the height of the viewport. @@ -167,8 +165,7 @@ func (m *Model) HalfViewDown() (lines []string) { return nil } - m.SetYOffset(m.YOffset + m.Height/2) - return m.visibleLines() + return m.LineDown(m.Height / 2) } // HalfViewUp moves the view up by half the height of the viewport. @@ -177,13 +174,12 @@ func (m *Model) HalfViewUp() (lines []string) { return nil } - m.SetYOffset(m.YOffset - m.Height/2) - return m.visibleLines() + return m.LineUp(m.Height / 2) } // LineDown moves the view down by the given number of lines. func (m *Model) LineDown(n int) (lines []string) { - if m.AtBottom() || n == 0 { + if m.AtBottom() || n == 0 || len(m.lines) == 0 { return nil } @@ -191,20 +187,38 @@ func (m *Model) LineDown(n int) (lines []string) { // greater than the number of lines we actually have left before we reach // the bottom. m.SetYOffset(m.YOffset + n) - return m.visibleLines() + + // Gather lines to send off for performance scrolling. + bottom := clamp(m.YOffset+m.Height, 0, len(m.lines)) + top := clamp(m.YOffset+m.Height-n, 0, bottom) + return m.lines[top:bottom] } // LineUp moves the view down by the given number of lines. Returns the new // lines to show. func (m *Model) LineUp(n int) (lines []string) { - if m.AtTop() || n == 0 { + if m.AtTop() || n == 0 || len(m.lines) == 0 { return nil } // Make sure the number of lines by which we're going to scroll isn't // greater than the number of lines we are from the top. m.SetYOffset(m.YOffset - n) - return m.visibleLines() + + // Gather lines to send off for performance scrolling. + top := max(0, m.YOffset) + bottom := clamp(m.YOffset+n, 0, m.maxYOffset()) + return m.lines[top:bottom] +} + +// TotalLineCount returns the total number of lines (both hidden and visible) within the viewport. +func (m Model) TotalLineCount() int { + return len(m.lines) +} + +// VisibleLineCount returns the number of the visible lines within the viewport. +func (m Model) VisibleLineCount() int { + return len(m.visibleLines()) } // GotoTop sets the viewport to the top position. @@ -237,12 +251,11 @@ func Sync(m Model) tea.Cmd { } // ViewDown is a high performance command that moves the viewport up by a given -// numer of lines. Use Model.ViewDown to get the lines that should be rendered. +// number of lines. Use Model.ViewDown to get the lines that should be rendered. // For example: // -// lines := model.ViewDown(1) -// cmd := ViewDown(m, lines) -// +// lines := model.ViewDown(1) +// cmd := ViewDown(m, lines) func ViewDown(m Model, lines []string) tea.Cmd { if len(lines) == 0 { return nil @@ -344,24 +357,29 @@ func (m Model) updateAsModel(msg tea.Msg) (Model, tea.Cmd) { func (m Model) View() string { if m.HighPerformanceRendering { // Just send newlines since we're going to be rendering the actual - // content seprately. We still need to send something that equals the + // content separately. We still need to send something that equals the // height of this view so that the Bubble Tea standard renderer can // position anything below this view properly. - return strings.Repeat("\n", m.Height-1) + return strings.Repeat("\n", max(0, m.Height-1)) } - lines := m.visibleLines() - - // Fill empty space with newlines - extraLines := "" - if len(lines) < m.Height { - extraLines = strings.Repeat("\n", max(0, m.Height-len(lines))) + w, h := m.Width, m.Height + if sw := m.Style.GetWidth(); sw != 0 { + w = min(w, sw) } - + if sh := m.Style.GetHeight(); sh != 0 { + h = min(h, sh) + } + contentWidth := w - m.Style.GetHorizontalFrameSize() + contentHeight := h - m.Style.GetVerticalFrameSize() + contents := lipgloss.NewStyle(). + Height(contentHeight). // pad to height. + MaxHeight(contentHeight). // truncate height if taller. + MaxWidth(contentWidth). // truncate width. + Render(strings.Join(m.visibleLines(), "\n")) return m.Style.Copy(). - UnsetWidth(). - UnsetHeight(). - Render(strings.Join(lines, "\n") + extraLines) + UnsetWidth().UnsetHeight(). // Style size already applied in contents. + Render(contents) } func clamp(v, low, high int) int { diff --git a/vendor/github.com/charmbracelet/bubbletea/.gitignore b/vendor/github.com/charmbracelet/bubbletea/.gitignore index 0eac410963cd7..9cc52352ebd97 100644 --- a/vendor/github.com/charmbracelet/bubbletea/.gitignore +++ b/vendor/github.com/charmbracelet/bubbletea/.gitignore @@ -9,6 +9,8 @@ examples/list-fancy/list-fancy examples/list-simple/list-simple examples/mouse/mouse examples/pager/pager +examples/progress-download/color_vortex.blend +examples/progress-download/progress-download examples/simple/simple examples/spinner/spinner examples/textinput/textinput diff --git a/vendor/github.com/charmbracelet/bubbletea/CONTRIBUTING.md b/vendor/github.com/charmbracelet/bubbletea/CONTRIBUTING.md new file mode 100644 index 0000000000000..19ee18c766281 --- /dev/null +++ b/vendor/github.com/charmbracelet/bubbletea/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing + +Pull requests are welcome for any changes. + +Consider opening an issue for larger changes to get feedback on the idea from the team. + +If your change touches parts of the Bubble Tea renderer or internals, make sure +that all the examples in the `examples/` folder continue to run correctly. + +For commit messages, please use conventional commits[^1] to make it easier to +generate release notes. + +[^1]: https://www.conventionalcommits.org/en/v1.0.0 diff --git a/vendor/github.com/charmbracelet/bubbletea/LICENSE b/vendor/github.com/charmbracelet/bubbletea/LICENSE index e5892cfe9db85..31d76c1c6ea62 100644 --- a/vendor/github.com/charmbracelet/bubbletea/LICENSE +++ b/vendor/github.com/charmbracelet/bubbletea/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 Charmbracelet, Inc +Copyright (c) 2020-2023 Charmbracelet, Inc Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/charmbracelet/bubbletea/README.md b/vendor/github.com/charmbracelet/bubbletea/README.md index 9cc4e61773c4d..0a88caa5fa868 100644 --- a/vendor/github.com/charmbracelet/bubbletea/README.md +++ b/vendor/github.com/charmbracelet/bubbletea/README.md @@ -1,5 +1,4 @@ -Bubble Tea -========== +# Bubble Tea

Bubble Tea Title Treatment
@@ -13,7 +12,7 @@ based on [The Elm Architecture][elm]. Bubble Tea is well-suited for simple and complex terminal applications, either inline, full-window, or a mix of both.

- Bubble Tea Example + Bubble Tea Example

Bubble Tea is in use in production and includes a number of features and @@ -22,7 +21,9 @@ framerate-based renderer, a renderer for high-performance scrollable regions which works alongside the main renderer, and mouse support. To get started, see the tutorial below, the [examples][examples], the -[docs][docs] and some common [resources](#libraries-we-use-with-bubble-tea). +[docs][docs], the [video tutorials][youtube] and some common [resources](#libraries-we-use-with-bubble-tea). + +[youtube]: https://charm.sh/yt ## By the way @@ -33,24 +34,25 @@ Be sure to check out [Bubbles][bubbles], a library of common UI components for B Text Input Example from Bubbles

-* * * +*** ## Tutorial Bubble Tea is based on the functional design paradigms of [The Elm -Architecture][elm], which happen to work nicely with Go. It's a delightful way to -build applications. - -By the way, the non-annotated source code for this program is available -[on GitHub](https://github.com/charmbracelet/bubbletea/tree/master/tutorials/basics). +Architecture][elm], which happens to work nicely with Go. It's a delightful way +to build applications. This tutorial assumes you have a working knowledge of Go. +By the way, the non-annotated source code for this program is available +[on GitHub][tut-source]. + [elm]: https://guide.elm-lang.org/architecture/ +[tut-source]:https://github.com/charmbracelet/bubbletea/tree/master/tutorials/basics -## Enough! Let's get to it. +### Enough! Let's get to it. -For this tutorial we're making a shopping list. +For this tutorial, we're making a shopping list. To start we'll define our package and import some libraries. Our only external import will be the Bubble Tea library, which we'll call `tea` for short. @@ -73,7 +75,7 @@ state and three simple methods on that model: * **Update**, a function that handles incoming events and updates the model accordingly. * **View**, a function that renders the UI based on the data in the model. -## The Model +### The Model So let's start by defining our model which will store our application's state. It can be any type, but a `struct` usually makes the most sense. @@ -86,16 +88,16 @@ type model struct { } ``` -## Initialization +### Initialization -Next we’ll define our application’s initial state. In this case we’re defining -a function to return our initial model, however we could just as easily define +Next, we’ll define our application’s initial state. In this case, we’re defining +a function to return our initial model, however, we could just as easily define the initial model as a variable elsewhere, too. ```go func initialModel() model { return model{ - // Our shopping list is a grocery list + // Our to-do list is a grocery list choices: []string{"Buy carrots", "Buy celery", "Buy kohlrabi"}, // A map which indicates which choices are selected. We're using @@ -106,8 +108,8 @@ func initialModel() model { } ``` -Next we define the `Init` method. `Init` can return a `Cmd` that could perform -some initial I/O. For now, we don't need to do any I/O, so for the command +Next, we define the `Init` method. `Init` can return a `Cmd` that could perform +some initial I/O. For now, we don't need to do any I/O, so for the command, we'll just return `nil`, which translates to "no command." ```go @@ -117,7 +119,7 @@ func (m model) Init() tea.Cmd { } ``` -## The Update Method +### The Update Method Next up is the update method. The update function is called when ”things happen.” Its job is to look at what has happened and return an updated model in @@ -185,11 +187,11 @@ You may have noticed that ctrl+c and q above return a `tea.Quit` command with the model. That’s a special command which instructs the Bubble Tea runtime to quit, exiting the program. -## The View Method +### The View Method At last, it’s time to render our UI. Of all the methods, the view is the -simplest. We look at the model in it's current state and use it to return -a `string`. That string is our UI! +simplest. We look at the model in its current state and use it to return +a `string`. That string is our UI! Because the view describes the entire UI of your application, you don’t have to worry about redrawing logic and stuff like that. Bubble Tea takes care of it @@ -227,7 +229,7 @@ func (m model) View() string { } ``` -## All Together Now +### All Together Now The last step is to simply run our program. We pass our initial model to `tea.NewProgram` and let it rip: @@ -235,7 +237,7 @@ The last step is to simply run our program. We pass our initial model to ```go func main() { p := tea.NewProgram(initialModel()) - if err := p.Start(); err != nil { + if _, err := p.Run(); err != nil { fmt.Printf("Alas, there's been an error: %v", err) os.Exit(1) } @@ -255,7 +257,9 @@ there are [Go Docs][docs]. [examples]: http://github.com/charmbracelet/bubbletea/tree/master/examples [docs]: https://pkg.go.dev/github.com/charmbracelet/bubbletea?tab=doc -## Debugging with Delve +## Debugging + +### Debugging with Delve Since Bubble Tea apps assume control of stdin and stdout, you’ll need to run delve in headless mode and then connect to it: @@ -272,17 +276,39 @@ $ dlv connect 127.0.0.1:34241 Note that the default port used will vary on your system and per run, so actually watch out what address the first `dlv` run tells you to connect to. +### Logging Stuff + +You can’t really log to stdout with Bubble Tea because your TUI is busy +occupying that! You can, however, log to a file by including something like +the following prior to starting your Bubble Tea program: + +```go +if len(os.Getenv("DEBUG")) > 0 { + f, err := tea.LogToFile("debug.log", "debug") + if err != nil { + fmt.Println("fatal:", err) + os.Exit(1) + } + defer f.Close() +} +``` + +To see what’s being logged in real time, run `tail -f debug.log` while you run +your program in another window. + ## Libraries we use with Bubble Tea * [Bubbles][bubbles]: Common Bubble Tea components such as text inputs, viewports, spinners and so on * [Lip Gloss][lipgloss]: Style, format and layout tools for terminal applications * [Harmonica][harmonica]: A spring animation library for smooth, natural motion +* [BubbleZone][bubblezone]: Easy mouse event tracking for Bubble Tea components * [Termenv][termenv]: Advanced ANSI styling for terminal applications * [Reflow][reflow]: Advanced ANSI-aware methods for working with text [bubbles]: https://github.com/charmbracelet/bubbles [lipgloss]: https://github.com/charmbracelet/lipgloss [harmonica]: https://github.com/charmbracelet/harmonica +[bubblezone]: https://github.com/lrstanley/bubblezone [termenv]: https://github.com/muesli/termenv [reflow]: https://github.com/muesli/reflow @@ -290,54 +316,81 @@ actually watch out what address the first `dlv` run tells you to connect to. For some Bubble Tea programs in production, see: -* [AT CLI](https://github.com/daskycodes/at_cli): a utility for executing AT Commands via serial port connections +* [AT CLI](https://github.com/daskycodes/at_cli): execute AT Commands via serial port connections +* [Aztify](https://github.com/Azure/aztfy): bring Microsoft Azure resources under Terraform +* [brows](https://github.com/rubysolo/brows): a GitHub release browser * [Canard](https://github.com/mrusme/canard): an RSS client -* [The Charm Tool](https://github.com/charmbracelet/charm): the Charm user account manager -* [clidle](https://github.com/ajeetdsouza/clidle): a Wordle clone for your terminal +* [charm](https://github.com/charmbracelet/charm): the official Charm user account manager +* [chezmoi](https://github.com/twpayne/chezmoi): securely manage your dotfiles across multiple machines +* [chtop](https://github.com/chhetripradeep/chtop): monitor your ClickHouse node without leaving terminal +* [circumflex](https://github.com/bensadeh/circumflex): read Hacker News in the terminal +* [clidle](https://github.com/ajeetdsouza/clidle): a Wordle clone +* [cLive](https://github.com/koki-develop/clive): automate terminal operations and view them live in a browser +* [container-canary](https://github.com/NVIDIA/container-canary): a container validator +* [countdown](https://github.com/aldernero/countdown): a multi-event countdown timer +* [dns53](https://github.com/purpleclay/dns53): dynamic DNS with Amazon Route53. Expose your EC2 quickly, securely and privately +* [eks-node-viewer](https://github.com/awslabs/eks-node-viewer): a tool for visualizing dynamic node usage within an eks cluster +* [enola](https://github.com/sherlock-project/enola): hunt down social media accounts by username across social networks +* [flapioca](https://github.com/kbrgl/flapioca): Flappy Bird on the CLI! * [fm](https://github.com/knipferrc/fm): a terminal-based file manager -* [fork-cleaner](https://github.com/caarlos0/fork-cleaner): cleans up old and inactive forks in your GitHub account -* [gambit](https://github.com/maaslalani/gambit): play chess in the terminal +* [fork-cleaner](https://github.com/caarlos0/fork-cleaner): clean up old and inactive forks in your GitHub account +* [fztea](https://github.com/jon4hz/fztea): a Flipper Zero TUI +* [gambit](https://github.com/maaslalani/gambit): chess in the terminal * [gembro](https://git.sr.ht/~rafael/gembro): a mouse-driven Gemini browser -* [gh-prs](https://www.github.com/dlvhdr/gh-prs): gh cli extension to display a dashboard of PRs +* [gh-b](https://github.com/joaom00/gh-b): a GitHub CLI extension for managing branches +* [gh-dash](https://www.github.com/dlvhdr/gh-dash): a GitHub CLI extension for PRs and issues * [gitflow-toolkit](https://github.com/mritd/gitflow-toolkit): a GitFlow submission tool -* [Glow](https://github.com/charmbracelet/glow): a markdown reader, browser and online markdown stash +* [Glow](https://github.com/charmbracelet/glow): a markdown reader, browser, and online markdown stash * [gocovsh](https://github.com/orlangure/gocovsh): explore Go coverage reports from the CLI +* [got](https://github.com/fedeztk/got): a simple translator and text-to-speech app build on top of simplytranslate's APIs * [httpit](https://github.com/gonetx/httpit): a rapid http(s) benchmark tool -* [IDNT](https://github.com/r-darwish/idnt): batch software uninstaller +* [IDNT](https://github.com/r-darwish/idnt): a batch software uninstaller * [kboard](https://github.com/CamiloGarciaLaRotta/kboard): a typing game -* [mergestat](https://github.com/mergestat/mergestat): run SQL queries on git repositories +* [mandelbrot-cli](https://github.com/MicheleFiladelfia/mandelbrot-cli): a multiplatform terminal mandelbrot set explorer * [mc](https://github.com/minio/mc): the official [MinIO](https://min.io) client -* [portal][portal]: securely send transfer between computers -* [redis-viewer](https://github.com/SaltFishPr/redis-viewer): browse Redis databases +* [mergestat](https://github.com/mergestat/mergestat): run SQL queries on git repositories +* [Neon Modem Overdrive](https://github.com/mrusme/neonmodem): a BBS-style TUI client for Discourse, Lemmy, Lobste.rs and Hacker News +* [Noted](https://github.com/torbratsberg/noted): a note viewer and manager +* [pathos](https://github.com/chip/pathos): a PATH env variable editor +* [portal](https://github.com/ZinoKader/portal): secure transfers between computers +* [redis-viewer](https://github.com/SaltFishPr/redis-viewer): a Redis databases browser +* [sku](https://github.com/fedeztk/sku): Sudoku on the CLI * [Slides](https://github.com/maaslalani/slides): a markdown-based presentation tool +* [SlurmCommander](https://github.com/CLIP-HPC/SlurmCommander): a Slurm workload manager TUI * [Soft Serve](https://github.com/charmbracelet/soft-serve): a command-line-first Git server that runs a TUI over SSH +* [solitaire-tui](https://github.com/brianstrauch/solitaire-tui): Klondike Solitaire for the terminal * [StormForge Optimize Controller](https://github.com/thestormforge/optimize-controller): a tool for experimenting with application configurations in Kubernetes -* [STTG](https://github.com/wille1101/sttg): teletext client for SVT, Sweden’s national public television station -* [sttr](https://github.com/abhimanyu003/sttr): run various text transformations +* [STTG](https://github.com/wille1101/sttg): a teletext client for SVT, Sweden’s national public television station +* [sttr](https://github.com/abhimanyu003/sttr): a general-purpose text transformer * [tasktimer](https://github.com/caarlos0/tasktimer): a dead-simple task timer * [termdbms](https://github.com/mathaou/termdbms): a keyboard and mouse driven database browser -* [ticker](https://github.com/achannarasappa/ticker): a terminal stock watcher and stock position tracker -* [tran](https://github.com/abdfnx/tran): securely transfer stuff between computers (based on [portal][portal]) -* [tz](https://github.com/oz/tz): an aid for scheduling across multiple time zones +* [ticker](https://github.com/achannarasappa/ticker): a terminal stock viewer and stock position tracker +* [tran](https://github.com/abdfnx/tran): securely transfer stuff between computers (based on [portal](https://github.com/ZinoKader/portal)) * [Typer](https://github.com/maaslalani/typer): a typing test +* [tz](https://github.com/oz/tz): an aid for scheduling across multiple time zones +* [ugm](https://github.com/ariasmn/ugm): a unix user and group browser +* [wander](https://github.com/robinovitch61/wander): a HashiCorp Nomad terminal client +* [WG Commander](https://github.com/AndrianBdn/wg-cmd): a TUI for a simple WireGuard VPN setup * [wishlist](https://github.com/charmbracelet/wishlist): an SSH directory -[portal]: https://github.com/ZinoKader/portal - ## Feedback -We'd love to hear your thoughts on this tutorial. Feel free to drop us a note! +We'd love to hear your thoughts on this project. Feel free to drop us a note! * [Twitter](https://twitter.com/charmcli) -* [The Fediverse](https://mastodon.technology/@charm) +* [The Fediverse](https://mastodon.social/@charmcli) +* [Discord](https://charm.sh/chat) ## Acknowledgments Bubble Tea is based on the paradigms of [The Elm Architecture][elm] by Evan -Czaplicki et alia and the excellent [go-tea][gotea] by TJ Holowaychuk. +Czaplicki et alia and the excellent [go-tea][gotea] by TJ Holowaychuk. It’s +inspired by the many great [_Zeichenorientierte Benutzerschnittstellen_][zb] +of days past. [elm]: https://guide.elm-lang.org/architecture/ [gotea]: https://github.com/tj/go-tea +[zb]: https://de.wikipedia.org/wiki/Zeichenorientierte_Benutzerschnittstelle ## License @@ -349,4 +402,4 @@ Part of [Charm](https://charm.sh). The Charm logo -Charm热爱开源 • Charm loves open source +Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة diff --git a/vendor/github.com/charmbracelet/bubbletea/commands.go b/vendor/github.com/charmbracelet/bubbletea/commands.go index 99007b2ce2b33..7c30a1216db34 100644 --- a/vendor/github.com/charmbracelet/bubbletea/commands.go +++ b/vendor/github.com/charmbracelet/bubbletea/commands.go @@ -1,12 +1,48 @@ package tea -// Convenience commands. Not part of the Bubble Tea core, but potentially -// handy. - import ( "time" ) +// Batch performs a bunch of commands concurrently with no ordering guarantees +// about the results. Use a Batch to return several commands. +// +// Example: +// +// func (m model) Init() Cmd { +// return tea.Batch(someCommand, someOtherCommand) +// } +func Batch(cmds ...Cmd) Cmd { + var validCmds []Cmd + for _, c := range cmds { + if c == nil { + continue + } + validCmds = append(validCmds, c) + } + if len(validCmds) == 0 { + return nil + } + return func() Msg { + return BatchMsg(validCmds) + } +} + +// BatchMsg is a message used to perform a bunch of commands concurrently with +// no ordering guarantees. You can send a BatchMsg with Batch. +type BatchMsg []Cmd + +// Sequence runs the given commands one at a time, in order. Contrast this with +// Batch, which runs commands concurrently. +func Sequence(cmds ...Cmd) Cmd { + return func() Msg { + return sequenceMsg(cmds) + } +} + +// sequenceMsg is used internally to run the given commands in order. +type sequenceMsg []Cmd + // Every is a command that ticks in sync with the system clock. So, if you // wanted to tick with the system clock every second, minute or hour you // could use this. It's also handy for having different things tick in sync. @@ -19,38 +55,38 @@ import ( // To produce the command, pass a duration and a function which returns // a message containing the time at which the tick occurred. // -// type TickMsg time.Time +// type TickMsg time.Time // -// cmd := Every(time.Second, func(t time.Time) Msg { -// return TickMsg(t) -// }) +// cmd := Every(time.Second, func(t time.Time) Msg { +// return TickMsg(t) +// }) // // Beginners' note: Every sends a single message and won't automatically // dispatch messages at an interval. To do that, you'll want to return another // Every command after receiving your tick message. For example: // -// type TickMsg time.Time -// -// // Send a message every second. -// func tickEvery() Cmd { -// return Every(time.Second, func(t time.Time) Msg { -// return TickMsg(t) -// }) -// } -// -// func (m model) Init() Cmd { -// // Start ticking. -// return tickEvery() -// } -// -// func (m model) Update(msg Msg) (Model, Cmd) { -// switch msg.(type) { -// case TickMsg: -// // Return your Every command again to loop. -// return m, tickEvery() -// } -// return m, nil -// } +// type TickMsg time.Time +// +// // Send a message every second. +// func tickEvery() Cmd { +// return Every(time.Second, func(t time.Time) Msg { +// return TickMsg(t) +// }) +// } +// +// func (m model) Init() Cmd { +// // Start ticking. +// return tickEvery() +// } +// +// func (m model) Update(msg Msg) (Model, Cmd) { +// switch msg.(type) { +// case TickMsg: +// // Return your Every command again to loop. +// return m, tickEvery() +// } +// return m, nil +// } // // Every is analogous to Tick in the Elm Architecture. func Every(duration time.Duration, fn func(time.Time) Msg) Cmd { @@ -63,44 +99,43 @@ func Every(duration time.Duration, fn func(time.Time) Msg) Cmd { } // Tick produces a command at an interval independent of the system clock at -// the given duration. That is, the timer begins when precisely when invoked, +// the given duration. That is, the timer begins precisely when invoked, // and runs for its entire duration. // // To produce the command, pass a duration and a function which returns // a message containing the time at which the tick occurred. // -// type TickMsg time.Time +// type TickMsg time.Time // -// cmd := Tick(time.Second, func(t time.Time) Msg { -// return TickMsg(t) -// }) +// cmd := Tick(time.Second, func(t time.Time) Msg { +// return TickMsg(t) +// }) // // Beginners' note: Tick sends a single message and won't automatically // dispatch messages at an interval. To do that, you'll want to return another // Tick command after receiving your tick message. For example: // -// type TickMsg time.Time -// -// func doTick() Cmd { -// return Tick(time.Second, func(t time.Time) Msg { -// return TickMsg(t) -// }) -// } -// -// func (m model) Init() Cmd { -// // Start ticking. -// return doTick() -// } -// -// func (m model) Update(msg Msg) (Model, Cmd) { -// switch msg.(type) { -// case TickMsg: -// // Return your Tick command again to loop. -// return m, doTick() -// } -// return m, nil -// } -// +// type TickMsg time.Time +// +// func doTick() Cmd { +// return Tick(time.Second, func(t time.Time) Msg { +// return TickMsg(t) +// }) +// } +// +// func (m model) Init() Cmd { +// // Start ticking. +// return doTick() +// } +// +// func (m model) Update(msg Msg) (Model, Cmd) { +// switch msg.(type) { +// case TickMsg: +// // Return your Tick command again to loop. +// return m, doTick() +// } +// return m, nil +// } func Tick(d time.Duration, fn func(time.Time) Msg) Cmd { return func() Msg { t := time.NewTimer(d) @@ -112,15 +147,16 @@ func Tick(d time.Duration, fn func(time.Time) Msg) Cmd { // commands. // The Msg returned is the first non-nil message returned by a Cmd. // -// func saveStateCmd() Msg { -// if err := save(); err != nil { -// return errMsg{err} -// } -// return nil -// } +// func saveStateCmd() Msg { +// if err := save(); err != nil { +// return errMsg{err} +// } +// return nil +// } // -// cmd := Sequentially(saveStateCmd, Quit) +// cmd := Sequentially(saveStateCmd, Quit) // +// Deprecated: use Sequence instead. func Sequentially(cmds ...Cmd) Cmd { return func() Msg { for _, cmd := range cmds { diff --git a/vendor/github.com/charmbracelet/bubbletea/exec.go b/vendor/github.com/charmbracelet/bubbletea/exec.go index d1d9d69a29f35..fb6d91ed15de3 100644 --- a/vendor/github.com/charmbracelet/bubbletea/exec.go +++ b/vendor/github.com/charmbracelet/bubbletea/exec.go @@ -13,7 +13,7 @@ type execMsg struct { } // Exec is used to perform arbitrary I/O in a blocking fashion, effectively -// pausing the Program while execution is runnning and resuming it when +// pausing the Program while execution is running and resuming it when // execution has completed. // // Most of the time you'll want to use ExecProcess, which runs an exec.Cmd. @@ -34,17 +34,17 @@ func Exec(c ExecCommand, fn ExecCallback) Cmd { // a message containing the error which may have occurred when running the // ExecCommand. // -// type VimFinishedMsg struct { err error } +// type VimFinishedMsg struct { err error } // -// c := exec.Command("vim", "file.txt") +// c := exec.Command("vim", "file.txt") // -// cmd := ExecProcess(c, func(err error) Msg { -// return VimFinishedMsg{err: error} -// }) +// cmd := ExecProcess(c, func(err error) Msg { +// return VimFinishedMsg{err: err} +// }) // // Or, if you don't care about errors, you could simply: // -// cmd := ExecProcess(exec.Command("vim", "file.txt"), nil) +// cmd := ExecProcess(exec.Command("vim", "file.txt"), nil) // // For non-interactive i/o you should use a Cmd (that is, a tea.Cmd). func ExecProcess(c *exec.Cmd, fn ExecCallback) Cmd { @@ -109,7 +109,7 @@ func (p *Program) exec(c ExecCommand, fn ExecCallback) { } c.SetStdin(p.input) - c.SetStdout(p.output) + c.SetStdout(p.output.TTY()) c.SetStderr(os.Stderr) // Execute system command. diff --git a/vendor/github.com/charmbracelet/bubbletea/key.go b/vendor/github.com/charmbracelet/bubbletea/key.go index e2415a994fa29..c2e5e3ab01675 100644 --- a/vendor/github.com/charmbracelet/bubbletea/key.go +++ b/vendor/github.com/charmbracelet/bubbletea/key.go @@ -2,39 +2,40 @@ package tea import ( "errors" - "fmt" "io" "unicode/utf8" + + "github.com/mattn/go-localereader" ) // KeyMsg contains information about a keypress. KeyMsgs are always sent to // the program's update function. There are a couple general patterns you could // use to check for keypresses: // -// // Switch on the string representation of the key (shorter) -// switch msg := msg.(type) { -// case KeyMsg: -// switch msg.String() { -// case "enter": -// fmt.Println("you pressed enter!") -// case "a": -// fmt.Println("you pressed a!") -// } -// } +// // Switch on the string representation of the key (shorter) +// switch msg := msg.(type) { +// case KeyMsg: +// switch msg.String() { +// case "enter": +// fmt.Println("you pressed enter!") +// case "a": +// fmt.Println("you pressed a!") +// } +// } // -// // Switch on the key type (more foolproof) -// switch msg := msg.(type) { -// case KeyMsg: -// switch msg.Type { -// case KeyEnter: -// fmt.Println("you pressed enter!") -// case KeyRunes: -// switch string(msg.Runes) { -// case "a": -// fmt.Println("you pressed a!") -// } -// } -// } +// // Switch on the key type (more foolproof) +// switch msg := msg.(type) { +// case KeyMsg: +// switch msg.Type { +// case KeyEnter: +// fmt.Println("you pressed enter!") +// case KeyRunes: +// switch string(msg.Runes) { +// case "a": +// fmt.Println("you pressed a!") +// } +// } +// } // // Note that Key.Runes will always contain at least one character, so you can // always safely call Key.Runes[0]. In most cases Key.Runes will only contain @@ -58,10 +59,9 @@ type Key struct { // String returns a friendly string representation for a key. It's safe (and // encouraged) for use in key comparison. // -// k := Key{Type: KeyEnter} -// fmt.Println(k) -// // Output: enter -// +// k := Key{Type: KeyEnter} +// fmt.Println(k) +// // Output: enter func (k Key) String() (str string) { if k.Alt { str += "alt+" @@ -80,16 +80,16 @@ func (k Key) String() (str string) { // All other keys will be type KeyRunes. To get the rune value, check the Rune // method on a Key struct, or use the Key.String() method: // -// k := Key{Type: KeyRunes, Runes: []rune{'a'}, Alt: true} -// if k.Type == KeyRunes { +// k := Key{Type: KeyRunes, Runes: []rune{'a'}, Alt: true} +// if k.Type == KeyRunes { // -// fmt.Println(k.Runes) -// // Output: a +// fmt.Println(k.Runes) +// // Output: a // -// fmt.Println(k.String()) -// // Output: alt+a +// fmt.Println(k.String()) +// // Output: alt+a // -// } +// } type KeyType int func (k KeyType) String() (str string) { @@ -197,20 +197,29 @@ const ( KeyEnd KeyPgUp KeyPgDown + KeyCtrlPgUp + KeyCtrlPgDown KeyDelete + KeyInsert KeySpace KeyCtrlUp KeyCtrlDown KeyCtrlRight KeyCtrlLeft + KeyCtrlHome + KeyCtrlEnd KeyShiftUp KeyShiftDown KeyShiftRight KeyShiftLeft + KeyShiftHome + KeyShiftEnd KeyCtrlShiftUp KeyCtrlShiftDown KeyCtrlShiftLeft KeyCtrlShiftRight + KeyCtrlShiftHome + KeyCtrlShiftEnd KeyF1 KeyF2 KeyF3 @@ -280,9 +289,18 @@ var keyNames = map[KeyType]string{ KeyShiftTab: "shift+tab", KeyHome: "home", KeyEnd: "end", + KeyCtrlHome: "ctrl+home", + KeyCtrlEnd: "ctrl+end", + KeyShiftHome: "shift+home", + KeyShiftEnd: "shift+end", + KeyCtrlShiftHome: "ctrl+shift+home", + KeyCtrlShiftEnd: "ctrl+shift+end", KeyPgUp: "pgup", KeyPgDown: "pgdown", + KeyCtrlPgUp: "ctrl+pgup", + KeyCtrlPgDown: "ctrl+pgdown", KeyDelete: "delete", + KeyInsert: "insert", KeyCtrlUp: "ctrl+up", KeyCtrlDown: "ctrl+down", KeyCtrlRight: "ctrl+right", @@ -374,100 +392,175 @@ var sequences = map[string]Key{ "\x1b[1;8D": {Type: KeyCtrlShiftLeft, Alt: true}, // Miscellaneous keys - "\x1b[Z": {Type: KeyShiftTab}, + "\x1b[Z": {Type: KeyShiftTab}, + + "\x1b[2~": {Type: KeyInsert}, + "\x1b[3;2~": {Type: KeyInsert, Alt: true}, + "\x1b\x1b[2~": {Type: KeyInsert, Alt: true}, // urxvt + "\x1b[3~": {Type: KeyDelete}, "\x1b[3;3~": {Type: KeyDelete, Alt: true}, - "\x1b[1~": {Type: KeyHome}, - "\x1b[1;3H~": {Type: KeyHome, Alt: true}, - "\x1b[4~": {Type: KeyEnd}, - "\x1b[1;3F~": {Type: KeyEnd, Alt: true}, + "\x1b\x1b[3~": {Type: KeyDelete, Alt: true}, // urxvt + "\x1b[5~": {Type: KeyPgUp}, "\x1b[5;3~": {Type: KeyPgUp, Alt: true}, + "\x1b\x1b[5~": {Type: KeyPgUp, Alt: true}, // urxvt + "\x1b[5;5~": {Type: KeyCtrlPgUp}, + "\x1b[5^": {Type: KeyCtrlPgUp}, // urxvt + "\x1b[5;7~": {Type: KeyCtrlPgUp, Alt: true}, + "\x1b\x1b[5^": {Type: KeyCtrlPgUp, Alt: true}, // urxvt + "\x1b[6~": {Type: KeyPgDown}, "\x1b[6;3~": {Type: KeyPgDown, Alt: true}, - "\x1b[7~": {Type: KeyHome}, // urxvt - "\x1b[8~": {Type: KeyEnd}, // urxvt - "\x1b\x1b[3~": {Type: KeyDelete, Alt: true}, // urxvt - "\x1b\x1b[5~": {Type: KeyPgUp, Alt: true}, // urxvt "\x1b\x1b[6~": {Type: KeyPgDown, Alt: true}, // urxvt - "\x1b\x1b[7~": {Type: KeyHome, Alt: true}, // urxvt - "\x1b\x1b[8~": {Type: KeyEnd, Alt: true}, // urxvt + "\x1b[6;5~": {Type: KeyCtrlPgDown}, + "\x1b[6^": {Type: KeyCtrlPgDown}, // urxvt + "\x1b[6;7~": {Type: KeyCtrlPgDown, Alt: true}, + "\x1b\x1b[6^": {Type: KeyCtrlPgDown, Alt: true}, // urxvt + + "\x1b[1~": {Type: KeyHome}, + "\x1b[H": {Type: KeyHome}, // xterm, lxterm + "\x1b[1;3H": {Type: KeyHome, Alt: true}, // xterm, lxterm + "\x1b[1;5H": {Type: KeyCtrlHome}, // xterm, lxterm + "\x1b[1;7H": {Type: KeyCtrlHome, Alt: true}, // xterm, lxterm + "\x1b[1;2H": {Type: KeyShiftHome}, // xterm, lxterm + "\x1b[1;4H": {Type: KeyShiftHome, Alt: true}, // xterm, lxterm + "\x1b[1;6H": {Type: KeyCtrlShiftHome}, // xterm, lxterm + "\x1b[1;8H": {Type: KeyCtrlShiftHome, Alt: true}, // xterm, lxterm + + "\x1b[4~": {Type: KeyEnd}, + "\x1b[F": {Type: KeyEnd}, // xterm, lxterm + "\x1b[1;3F": {Type: KeyEnd, Alt: true}, // xterm, lxterm + "\x1b[1;5F": {Type: KeyCtrlEnd}, // xterm, lxterm + "\x1b[1;7F": {Type: KeyCtrlEnd, Alt: true}, // xterm, lxterm + "\x1b[1;2F": {Type: KeyShiftEnd}, // xterm, lxterm + "\x1b[1;4F": {Type: KeyShiftEnd, Alt: true}, // xterm, lxterm + "\x1b[1;6F": {Type: KeyCtrlShiftEnd}, // xterm, lxterm + "\x1b[1;8F": {Type: KeyCtrlShiftEnd, Alt: true}, // xterm, lxterm + + "\x1b[7~": {Type: KeyHome}, // urxvt + "\x1b\x1b[7~": {Type: KeyHome, Alt: true}, // urxvt + "\x1b[7^": {Type: KeyCtrlHome}, // urxvt + "\x1b\x1b[7^": {Type: KeyCtrlHome, Alt: true}, // urxvt + "\x1b[7$": {Type: KeyShiftHome}, // urxvt + "\x1b\x1b[7$": {Type: KeyShiftHome, Alt: true}, // urxvt + "\x1b[7@": {Type: KeyCtrlShiftHome}, // urxvt + "\x1b\x1b[7@": {Type: KeyCtrlShiftHome, Alt: true}, // urxvt + + "\x1b[8~": {Type: KeyEnd}, // urxvt + "\x1b\x1b[8~": {Type: KeyEnd, Alt: true}, // urxvt + "\x1b[8^": {Type: KeyCtrlEnd}, // urxvt + "\x1b\x1b[8^": {Type: KeyCtrlEnd, Alt: true}, // urxvt + "\x1b[8$": {Type: KeyShiftEnd}, // urxvt + "\x1b\x1b[8$": {Type: KeyShiftEnd, Alt: true}, // urxvt + "\x1b[8@": {Type: KeyCtrlShiftEnd}, // urxvt + "\x1b\x1b[8@": {Type: KeyCtrlShiftEnd, Alt: true}, // urxvt + + // Function keys, Linux console + "\x1b[[A": {Type: KeyF1}, // linux console + "\x1b[[B": {Type: KeyF2}, // linux console + "\x1b[[C": {Type: KeyF3}, // linux console + "\x1b[[D": {Type: KeyF4}, // linux console + "\x1b[[E": {Type: KeyF5}, // linux console // Function keys, X11 - "\x1bOP": {Type: KeyF1}, // vt100 - "\x1bOQ": {Type: KeyF2}, // vt100 - "\x1bOR": {Type: KeyF3}, // vt100 - "\x1bOS": {Type: KeyF4}, // vt100 - "\x1b[15~": {Type: KeyF5}, // also urxvt - "\x1b[17~": {Type: KeyF6}, // also urxvt - "\x1b[18~": {Type: KeyF7}, // also urxvt - "\x1b[19~": {Type: KeyF8}, // also urxvt - "\x1b[20~": {Type: KeyF9}, // also urxvt - "\x1b[21~": {Type: KeyF10}, // also urxvt - "\x1b[23~": {Type: KeyF11}, // also urxvt - "\x1b[24~": {Type: KeyF12}, // also urxvt - "\x1b[1;2P": {Type: KeyF13}, - "\x1b[1;2Q": {Type: KeyF14}, - "\x1b[1;2R": {Type: KeyF15}, - "\x1b[1;2S": {Type: KeyF16}, + "\x1bOP": {Type: KeyF1}, // vt100, xterm + "\x1bOQ": {Type: KeyF2}, // vt100, xterm + "\x1bOR": {Type: KeyF3}, // vt100, xterm + "\x1bOS": {Type: KeyF4}, // vt100, xterm + + "\x1b[1;3P": {Type: KeyF1, Alt: true}, // vt100, xterm + "\x1b[1;3Q": {Type: KeyF2, Alt: true}, // vt100, xterm + "\x1b[1;3R": {Type: KeyF3, Alt: true}, // vt100, xterm + "\x1b[1;3S": {Type: KeyF4, Alt: true}, // vt100, xterm + + "\x1b[11~": {Type: KeyF1}, // urxvt + "\x1b[12~": {Type: KeyF2}, // urxvt + "\x1b[13~": {Type: KeyF3}, // urxvt + "\x1b[14~": {Type: KeyF4}, // urxvt + + "\x1b\x1b[11~": {Type: KeyF1, Alt: true}, // urxvt + "\x1b\x1b[12~": {Type: KeyF2, Alt: true}, // urxvt + "\x1b\x1b[13~": {Type: KeyF3, Alt: true}, // urxvt + "\x1b\x1b[14~": {Type: KeyF4, Alt: true}, // urxvt + + "\x1b[15~": {Type: KeyF5}, // vt100, xterm, also urxvt + + "\x1b[15;3~": {Type: KeyF5, Alt: true}, // vt100, xterm, also urxvt + + "\x1b\x1b[15~": {Type: KeyF5, Alt: true}, // urxvt + + "\x1b[17~": {Type: KeyF6}, // vt100, xterm, also urxvt + "\x1b[18~": {Type: KeyF7}, // vt100, xterm, also urxvt + "\x1b[19~": {Type: KeyF8}, // vt100, xterm, also urxvt + "\x1b[20~": {Type: KeyF9}, // vt100, xterm, also urxvt + "\x1b[21~": {Type: KeyF10}, // vt100, xterm, also urxvt + + "\x1b\x1b[17~": {Type: KeyF6, Alt: true}, // urxvt + "\x1b\x1b[18~": {Type: KeyF7, Alt: true}, // urxvt + "\x1b\x1b[19~": {Type: KeyF8, Alt: true}, // urxvt + "\x1b\x1b[20~": {Type: KeyF9, Alt: true}, // urxvt + "\x1b\x1b[21~": {Type: KeyF10, Alt: true}, // urxvt + + "\x1b[17;3~": {Type: KeyF6, Alt: true}, // vt100, xterm + "\x1b[18;3~": {Type: KeyF7, Alt: true}, // vt100, xterm + "\x1b[19;3~": {Type: KeyF8, Alt: true}, // vt100, xterm + "\x1b[20;3~": {Type: KeyF9, Alt: true}, // vt100, xterm + "\x1b[21;3~": {Type: KeyF10, Alt: true}, // vt100, xterm + + "\x1b[23~": {Type: KeyF11}, // vt100, xterm, also urxvt + "\x1b[24~": {Type: KeyF12}, // vt100, xterm, also urxvt + + "\x1b[23;3~": {Type: KeyF11, Alt: true}, // vt100, xterm + "\x1b[24;3~": {Type: KeyF12, Alt: true}, // vt100, xterm + + "\x1b\x1b[23~": {Type: KeyF11, Alt: true}, // urxvt + "\x1b\x1b[24~": {Type: KeyF12, Alt: true}, // urxvt + + "\x1b[1;2P": {Type: KeyF13}, + "\x1b[1;2Q": {Type: KeyF14}, + + "\x1b[25~": {Type: KeyF13}, // vt100, xterm, also urxvt + "\x1b[26~": {Type: KeyF14}, // vt100, xterm, also urxvt + + "\x1b[25;3~": {Type: KeyF13, Alt: true}, // vt100, xterm + "\x1b[26;3~": {Type: KeyF14, Alt: true}, // vt100, xterm + + "\x1b\x1b[25~": {Type: KeyF13, Alt: true}, // urxvt + "\x1b\x1b[26~": {Type: KeyF14, Alt: true}, // urxvt + + "\x1b[1;2R": {Type: KeyF15}, + "\x1b[1;2S": {Type: KeyF16}, + + "\x1b[28~": {Type: KeyF15}, // vt100, xterm, also urxvt + "\x1b[29~": {Type: KeyF16}, // vt100, xterm, also urxvt + + "\x1b[28;3~": {Type: KeyF15, Alt: true}, // vt100, xterm + "\x1b[29;3~": {Type: KeyF16, Alt: true}, // vt100, xterm + + "\x1b\x1b[28~": {Type: KeyF15, Alt: true}, // urxvt + "\x1b\x1b[29~": {Type: KeyF16, Alt: true}, // urxvt + "\x1b[15;2~": {Type: KeyF17}, "\x1b[17;2~": {Type: KeyF18}, "\x1b[18;2~": {Type: KeyF19}, "\x1b[19;2~": {Type: KeyF20}, - // Function keys with the alt modifier, X11 - "\x1b[1;3P": {Type: KeyF1, Alt: true}, - "\x1b[1;3Q": {Type: KeyF2, Alt: true}, - "\x1b[1;3R": {Type: KeyF3, Alt: true}, - "\x1b[1;3S": {Type: KeyF4, Alt: true}, - "\x1b[15;3~": {Type: KeyF5, Alt: true}, - "\x1b[17;3~": {Type: KeyF6, Alt: true}, - "\x1b[18;3~": {Type: KeyF7, Alt: true}, - "\x1b[19;3~": {Type: KeyF8, Alt: true}, - "\x1b[20;3~": {Type: KeyF9, Alt: true}, - "\x1b[21;3~": {Type: KeyF10, Alt: true}, - "\x1b[23;3~": {Type: KeyF11, Alt: true}, - "\x1b[24;3~": {Type: KeyF12, Alt: true}, - - // Function keys, urxvt - "\x1b[11~": {Type: KeyF1}, - "\x1b[12~": {Type: KeyF2}, - "\x1b[13~": {Type: KeyF3}, - "\x1b[14~": {Type: KeyF4}, - "\x1b[25~": {Type: KeyF13}, - "\x1b[26~": {Type: KeyF14}, - "\x1b[28~": {Type: KeyF15}, - "\x1b[29~": {Type: KeyF16}, "\x1b[31~": {Type: KeyF17}, "\x1b[32~": {Type: KeyF18}, "\x1b[33~": {Type: KeyF19}, "\x1b[34~": {Type: KeyF20}, - // Function keys with the alt modifier, urxvt - "\x1b\x1b[11~": {Type: KeyF1, Alt: true}, - "\x1b\x1b[12~": {Type: KeyF2, Alt: true}, - "\x1b\x1b[13~": {Type: KeyF3, Alt: true}, - "\x1b\x1b[14~": {Type: KeyF4, Alt: true}, - "\x1b\x1b[25~": {Type: KeyF13, Alt: true}, - "\x1b\x1b[26~": {Type: KeyF14, Alt: true}, - "\x1b\x1b[28~": {Type: KeyF15, Alt: true}, - "\x1b\x1b[29~": {Type: KeyF16, Alt: true}, - "\x1b\x1b[31~": {Type: KeyF17, Alt: true}, - "\x1b\x1b[32~": {Type: KeyF18, Alt: true}, - "\x1b\x1b[33~": {Type: KeyF19, Alt: true}, - "\x1b\x1b[34~": {Type: KeyF20, Alt: true}, -} - -// Hex code mappings. -var hexes = map[string]Key{ - "1b0d": {Type: KeyEnter, Alt: true}, - "1b7f": {Type: KeyBackspace, Alt: true}, + "\x1b\x1b[31~": {Type: KeyF17, Alt: true}, // urxvt + "\x1b\x1b[32~": {Type: KeyF18, Alt: true}, // urxvt + "\x1b\x1b[33~": {Type: KeyF19, Alt: true}, // urxvt + "\x1b\x1b[34~": {Type: KeyF20, Alt: true}, // urxvt - // Powershell - "1b4f41": {Type: KeyUp, Alt: false}, - "1b4f42": {Type: KeyDown, Alt: false}, - "1b4f43": {Type: KeyRight, Alt: false}, - "1b4f44": {Type: KeyLeft, Alt: false}, + // Powershell sequences. + "\x1bOA": {Type: KeyUp, Alt: false}, + "\x1bOB": {Type: KeyDown, Alt: false}, + "\x1bOC": {Type: KeyRight, Alt: false}, + "\x1bOD": {Type: KeyLeft, Alt: false}, } // readInputs reads keypress and mouse inputs from a TTY and returns messages @@ -480,10 +573,15 @@ func readInputs(input io.Reader) ([]Msg, error) { if err != nil { return nil, err } + b := buf[:numBytes] + b, err = localereader.UTF8(b) + if err != nil { + return nil, err + } // Check if it's a mouse event. For now we're parsing X10-type mouse events // only. - mouseEvent, err := parseX10MouseEvents(buf[:numBytes]) + mouseEvent, err := parseX10MouseEvents(b) if err == nil { var m []Msg for _, v := range mouseEvent { @@ -492,37 +590,8 @@ func readInputs(input io.Reader) ([]Msg, error) { return m, nil } - // Is it a sequence, like an arrow key? - if k, ok := sequences[string(buf[:numBytes])]; ok { - return []Msg{ - KeyMsg(k), - }, nil - } - - // Some of these need special handling. - hex := fmt.Sprintf("%x", buf[:numBytes]) - if k, ok := hexes[hex]; ok { - return []Msg{ - KeyMsg(k), - }, nil - } - - // Is the alt key pressed? If so, the buffer will be prefixed with an - // escape. - if numBytes > 1 && buf[0] == 0x1b { - // Now remove the initial escape sequence and re-process to get the - // character being pressed in combination with alt. - c, _ := utf8.DecodeRune(buf[1:]) - if c == utf8.RuneError { - return nil, errors.New("could not decode rune after removing initial escape") - } - return []Msg{ - KeyMsg(Key{Alt: true, Type: KeyRunes, Runes: []rune{c}}), - }, nil - } - + var runeSets [][]rune var runes []rune - b := buf[:numBytes] // Translate input into runes. In most cases we'll receive exactly one // rune, but there are cases, particularly when an input method editor is @@ -532,38 +601,64 @@ func readInputs(input io.Reader) ([]Msg, error) { if r == utf8.RuneError { return nil, errors.New("could not decode rune") } + + if r == '\x1b' && len(runes) > 1 { + // a new key sequence has started + runeSets = append(runeSets, runes) + runes = []rune{} + } + runes = append(runes, r) w = width } + // add the final set of runes we decoded + runeSets = append(runeSets, runes) - if len(runes) == 0 { + if len(runeSets) == 0 { return nil, errors.New("received 0 runes from input") - } else if len(runes) > 1 { - // We received multiple runes, so we know this isn't a control - // character, sequence, and so on. - return []Msg{ - KeyMsg(Key{Type: KeyRunes, Runes: runes}), - }, nil } - // Is the first rune a control character? - r := KeyType(runes[0]) - if numBytes == 1 && r <= keyUS || r == keyDEL { - return []Msg{ - KeyMsg(Key{Type: r}), - }, nil - } + var msgs []Msg + for _, runes := range runeSets { + // Is it a sequence, like an arrow key? + if k, ok := sequences[string(runes)]; ok { + msgs = append(msgs, KeyMsg(k)) + continue + } + + // Is this an unrecognized CSI sequence? If so, ignore it. + if len(runes) > 2 && runes[0] == 0x1b && (runes[1] == '[' || + (len(runes) > 3 && runes[1] == 0x1b && runes[2] == '[')) { + continue + } - // If it's a space, override the type with KeySpace (but still include the - // rune). - if runes[0] == ' ' { - return []Msg{ - KeyMsg(Key{Type: KeySpace, Runes: runes}), - }, nil + // Is the alt key pressed? If so, the buffer will be prefixed with an + // escape. + alt := false + if len(runes) > 1 && runes[0] == 0x1b { + alt = true + runes = runes[1:] + } + + for _, v := range runes { + // Is the first rune a control character? + r := KeyType(v) + if r <= keyUS || r == keyDEL { + msgs = append(msgs, KeyMsg(Key{Type: r, Alt: alt})) + continue + } + + // If it's a space, override the type with KeySpace (but still include + // the rune). + if r == ' ' { + msgs = append(msgs, KeyMsg(Key{Type: KeySpace, Runes: []rune{v}, Alt: alt})) + continue + } + + // Welp, just regular, ol' runes. + msgs = append(msgs, KeyMsg(Key{Type: KeyRunes, Runes: []rune{v}, Alt: alt})) + } } - // Welp, it's just a regular, ol' single rune. - return []Msg{ - KeyMsg(Key{Type: KeyRunes, Runes: runes}), - }, nil + return msgs, nil } diff --git a/vendor/github.com/charmbracelet/bubbletea/logging.go b/vendor/github.com/charmbracelet/bubbletea/logging.go index bca66ca5a28b1..59258d4c7c124 100644 --- a/vendor/github.com/charmbracelet/bubbletea/logging.go +++ b/vendor/github.com/charmbracelet/bubbletea/logging.go @@ -1,6 +1,7 @@ package tea import ( + "io" "log" "os" "unicode" @@ -12,14 +13,26 @@ import ( // // Don't forget to close the file when you're done with it. // -// f, err := LogToFile("debug.log", "debug") -// if err != nil { -// fmt.Println("fatal:", err) -// os.Exit(1) -// } -// defer f.Close() +// f, err := LogToFile("debug.log", "debug") +// if err != nil { +// fmt.Println("fatal:", err) +// os.Exit(1) +// } +// defer f.Close() func LogToFile(path string, prefix string) (*os.File, error) { - f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) + return LogToFileWith(path, prefix, log.Default()) +} + +// LogOptionsSetter is an interface implemented by stdlib's log and charm's log +// libraries. +type LogOptionsSetter interface { + SetOutput(io.Writer) + SetPrefix(string) +} + +// LogToFileWith does allows to call LogToFile with a custom LogOptionsSetter. +func LogToFileWith(path string, prefix string, log LogOptionsSetter) (*os.File, error) { + f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o644) if err != nil { return nil, err } diff --git a/vendor/github.com/charmbracelet/bubbletea/mouse.go b/vendor/github.com/charmbracelet/bubbletea/mouse.go index 5fb7427c9cab3..f918d20695a75 100644 --- a/vendor/github.com/charmbracelet/bubbletea/mouse.go +++ b/vendor/github.com/charmbracelet/bubbletea/mouse.go @@ -5,9 +5,9 @@ import ( "errors" ) -// MouseMsg contains information about a mouse event and are sent to a programs +// MouseMsg contains information about a mouse event and is sent to a program's // update function when mouse activity occurs. Note that the mouse must first -// be enabled via in order the mouse events to be received. +// be enabled in order for the mouse events to be received. type MouseMsg MouseEvent // MouseEvent represents a mouse event, which could be a click, a scroll wheel @@ -63,7 +63,7 @@ var mouseEventTypes = map[MouseEventType]string{ // // X10 mouse events look like: // -// ESC [M Cb Cx Cy +// ESC [M Cb Cx Cy // // See: http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking func parseX10MouseEvents(buf []byte) ([]MouseEvent, error) { diff --git a/vendor/github.com/charmbracelet/bubbletea/nil_renderer.go b/vendor/github.com/charmbracelet/bubbletea/nil_renderer.go index 03dd94949209f..f5637aa47b934 100644 --- a/vendor/github.com/charmbracelet/bubbletea/nil_renderer.go +++ b/vendor/github.com/charmbracelet/bubbletea/nil_renderer.go @@ -2,10 +2,18 @@ package tea type nilRenderer struct{} -func (n nilRenderer) start() {} -func (n nilRenderer) stop() {} -func (n nilRenderer) kill() {} -func (n nilRenderer) write(v string) {} -func (n nilRenderer) repaint() {} -func (n nilRenderer) altScreen() bool { return false } -func (n nilRenderer) setAltScreen(v bool) {} +func (n nilRenderer) start() {} +func (n nilRenderer) stop() {} +func (n nilRenderer) kill() {} +func (n nilRenderer) write(_ string) {} +func (n nilRenderer) repaint() {} +func (n nilRenderer) clearScreen() {} +func (n nilRenderer) altScreen() bool { return false } +func (n nilRenderer) enterAltScreen() {} +func (n nilRenderer) exitAltScreen() {} +func (n nilRenderer) showCursor() {} +func (n nilRenderer) hideCursor() {} +func (n nilRenderer) enableMouseCellMotion() {} +func (n nilRenderer) disableMouseCellMotion() {} +func (n nilRenderer) enableMouseAllMotion() {} +func (n nilRenderer) disableMouseAllMotion() {} diff --git a/vendor/github.com/charmbracelet/bubbletea/options.go b/vendor/github.com/charmbracelet/bubbletea/options.go index 09e5ecbd73f0e..30f7e6c50bc3e 100644 --- a/vendor/github.com/charmbracelet/bubbletea/options.go +++ b/vendor/github.com/charmbracelet/bubbletea/options.go @@ -1,37 +1,64 @@ package tea -import "io" +import ( + "context" + "io" + + "github.com/muesli/termenv" +) // ProgramOption is used to set options when initializing a Program. Program can // accept a variable number of options. // // Example usage: // -// p := NewProgram(model, WithInput(someInput), WithOutput(someOutput)) -// +// p := NewProgram(model, WithInput(someInput), WithOutput(someOutput)) type ProgramOption func(*Program) +// WithContext lets you specify a context in which to run the Program. This is +// useful if you want to cancel the execution from outside. When a Program gets +// cancelled it will exit with an error ErrProgramKilled. +func WithContext(ctx context.Context) ProgramOption { + return func(p *Program) { + p.ctx = ctx + } +} + // WithOutput sets the output which, by default, is stdout. In most cases you // won't need to use this. func WithOutput(output io.Writer) ProgramOption { - return func(m *Program) { - m.output = output + return func(p *Program) { + if o, ok := output.(*termenv.Output); ok { + p.output = o + } else { + p.output = termenv.NewOutput(output, termenv.WithColorCache(true)) + } } } // WithInput sets the input which, by default, is stdin. In most cases you -// won't need to use this. +// won't need to use this. To disable input entirely pass nil. +// +// p := NewProgram(model, WithInput(nil)) func WithInput(input io.Reader) ProgramOption { - return func(m *Program) { - m.input = input - m.startupOptions |= withCustomInput + return func(p *Program) { + p.input = input + p.inputType = customInput } } -// WithInputTTY open a new TTY for input (or console input device on Windows). +// WithInputTTY opens a new TTY for input (or console input device on Windows). func WithInputTTY() ProgramOption { return func(p *Program) { - p.startupOptions |= withInputTTY + p.inputType = ttyInput + } +} + +// WithoutSignalHandler disables the signal handler that Bubble Tea sets up for +// Programs. This is useful if you want to handle signals yourself. +func WithoutSignalHandler() ProgramOption { + return func(p *Program) { + p.startupOptions |= withoutSignalHandler } } @@ -40,8 +67,16 @@ func WithInputTTY() ProgramOption { // unusable state after a panic because Bubble Tea will not perform its usual // cleanup on exit. func WithoutCatchPanics() ProgramOption { - return func(m *Program) { - m.CatchPanics = false + return func(p *Program) { + p.startupOptions |= withoutCatchPanics + } +} + +// WithoutSignals will ignore OS signals. +// This is mainly useful for testing. +func WithoutSignals() ProgramOption { + return func(p *Program) { + p.ignoreSignals = true } } @@ -51,11 +86,11 @@ func WithoutCatchPanics() ProgramOption { // // Example: // -// p := tea.NewProgram(Model{}, tea.WithAltScreen()) -// if err := p.Start(); err != nil { -// fmt.Println("Error running program:", err) -// os.Exit(1) -// } +// p := tea.NewProgram(Model{}, tea.WithAltScreen()) +// if _, err := p.Run(); err != nil { +// fmt.Println("Error running program:", err) +// os.Exit(1) +// } // // To enter the altscreen once the program has already started running use the // EnterAltScreen command. @@ -115,18 +150,53 @@ func WithMouseAllMotion() ProgramOption { // programs. For example, your program could behave like a daemon if output is // not a TTY. func WithoutRenderer() ProgramOption { - return func(m *Program) { - m.renderer = &nilRenderer{} + return func(p *Program) { + p.renderer = &nilRenderer{} } } // WithANSICompressor removes redundant ANSI sequences to produce potentially // smaller output, at the cost of some processing overhead. // -// This feature is provisional, and may be changed removed in a future version +// This feature is provisional, and may be changed or removed in a future version // of this package. func WithANSICompressor() ProgramOption { return func(p *Program) { p.startupOptions |= withANSICompressor } } + +// WithFilter supplies an event filter that will be invoked before Bubble Tea +// processes a tea.Msg. The event filter can return any tea.Msg which will then +// get handled by Bubble Tea instead of the original event. If the event filter +// returns nil, the event will be ignored and Bubble Tea will not process it. +// +// As an example, this could be used to prevent a program from shutting down if +// there are unsaved changes. +// +// Example: +// +// func filter(m tea.Model, msg tea.Msg) tea.Msg { +// if _, ok := msg.(tea.QuitMsg); !ok { +// return msg +// } +// +// model := m.(myModel) +// if model.hasChanges { +// return nil +// } +// +// return msg +// } +// +// p := tea.NewProgram(Model{}, tea.WithFilter(filter)); +// +// if _,err := p.Run(); err != nil { +// fmt.Println("Error running program:", err) +// os.Exit(1) +// } +func WithFilter(filter func(Model, Msg) Msg) ProgramOption { + return func(p *Program) { + p.filter = filter + } +} diff --git a/vendor/github.com/charmbracelet/bubbletea/renderer.go b/vendor/github.com/charmbracelet/bubbletea/renderer.go index aedbb66642f89..a6f416277f659 100644 --- a/vendor/github.com/charmbracelet/bubbletea/renderer.go +++ b/vendor/github.com/charmbracelet/bubbletea/renderer.go @@ -21,12 +21,35 @@ type renderer interface { // in succession. repaint() + // Clears the terminal. + clearScreen() + // Whether or not the alternate screen buffer is enabled. altScreen() bool - - // Record internally that the alternate screen buffer is enabled. This - // does not actually toggle the alternate screen buffer. - setAltScreen(bool) + // Enable the alternate screen buffer. + enterAltScreen() + // Disable the alternate screen buffer. + exitAltScreen() + + // Show the cursor. + showCursor() + // Hide the cursor. + hideCursor() + + // enableMouseCellMotion enables mouse click, release, wheel and motion + // events if a mouse button is pressed (i.e., drag events). + enableMouseCellMotion() + + // DisableMouseCellMotion disables Mouse Cell Motion tracking. + disableMouseCellMotion() + + // EnableMouseAllMotion enables mouse click, release, wheel and motion + // events, regardless of whether a mouse button is pressed. Many modern + // terminals support this, but not all. + enableMouseAllMotion() + + // DisableMouseAllMotion disables All Motion mouse tracking. + disableMouseAllMotion() } // repaintMsg forces a full repaint. diff --git a/vendor/github.com/charmbracelet/bubbletea/screen.go b/vendor/github.com/charmbracelet/bubbletea/screen.go index 63db7ae6561fa..899db3d2575f0 100644 --- a/vendor/github.com/charmbracelet/bubbletea/screen.go +++ b/vendor/github.com/charmbracelet/bubbletea/screen.go @@ -1,53 +1,169 @@ package tea -import ( - "fmt" - "io" +// WindowSizeMsg is used to report the terminal size. It's sent to Update once +// initially and then on every terminal resize. Note that Windows does not +// have support for reporting when resizes occur as it does not support the +// SIGWINCH signal. +type WindowSizeMsg struct { + Width int + Height int +} + +// ClearScreen is a special command that tells the program to clear the screen +// before the next update. This can be used to move the cursor to the top left +// of the screen and clear visual clutter when the alt screen is not in use. +// +// Note that it should never be necessary to call ClearScreen() for regular +// redraws. +func ClearScreen() Msg { + return clearScreenMsg{} +} + +// clearScreenMsg is an internal message that signals to clear the screen. +// You can send a clearScreenMsg with ClearScreen. +type clearScreenMsg struct{} + +// EnterAltScreen is a special command that tells the Bubble Tea program to +// enter the alternate screen buffer. +// +// Because commands run asynchronously, this command should not be used in your +// model's Init function. To initialize your program with the altscreen enabled +// use the WithAltScreen ProgramOption instead. +func EnterAltScreen() Msg { + return enterAltScreenMsg{} +} + +// enterAltScreenMsg in an internal message signals that the program should +// enter alternate screen buffer. You can send a enterAltScreenMsg with +// EnterAltScreen. +type enterAltScreenMsg struct{} - te "github.com/muesli/termenv" -) +// ExitAltScreen is a special command that tells the Bubble Tea program to exit +// the alternate screen buffer. This command should be used to exit the +// alternate screen buffer while the program is running. +// +// Note that the alternate screen buffer will be automatically exited when the +// program quits. +func ExitAltScreen() Msg { + return exitAltScreenMsg{} +} + +// exitAltScreenMsg in an internal message signals that the program should exit +// alternate screen buffer. You can send a exitAltScreenMsg with ExitAltScreen. +type exitAltScreenMsg struct{} -func hideCursor(w io.Writer) { - fmt.Fprintf(w, te.CSI+te.HideCursorSeq) +// EnableMouseCellMotion is a special command that enables mouse click, +// release, and wheel events. Mouse movement events are also captured if +// a mouse button is pressed (i.e., drag events). +// +// Because commands run asynchronously, this command should not be used in your +// model's Init function. Use the WithMouseCellMotion ProgramOption instead. +func EnableMouseCellMotion() Msg { + return enableMouseCellMotionMsg{} } -func showCursor(w io.Writer) { - fmt.Fprintf(w, te.CSI+te.ShowCursorSeq) +// enableMouseCellMotionMsg is a special command that signals to start +// listening for "cell motion" type mouse events (ESC[?1002l). To send an +// enableMouseCellMotionMsg, use the EnableMouseCellMotion command. +type enableMouseCellMotionMsg struct{} + +// EnableMouseAllMotion is a special command that enables mouse click, release, +// wheel, and motion events, which are delivered regardless of whether a mouse +// button is pressed, effectively enabling support for hover interactions. +// +// Many modern terminals support this, but not all. If in doubt, use +// EnableMouseCellMotion instead. +// +// Because commands run asynchronously, this command should not be used in your +// model's Init function. Use the WithMouseAllMotion ProgramOption instead. +func EnableMouseAllMotion() Msg { + return enableMouseAllMotionMsg{} } -func clearLine(w io.Writer) { - fmt.Fprintf(w, te.CSI+te.EraseLineSeq, 2) +// enableMouseAllMotionMsg is a special command that signals to start listening +// for "all motion" type mouse events (ESC[?1003l). To send an +// enableMouseAllMotionMsg, use the EnableMouseAllMotion command. +type enableMouseAllMotionMsg struct{} + +// DisableMouse is a special command that stops listening for mouse events. +func DisableMouse() Msg { + return disableMouseMsg{} } -func cursorUp(w io.Writer) { - fmt.Fprintf(w, te.CSI+te.CursorUpSeq, 1) +// disableMouseMsg is an internal message that signals to stop listening +// for mouse events. To send a disableMouseMsg, use the DisableMouse command. +type disableMouseMsg struct{} + +// HideCursor is a special command for manually instructing Bubble Tea to hide +// the cursor. In some rare cases, certain operations will cause the terminal +// to show the cursor, which is normally hidden for the duration of a Bubble +// Tea program's lifetime. You will most likely not need to use this command. +func HideCursor() Msg { + return hideCursorMsg{} } -func cursorDown(w io.Writer) { - fmt.Fprintf(w, te.CSI+te.CursorDownSeq, 1) +// hideCursorMsg is an internal command used to hide the cursor. You can send +// this message with HideCursor. +type hideCursorMsg struct{} + +// ShowCursor is a special command for manually instructing Bubble Tea to show +// the cursor. +func ShowCursor() Msg { + return showCursorMsg{} } -func insertLine(w io.Writer, numLines int) { - fmt.Fprintf(w, te.CSI+"%dL", numLines) +// showCursorMsg is an internal command used to show the cursor. You can send +// this message with ShowCursor. +type showCursorMsg struct{} + +// EnterAltScreen enters the alternate screen buffer, which consumes the entire +// terminal window. ExitAltScreen will return the terminal to its former state. +// +// Deprecated: Use the WithAltScreen ProgramOption instead. +func (p *Program) EnterAltScreen() { + if p.renderer != nil { + p.renderer.enterAltScreen() + } } -func moveCursor(w io.Writer, row, col int) { - fmt.Fprintf(w, te.CSI+te.CursorPositionSeq, row, col) +// ExitAltScreen exits the alternate screen buffer. +// +// Deprecated: The altscreen will exited automatically when the program exits. +func (p *Program) ExitAltScreen() { + if p.renderer != nil { + p.renderer.exitAltScreen() + } } -func changeScrollingRegion(w io.Writer, top, bottom int) { - fmt.Fprintf(w, te.CSI+te.ChangeScrollingRegionSeq, top, bottom) +// EnableMouseCellMotion enables mouse click, release, wheel and motion events +// if a mouse button is pressed (i.e., drag events). +// +// Deprecated: Use the WithMouseCellMotion ProgramOption instead. +func (p *Program) EnableMouseCellMotion() { + p.renderer.enableMouseCellMotion() } -func cursorBack(w io.Writer, n int) { - fmt.Fprintf(w, te.CSI+te.CursorBackSeq, n) +// DisableMouseCellMotion disables Mouse Cell Motion tracking. This will be +// called automatically when exiting a Bubble Tea program. +// +// Deprecated: The mouse will automatically be disabled when the program exits. +func (p *Program) DisableMouseCellMotion() { + p.renderer.disableMouseCellMotion() } -func enterAltScreen(w io.Writer) { - fmt.Fprintf(w, te.CSI+te.AltScreenSeq) - moveCursor(w, 0, 0) +// EnableMouseAllMotion enables mouse click, release, wheel and motion events, +// regardless of whether a mouse button is pressed. Many modern terminals +// support this, but not all. +// +// Deprecated: Use the WithMouseAllMotion ProgramOption instead. +func (p *Program) EnableMouseAllMotion() { + p.renderer.enableMouseAllMotion() } -func exitAltScreen(w io.Writer) { - fmt.Fprintf(w, te.CSI+te.ExitAltScreenSeq) +// DisableMouseAllMotion disables All Motion mouse tracking. This will be +// called automatically when exiting a Bubble Tea program. +// +// Deprecated: The mouse will automatically be disabled when the program exits. +func (p *Program) DisableMouseAllMotion() { + p.renderer.disableMouseAllMotion() } diff --git a/vendor/github.com/charmbracelet/bubbletea/signals_unix.go b/vendor/github.com/charmbracelet/bubbletea/signals_unix.go index c7ca029451b2e..826f58b98ff27 100644 --- a/vendor/github.com/charmbracelet/bubbletea/signals_unix.go +++ b/vendor/github.com/charmbracelet/bubbletea/signals_unix.go @@ -1,21 +1,18 @@ -//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || aix +// +build darwin dragonfly freebsd linux netbsd openbsd solaris aix package tea import ( - "context" "os" "os/signal" "syscall" - - "golang.org/x/term" ) // listenForResize sends messages (or errors) when the terminal resizes. // Argument output should be the file descriptor for the terminal; usually // os.Stdout. -func listenForResize(ctx context.Context, output *os.File, msgs chan Msg, errs chan error, done chan struct{}) { +func (p *Program) listenForResize(done chan struct{}) { sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGWINCH) @@ -26,20 +23,11 @@ func listenForResize(ctx context.Context, output *os.File, msgs chan Msg, errs c for { select { - case <-ctx.Done(): + case <-p.ctx.Done(): return case <-sig: } - w, h, err := term.GetSize(int(output.Fd())) - if err != nil { - errs <- err - } - - select { - case <-ctx.Done(): - return - case msgs <- WindowSizeMsg{w, h}: - } + p.checkResize() } } diff --git a/vendor/github.com/charmbracelet/bubbletea/signals_windows.go b/vendor/github.com/charmbracelet/bubbletea/signals_windows.go index 71da4f00e80d1..2fc6f8ae793fc 100644 --- a/vendor/github.com/charmbracelet/bubbletea/signals_windows.go +++ b/vendor/github.com/charmbracelet/bubbletea/signals_windows.go @@ -3,14 +3,8 @@ package tea -import ( - "context" - "os" -) - // listenForResize is not available on windows because windows does not // implement syscall.SIGWINCH. -func listenForResize(ctx context.Context, output *os.File, msgs chan Msg, - errs chan error, done chan struct{}) { +func (p *Program) listenForResize(done chan struct{}) { close(done) } diff --git a/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go b/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go index 12087bb98706b..0ab9473d80ac7 100644 --- a/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go +++ b/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go @@ -2,6 +2,7 @@ package tea import ( "bytes" + "fmt" "io" "strings" "sync" @@ -9,6 +10,7 @@ import ( "github.com/muesli/ansi/compressor" "github.com/muesli/reflow/truncate" + "github.com/muesli/termenv" ) const ( @@ -23,16 +25,21 @@ const ( // In cases where very high performance is needed the renderer can be told // to exclude ranges of lines, allowing them to be written to directly. type standardRenderer struct { - out io.Writer - buf bytes.Buffer - framerate time.Duration - ticker *time.Ticker - mtx *sync.Mutex - done chan struct{} - lastRender string - linesRendered int - useANSICompressor bool - once sync.Once + mtx *sync.Mutex + out *termenv.Output + + buf bytes.Buffer + queuedMessageLines []string + framerate time.Duration + ticker *time.Ticker + done chan struct{} + lastRender string + linesRendered int + useANSICompressor bool + once sync.Once + + // cursor visibility state + cursorHidden bool // essentially whether or not we're using the full size of the terminal altScreenActive bool @@ -47,15 +54,17 @@ type standardRenderer struct { // newRenderer creates a new renderer. Normally you'll want to initialize it // with os.Stdout as the first argument. -func newRenderer(out io.Writer, mtx *sync.Mutex, useANSICompressor bool) renderer { +func newRenderer(out *termenv.Output, useANSICompressor bool) renderer { r := &standardRenderer{ - out: out, - mtx: mtx, - framerate: defaultFramerate, - useANSICompressor: useANSICompressor, + out: out, + mtx: &sync.Mutex{}, + done: make(chan struct{}), + framerate: defaultFramerate, + useANSICompressor: useANSICompressor, + queuedMessageLines: []string{}, } if r.useANSICompressor { - r.out = &compressor.Writer{Forward: out} + r.out = termenv.NewOutput(&compressor.Writer{Forward: out}) } return r } @@ -64,21 +73,36 @@ func newRenderer(out io.Writer, mtx *sync.Mutex, useANSICompressor bool) rendere func (r *standardRenderer) start() { if r.ticker == nil { r.ticker = time.NewTicker(r.framerate) + } else { + // If the ticker already exists, it has been stopped and we need to + // reset it. + r.ticker.Reset(r.framerate) } - r.done = make(chan struct{}) + + // Since the renderer can be restarted after a stop, we need to reset + // the done channel and its corresponding sync.Once. + r.once = sync.Once{} + go r.listen() } // stop permanently halts the renderer, rendering the final frame. func (r *standardRenderer) stop() { - r.flush() - clearLine(r.out) + // Stop the renderer before acquiring the mutex to avoid a deadlock. r.once.Do(func() { - close(r.done) + r.done <- struct{}{} }) + // flush locks the mutex + r.flush() + + r.mtx.Lock() + defer r.mtx.Unlock() + + r.out.ClearLine() + if r.useANSICompressor { - if w, ok := r.out.(io.WriteCloser); ok { + if w, ok := r.out.TTY().(io.WriteCloser); ok { _ = w.Close() } } @@ -86,24 +110,27 @@ func (r *standardRenderer) stop() { // kill halts the renderer. The final frame will not be rendered. func (r *standardRenderer) kill() { - clearLine(r.out) + // Stop the renderer before acquiring the mutex to avoid a deadlock. r.once.Do(func() { - close(r.done) + r.done <- struct{}{} }) + + r.mtx.Lock() + defer r.mtx.Unlock() + + r.out.ClearLine() } // listen waits for ticks on the ticker, or a signal to stop the renderer. func (r *standardRenderer) listen() { for { select { - case <-r.ticker.C: - if r.ticker != nil { - r.flush() - } case <-r.done: r.ticker.Stop() - r.ticker = nil return + + case <-r.ticker.C: + r.flush() } } } @@ -119,11 +146,29 @@ func (r *standardRenderer) flush() { } // Output buffer - out := new(bytes.Buffer) + buf := &bytes.Buffer{} + out := termenv.NewOutput(buf) newLines := strings.Split(r.buf.String(), "\n") + + // If we know the output's height, we can use it to determine how many + // lines we can render. We drop lines from the top of the render buffer if + // necessary, as we can't navigate the cursor into the terminal's scrollback + // buffer. + if r.height > 0 && len(newLines) > r.height { + newLines = newLines[len(newLines)-r.height:] + } + + numLinesThisFlush := len(newLines) oldLines := strings.Split(r.lastRender, "\n") skipLines := make(map[int]struct{}) + flushQueuedMessages := len(r.queuedMessageLines) > 0 && !r.altScreenActive + + // Add any queued messages to this render + if flushQueuedMessages { + newLines = append(r.queuedMessageLines, newLines...) + r.queuedMessageLines = []string{} + } // Clear any lines we painted in the last render. if r.linesRendered > 0 { @@ -134,10 +179,10 @@ func (r *standardRenderer) flush() { if (len(newLines) <= len(oldLines)) && (len(newLines) > i && len(oldLines) > i) && (newLines[i] == oldLines[i]) { skipLines[i] = struct{}{} } else if _, exists := r.ignoreLines[i]; !exists { - clearLine(out) + out.ClearLine() } - cursorUp(out) + out.CursorUp(1) } if _, exists := r.ignoreLines[0]; !exists { @@ -150,8 +195,8 @@ func (r *standardRenderer) flush() { // standard (whereas others are proprietary to, say, VT100/VT52). // If cursor previous line (ESC[ + + F) were better supported // we could use that above to eliminate this step. - cursorBack(out, r.width) - clearLine(out) + out.CursorBack(r.width) + out.ClearLine() } } @@ -163,14 +208,12 @@ func (r *standardRenderer) flush() { } } - r.linesRendered = 0 - // Paint new lines for i := 0; i < len(newLines); i++ { - if _, skip := skipLines[r.linesRendered]; skip { + if _, skip := skipLines[i]; skip { // Unless this is the last line, move the cursor down. if i < len(newLines)-1 { - cursorDown(out) + out.CursorDown(1) } } else { line := newLines[i] @@ -186,14 +229,14 @@ func (r *standardRenderer) flush() { line = truncate.String(line, uint(r.width)) } - _, _ = io.WriteString(out, line) + _, _ = out.WriteString(line) if i < len(newLines)-1 { - _, _ = io.WriteString(out, "\r\n") + _, _ = out.WriteString("\r\n") } } - r.linesRendered++ } + r.linesRendered = numLinesThisFlush // Make sure the cursor is at the start of the last line to keep rendering // behavior consistent. @@ -201,12 +244,12 @@ func (r *standardRenderer) flush() { // This case fixes a bug in macOS terminal. In other terminals the // other case seems to do the job regardless of whether or not we're // using the full terminal window. - moveCursor(out, r.linesRendered, 0) + out.MoveCursor(r.linesRendered, 0) } else { - cursorBack(out, r.width) + out.CursorBack(r.width) } - _, _ = r.out.Write(out.Bytes()) + _, _ = r.out.Write(buf.Bytes()) r.lastRender = r.buf.String() r.buf.Reset() } @@ -233,15 +276,122 @@ func (r *standardRenderer) repaint() { r.lastRender = "" } +func (r *standardRenderer) clearScreen() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.out.ClearScreen() + r.out.MoveCursor(1, 1) + + r.repaint() +} + func (r *standardRenderer) altScreen() bool { + r.mtx.Lock() + defer r.mtx.Unlock() + return r.altScreenActive } -func (r *standardRenderer) setAltScreen(v bool) { - r.altScreenActive = v +func (r *standardRenderer) enterAltScreen() { + r.mtx.Lock() + defer r.mtx.Unlock() + + if r.altScreenActive { + return + } + + r.altScreenActive = true + r.out.AltScreen() + + // Ensure that the terminal is cleared, even when it doesn't support + // alt screen (or alt screen support is disabled, like GNU screen by + // default). + // + // Note: we can't use r.clearScreen() here because the mutex is already + // locked. + r.out.ClearScreen() + r.out.MoveCursor(1, 1) + + // cmd.exe and other terminals keep separate cursor states for the AltScreen + // and the main buffer. We have to explicitly reset the cursor visibility + // whenever we enter AltScreen. + if r.cursorHidden { + r.out.HideCursor() + } else { + r.out.ShowCursor() + } + r.repaint() } +func (r *standardRenderer) exitAltScreen() { + r.mtx.Lock() + defer r.mtx.Unlock() + + if !r.altScreenActive { + return + } + + r.altScreenActive = false + r.out.ExitAltScreen() + + // cmd.exe and other terminals keep separate cursor states for the AltScreen + // and the main buffer. We have to explicitly reset the cursor visibility + // whenever we exit AltScreen. + if r.cursorHidden { + r.out.HideCursor() + } else { + r.out.ShowCursor() + } + + r.repaint() +} + +func (r *standardRenderer) showCursor() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.cursorHidden = false + r.out.ShowCursor() +} + +func (r *standardRenderer) hideCursor() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.cursorHidden = true + r.out.HideCursor() +} + +func (r *standardRenderer) enableMouseCellMotion() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.out.EnableMouseCellMotion() +} + +func (r *standardRenderer) disableMouseCellMotion() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.out.DisableMouseCellMotion() +} + +func (r *standardRenderer) enableMouseAllMotion() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.out.EnableMouseAllMotion() +} + +func (r *standardRenderer) disableMouseAllMotion() { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.out.DisableMouseAllMotion() +} + // setIgnoredLines specifies lines not to be touched by the standard Bubble Tea // renderer. func (r *standardRenderer) setIgnoredLines(from int, to int) { @@ -261,15 +411,17 @@ func (r *standardRenderer) setIgnoredLines(from int, to int) { // Erase ignored lines if r.linesRendered > 0 { - out := new(bytes.Buffer) + buf := &bytes.Buffer{} + out := termenv.NewOutput(buf) + for i := r.linesRendered - 1; i >= 0; i-- { if _, exists := r.ignoreLines[i]; exists { - clearLine(out) + out.ClearLine() } - cursorUp(out) + out.CursorUp(1) } - moveCursor(out, r.linesRendered, 0) // put cursor back - _, _ = r.out.Write(out.Bytes()) + out.MoveCursor(r.linesRendered, 0) // put cursor back + _, _ = r.out.Write(buf.Bytes()) } } @@ -302,18 +454,19 @@ func (r *standardRenderer) insertTop(lines []string, topBoundary, bottomBoundary r.mtx.Lock() defer r.mtx.Unlock() - b := new(bytes.Buffer) + buf := &bytes.Buffer{} + out := termenv.NewOutput(buf) - changeScrollingRegion(b, topBoundary, bottomBoundary) - moveCursor(b, topBoundary, 0) - insertLine(b, len(lines)) - _, _ = io.WriteString(b, strings.Join(lines, "\r\n")) - changeScrollingRegion(b, 0, r.height) + out.ChangeScrollingRegion(topBoundary, bottomBoundary) + out.MoveCursor(topBoundary, 0) + out.InsertLines(len(lines)) + _, _ = out.WriteString(strings.Join(lines, "\r\n")) + out.ChangeScrollingRegion(0, r.height) // Move cursor back to where the main rendering routine expects it to be - moveCursor(b, r.linesRendered, 0) + out.MoveCursor(r.linesRendered, 0) - _, _ = r.out.Write(b.Bytes()) + _, _ = r.out.Write(buf.Bytes()) } // insertBottom effectively scrolls down. It inserts lines at the bottom of @@ -329,17 +482,18 @@ func (r *standardRenderer) insertBottom(lines []string, topBoundary, bottomBound r.mtx.Lock() defer r.mtx.Unlock() - b := new(bytes.Buffer) + buf := &bytes.Buffer{} + out := termenv.NewOutput(buf) - changeScrollingRegion(b, topBoundary, bottomBoundary) - moveCursor(b, bottomBoundary, 0) - _, _ = io.WriteString(b, "\r\n"+strings.Join(lines, "\r\n")) - changeScrollingRegion(b, 0, r.height) + out.ChangeScrollingRegion(topBoundary, bottomBoundary) + out.MoveCursor(bottomBoundary, 0) + _, _ = out.WriteString("\r\n" + strings.Join(lines, "\r\n")) + out.ChangeScrollingRegion(0, r.height) // Move cursor back to where the main rendering routine expects it to be - moveCursor(b, r.linesRendered, 0) + out.MoveCursor(r.linesRendered, 0) - _, _ = r.out.Write(b.Bytes()) + _, _ = r.out.Write(buf.Bytes()) } // handleMessages handles internal messages for the renderer. @@ -348,12 +502,15 @@ func (r *standardRenderer) handleMessages(msg Msg) { case repaintMsg: // Force a repaint by clearing the render cache as we slide into a // render. + r.mtx.Lock() r.repaint() + r.mtx.Unlock() case WindowSizeMsg: r.mtx.Lock() r.width = msg.Width r.height = msg.Height + r.repaint() r.mtx.Unlock() case clearScrollAreaMsg: @@ -362,7 +519,7 @@ func (r *standardRenderer) handleMessages(msg Msg) { // Force a repaint on the area where the scrollable stuff was in this // update cycle r.mtx.Lock() - r.lastRender = "" + r.repaint() r.mtx.Unlock() case syncScrollAreaMsg: @@ -373,7 +530,7 @@ func (r *standardRenderer) handleMessages(msg Msg) { // Force non-scrolling stuff to repaint in this update cycle r.mtx.Lock() - r.lastRender = "" + r.repaint() r.mtx.Unlock() case scrollUpMsg: @@ -381,6 +538,15 @@ func (r *standardRenderer) handleMessages(msg Msg) { case scrollDownMsg: r.insertBottom(msg.lines, msg.topBoundary, msg.bottomBoundary) + + case printLineMessage: + if !r.altScreenActive { + lines := strings.Split(msg.messageBody, "\n") + r.mtx.Lock() + r.queuedMessageLines = append(r.queuedMessageLines, lines...) + r.repaint() + r.mtx.Unlock() + } } } @@ -458,3 +624,38 @@ func ScrollDown(newLines []string, topBoundary, bottomBoundary int) Cmd { } } } + +type printLineMessage struct { + messageBody string +} + +// Println prints above the Program. This output is unmanaged by the program and +// will persist across renders by the Program. +// +// Unlike fmt.Println (but similar to log.Println) the message will be print on +// its own line. +// +// If the altscreen is active no output will be printed. +func Println(args ...interface{}) Cmd { + return func() Msg { + return printLineMessage{ + messageBody: fmt.Sprint(args...), + } + } +} + +// Printf prints above the Program. It takes a format template followed by +// values similar to fmt.Printf. This output is unmanaged by the program and +// will persist across renders by the Program. +// +// Unlike fmt.Printf (but similar to log.Printf) the message will be print on +// its own line. +// +// If the altscreen is active no output will be printed. +func Printf(template string, args ...interface{}) Cmd { + return func() Msg { + return printLineMessage{ + messageBody: fmt.Sprintf(template, args...), + } + } +} diff --git a/vendor/github.com/charmbracelet/bubbletea/tea.go b/vendor/github.com/charmbracelet/bubbletea/tea.go index 093e5f80bad27..2e024ed604523 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tea.go +++ b/vendor/github.com/charmbracelet/bubbletea/tea.go @@ -11,6 +11,7 @@ package tea import ( "context" + "errors" "fmt" "io" "os" @@ -18,15 +19,17 @@ import ( "runtime/debug" "sync" "syscall" - "time" "github.com/containerd/console" isatty "github.com/mattn/go-isatty" "github.com/muesli/cancelreader" - te "github.com/muesli/termenv" - "golang.org/x/term" + "github.com/muesli/termenv" + "golang.org/x/sync/errgroup" ) +// ErrProgramKilled is returned by [Program.Run] when the program got killed. +var ErrProgramKilled = errors.New("program was killed") + // Msg contain data from the result of a IO operation. Msgs trigger the update // function and, henceforth, the UI. type Msg interface{} @@ -55,6 +58,26 @@ type Model interface { // update function. type Cmd func() Msg +type handlers []chan struct{} + +type inputType int + +const ( + defaultInput inputType = iota + ttyInput + customInput +) + +// String implements the stringer interface for [inputType]. It is inteded to +// be used in testing. +func (i inputType) String() string { + return [...]string{ + "default input", + "tty input", + "custom input", + }[i] +} + // Options to customize the program during its initialization. These are // generally set with ProgramOptions. // @@ -69,9 +92,14 @@ const ( withAltScreen startupOptions = 1 << iota withMouseCellMotion withMouseAllMotion - withInputTTY - withCustomInput withANSICompressor + withoutSignalHandler + + // Catching panics is incredibly useful for restoring the terminal to a + // usable state after a panic occurs. When this is set, Bubble Tea will + // recover from panics, print the stack trace, and disable raw mode. This + // feature is on by default. + withoutCatchPanics ) // Program is a terminal user interface. @@ -82,32 +110,29 @@ type Program struct { // treated as bits. These options can be set via various ProgramOptions. startupOptions startupOptions - ctx context.Context - mtx *sync.Mutex + inputType inputType - msgs chan Msg - errs chan error - readLoopDone chan struct{} + ctx context.Context + cancel context.CancelFunc - output io.Writer // where to send output. this will usually be os.Stdout. - input io.Reader // this will usually be os.Stdin. - cancelReader cancelreader.CancelReader + msgs chan Msg + errs chan error + finished chan struct{} - renderer renderer - altScreenActive bool - altScreenWasActive bool // was the altscreen active before releasing the terminal? + // where to send output, this will usually be os.Stdout. + output *termenv.Output + restoreOutput func() error + renderer renderer - // CatchPanics is incredibly useful for restoring the terminal to a usable - // state after a panic occurs. When this is set, Bubble Tea will recover - // from panics, print the stack trace, and disable raw mode. This feature - // is on by default. - CatchPanics bool - - ignoreSignals bool - - killc chan bool + // where to read inputs from, this will usually be os.Stdin. + input io.Reader + cancelReader cancelreader.CancelReader + readLoopDone chan struct{} + console console.Console - console console.Console + // was the altscreen active before releasing the terminal? + altScreenWasActive bool + ignoreSignals bool // Stores the original reference to stdin for cases where input is not a // TTY on windows and we've automatically opened CONIN$ to receive input. @@ -117,148 +142,24 @@ type Program struct { // as this value only comes into play on Windows, hence the ignore comment // below. windowsStdin *os.File //nolint:golint,structcheck,unused -} -// Batch performs a bunch of commands concurrently with no ordering guarantees -// about the results. Use a Batch to return several commands. -// -// Example: -// -// func (m model) Init() Cmd { -// return tea.Batch(someCommand, someOtherCommand) -// } -// -func Batch(cmds ...Cmd) Cmd { - var validCmds []Cmd - for _, c := range cmds { - if c == nil { - continue - } - validCmds = append(validCmds, c) - } - if len(validCmds) == 0 { - return nil - } - return func() Msg { - return batchMsg(validCmds) - } + filter func(Model, Msg) Msg } -// batchMsg is the internal message used to perform a bunch of commands. You -// can send a batchMsg with Batch. -type batchMsg []Cmd - // Quit is a special command that tells the Bubble Tea program to exit. func Quit() Msg { - return quitMsg{} -} - -// quitMsg in an internal message signals that the program should quit. You can -// send a quitMsg with Quit. -type quitMsg struct{} - -// EnterAltScreen is a special command that tells the Bubble Tea program to -// enter the alternate screen buffer. -// -// Because commands run asynchronously, this command should not be used in your -// model's Init function. To initialize your program with the altscreen enabled -// use the WithAltScreen ProgramOption instead. -func EnterAltScreen() Msg { - return enterAltScreenMsg{} -} - -// enterAltScreenMsg in an internal message signals that the program should -// enter alternate screen buffer. You can send a enterAltScreenMsg with -// EnterAltScreen. -type enterAltScreenMsg struct{} - -// ExitAltScreen is a special command that tells the Bubble Tea program to exit -// the alternate screen buffer. This command should be used to exit the -// alternate screen buffer while the program is running. -// -// Note that the alternate screen buffer will be automatically exited when the -// program quits. -func ExitAltScreen() Msg { - return exitAltScreenMsg{} + return QuitMsg{} } -// exitAltScreenMsg in an internal message signals that the program should exit -// alternate screen buffer. You can send a exitAltScreenMsg with ExitAltScreen. -type exitAltScreenMsg struct{} - -// EnableMouseCellMotion is a special command that enables mouse click, -// release, and wheel events. Mouse movement events are also captured if -// a mouse button is pressed (i.e., drag events). -// -// Because commands run asynchronously, this command should not be used in your -// model's Init function. Use the WithMouseCellMotion ProgramOption instead. -func EnableMouseCellMotion() Msg { - return enableMouseCellMotionMsg{} -} - -// enableMouseCellMotionMsg is a special command that signals to start -// listening for "cell motion" type mouse events (ESC[?1002l). To send an -// enableMouseCellMotionMsg, use the EnableMouseCellMotion command. -type enableMouseCellMotionMsg struct{} - -// EnableMouseAllMotion is a special command that enables mouse click, release, -// wheel, and motion events, which are delivered regardless of whether a mouse -// button is pressed, effectively enabling support for hover interactions. -// -// Many modern terminals support this, but not all. If in doubt, use -// EnableMouseCellMotion instead. -// -// Because commands run asynchronously, this command should not be used in your -// model's Init function. Use the WithMouseAllMotion ProgramOption instead. -func EnableMouseAllMotion() Msg { - return enableMouseAllMotionMsg{} -} - -// enableMouseAllMotionMsg is a special command that signals to start listening -// for "all motion" type mouse events (ESC[?1003l). To send an -// enableMouseAllMotionMsg, use the EnableMouseAllMotion command. -type enableMouseAllMotionMsg struct{} - -// DisableMouse is a special command that stops listening for mouse events. -func DisableMouse() Msg { - return disableMouseMsg{} -} - -// disableMouseMsg is an internal message that that signals to stop listening -// for mouse events. To send a disableMouseMsg, use the DisableMouse command. -type disableMouseMsg struct{} - -// WindowSizeMsg is used to report the terminal size. It's sent to Update once -// initially and then on every terminal resize. Note that Windows does not -// have support for reporting when resizes occur as it does not support the -// SIGWINCH signal. -type WindowSizeMsg struct { - Width int - Height int -} - -// HideCursor is a special command for manually instructing Bubble Tea to hide -// the cursor. In some rare cases, certain operations will cause the terminal -// to show the cursor, which is normally hidden for the duration of a Bubble -// Tea program's lifetime. You will most likely not need to use this command. -func HideCursor() Msg { - return hideCursorMsg{} -} - -// hideCursorMsg is an internal command used to hide the cursor. You can send -// this message with HideCursor. -type hideCursorMsg struct{} +// QuitMsg signals that the program should quit. You can send a QuitMsg with +// Quit. +type QuitMsg struct{} // NewProgram creates a new Program. func NewProgram(model Model, opts ...ProgramOption) *Program { p := &Program{ - mtx: &sync.Mutex{}, initialModel: model, - output: os.Stdout, - input: os.Stdin, msgs: make(chan Msg), - CatchPanics: true, - killc: make(chan bool, 1), } // Apply all options to the program. @@ -266,195 +167,93 @@ func NewProgram(model Model, opts ...ProgramOption) *Program { opt(p) } - return p -} - -// StartReturningModel initializes the program. Returns the final model. -func (p *Program) StartReturningModel() (Model, error) { - cmds := make(chan Cmd) - p.errs = make(chan error) - - // Channels for managing goroutine lifecycles. - var ( - sigintLoopDone = make(chan struct{}) - cmdLoopDone = make(chan struct{}) - resizeLoopDone = make(chan struct{}) - initSignalDone = make(chan struct{}) - - waitForGoroutines = func(withReadLoop bool) { - if withReadLoop { - select { - case <-p.readLoopDone: - case <-time.After(500 * time.Millisecond): - // The read loop hangs, which means the input - // cancelReader's cancel function has returned true even - // though it was not able to cancel the read. - } - } - <-cmdLoopDone - <-resizeLoopDone - <-sigintLoopDone - <-initSignalDone - } - ) - - var cancelContext context.CancelFunc - p.ctx, cancelContext = context.WithCancel(context.Background()) - defer cancelContext() - - switch { - case p.startupOptions.has(withInputTTY): - // Open a new TTY, by request - f, err := openInputTTY() - if err != nil { - return p.initialModel, err - } - - defer f.Close() // nolint:errcheck - - p.input = f + // A context can be provided with a ProgramOption, but if none was provided + // we'll use the default background context. + if p.ctx == nil { + p.ctx = context.Background() + } + // Initialize context and teardown channel. + p.ctx, p.cancel = context.WithCancel(p.ctx) - case !p.startupOptions.has(withCustomInput): - // If the user hasn't set a custom input, and input's not a terminal, - // open a TTY so we can capture input as normal. This will allow things - // to "just work" in cases where data was piped or redirected into this - // application. - f, isFile := p.input.(*os.File) - if !isFile { - break - } + // if no output was set, set it to stdout + if p.output == nil { + p.output = termenv.DefaultOutput() - if isatty.IsTerminal(f.Fd()) { - break - } + // cache detected color values + termenv.WithColorCache(true)(p.output) + } - f, err := openInputTTY() - if err != nil { - return p.initialModel, err - } + p.restoreOutput, _ = termenv.EnableVirtualTerminalProcessing(p.output) - defer f.Close() // nolint:errcheck + return p +} - p.input = f - } +func (p *Program) handleSignals() chan struct{} { + ch := make(chan struct{}) - // Listen for SIGINT. Note that in most cases ^C will not send an - // interrupt because the terminal will be in raw mode and thus capture - // that keystroke and send it along to Program.Update. If input is not a - // TTY, however, ^C will be caught here. + // Listen for SIGINT and SIGTERM. + // + // In most cases ^C will not send an interrupt because the terminal will be + // in raw mode and ^C will be captured as a keystroke and sent along to + // Program.Update as a KeyMsg. When input is not a TTY, however, ^C will be + // caught here. + // + // SIGTERM is sent by unix utilities (like kill) to terminate a process. go func() { sig := make(chan os.Signal, 1) - signal.Notify(sig, syscall.SIGINT) + signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) defer func() { signal.Stop(sig) - close(sigintLoopDone) + close(ch) }() for { select { case <-p.ctx.Done(): return + case <-sig: if !p.ignoreSignals { - p.msgs <- quitMsg{} + p.msgs <- QuitMsg{} return } } } }() - if p.CatchPanics { - defer func() { - if r := recover(); r != nil { - p.shutdown(true) - fmt.Printf("Caught panic:\n\n%s\n\nRestoring terminal...\n\n", r) - debug.PrintStack() - return - } - }() - } - - // Check if output is a TTY before entering raw mode, hiding the cursor and - // so on. - if err := p.initTerminal(); err != nil { - return p.initialModel, err - } - - // If no renderer is set use the standard one. - if p.renderer == nil { - p.renderer = newRenderer(p.output, p.mtx, p.startupOptions.has(withANSICompressor)) - } - - // Honor program startup options. - if p.startupOptions&withAltScreen != 0 { - p.EnterAltScreen() - } - if p.startupOptions&withMouseCellMotion != 0 { - p.EnableMouseCellMotion() - } else if p.startupOptions&withMouseAllMotion != 0 { - p.EnableMouseAllMotion() - } - - // Initialize the program. - model := p.initialModel - if initCmd := model.Init(); initCmd != nil { - go func() { - defer close(initSignalDone) - select { - case cmds <- initCmd: - case <-p.ctx.Done(): - } - }() - } else { - close(initSignalDone) - } + return ch +} - // Start the renderer. - p.renderer.start() - p.renderer.setAltScreen(p.altScreenActive) +// handleResize handles terminal resize events. +func (p *Program) handleResize() chan struct{} { + ch := make(chan struct{}) - // Render the initial view. - p.renderer.write(model.View()) - - // Subscribe to user input. - if p.input != nil { - if err := p.initCancelReader(); err != nil { - return model, err - } - } else { - defer close(p.readLoopDone) - } - defer p.cancelReader.Close() // nolint:errcheck - - if f, ok := p.output.(*os.File); ok && isatty.IsTerminal(f.Fd()) { + if f, ok := p.output.TTY().(*os.File); ok && isatty.IsTerminal(f.Fd()) { // Get the initial terminal size and send it to the program. - go func() { - w, h, err := term.GetSize(int(f.Fd())) - if err != nil { - p.errs <- err - } - - select { - case <-p.ctx.Done(): - case p.msgs <- WindowSizeMsg{w, h}: - } - }() + go p.checkResize() // Listen for window resizes. - go listenForResize(p.ctx, f, p.msgs, p.errs, resizeLoopDone) + go p.listenForResize(ch) } else { - close(resizeLoopDone) + close(ch) } - // Process commands. + return ch +} + +// handleCommands runs commands in a goroutine and sends the result to the +// program's message channel. +func (p *Program) handleCommands(cmds chan Cmd) chan struct{} { + ch := make(chan struct{}) + go func() { - defer close(cmdLoopDone) + defer close(ch) for { select { case <-p.ctx.Done(): - return + case cmd := <-cmds: if cmd == nil { continue @@ -466,67 +265,103 @@ func (p *Program) StartReturningModel() (Model, error) { // possible to cancel them so we'll have to leak the goroutine // until Cmd returns. go func() { - select { - case p.msgs <- cmd(): - case <-p.ctx.Done(): - } + msg := cmd() // this can be long. + p.Send(msg) }() } } }() - // Handle updates and draw. + return ch +} + +// eventLoop is the central message loop. It receives and handles the default +// Bubble Tea messages, update the model and triggers redraws. +func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) { for { select { - case <-p.killc: - return nil, nil + case <-p.ctx.Done(): + return model, nil + case err := <-p.errs: - cancelContext() - waitForGoroutines(p.cancelReader.Cancel()) - p.shutdown(false) return model, err case msg := <-p.msgs: + // Filter messages. + if p.filter != nil { + msg = p.filter(model, msg) + } + if msg == nil { + continue + } // Handle special internal messages. switch msg := msg.(type) { - case quitMsg: - cancelContext() - waitForGoroutines(p.cancelReader.Cancel()) - p.shutdown(false) + case QuitMsg: return model, nil - case batchMsg: - for _, cmd := range msg { - cmds <- cmd - } - continue - - case WindowSizeMsg: - p.renderer.repaint() + case clearScreenMsg: + p.renderer.clearScreen() case enterAltScreenMsg: - p.EnterAltScreen() + p.renderer.enterAltScreen() case exitAltScreenMsg: - p.ExitAltScreen() + p.renderer.exitAltScreen() case enableMouseCellMotionMsg: - p.EnableMouseCellMotion() + p.renderer.enableMouseCellMotion() case enableMouseAllMotionMsg: - p.EnableMouseAllMotion() + p.renderer.enableMouseAllMotion() case disableMouseMsg: - p.DisableMouseCellMotion() - p.DisableMouseAllMotion() + p.renderer.disableMouseCellMotion() + p.renderer.disableMouseAllMotion() + + case showCursorMsg: + p.renderer.showCursor() case hideCursorMsg: - hideCursor(p.output) + p.renderer.hideCursor() case execMsg: // NB: this blocks. p.exec(msg.cmd, msg.fn) + + case BatchMsg: + for _, cmd := range msg { + cmds <- cmd + } + continue + + case sequenceMsg: + go func() { + // Execute commands one at a time, in order. + for _, cmd := range msg { + if cmd == nil { + continue + } + + msg := cmd() + if batchMsg, ok := msg.(BatchMsg); ok { + g, _ := errgroup.WithContext(p.ctx) + for _, cmd := range batchMsg { + cmd := cmd + g.Go(func() error { + p.Send(cmd()) + return nil + }) + } + + //nolint:errcheck + g.Wait() // wait for all commands from batch msg to finish + continue + } + + p.Send(msg) + } + }() } // Process internal messages for the renderer. @@ -542,9 +377,175 @@ func (p *Program) StartReturningModel() (Model, error) { } } -// Start initializes the program. Ignores the final model. +// Run initializes the program and runs its event loops, blocking until it gets +// terminated by either [Program.Quit], [Program.Kill], or its signal handler. +// Returns the final model. +func (p *Program) Run() (Model, error) { + handlers := handlers{} + cmds := make(chan Cmd) + p.errs = make(chan error) + p.finished = make(chan struct{}, 1) + + defer p.cancel() + + switch p.inputType { + case defaultInput: + p.input = os.Stdin + + // The user has not set a custom input, so we need to check whether or + // not standard input is a terminal. If it's not, we open a new TTY for + // input. This will allow things to "just work" in cases where data was + // piped in or redirected to the application. + // + // To disable input entirely pass nil to the [WithInput] program option. + f, isFile := p.input.(*os.File) + if !isFile { + break + } + if isatty.IsTerminal(f.Fd()) { + break + } + + f, err := openInputTTY() + if err != nil { + return p.initialModel, err + } + defer f.Close() //nolint:errcheck + p.input = f + + case ttyInput: + // Open a new TTY, by request + f, err := openInputTTY() + if err != nil { + return p.initialModel, err + } + defer f.Close() //nolint:errcheck + p.input = f + + case customInput: + // (There is nothing extra to do.) + } + + // Handle signals. + if !p.startupOptions.has(withoutSignalHandler) { + handlers.add(p.handleSignals()) + } + + // Recover from panics. + if !p.startupOptions.has(withoutCatchPanics) { + defer func() { + if r := recover(); r != nil { + p.shutdown(true) + fmt.Printf("Caught panic:\n\n%s\n\nRestoring terminal...\n\n", r) + debug.PrintStack() + return + } + }() + } + + // If no renderer is set use the standard one. + if p.renderer == nil { + p.renderer = newRenderer(p.output, p.startupOptions.has(withANSICompressor)) + } + + // Check if output is a TTY before entering raw mode, hiding the cursor and + // so on. + if err := p.initTerminal(); err != nil { + return p.initialModel, err + } + + // Honor program startup options. + if p.startupOptions&withAltScreen != 0 { + p.renderer.enterAltScreen() + } + if p.startupOptions&withMouseCellMotion != 0 { + p.renderer.enableMouseCellMotion() + } else if p.startupOptions&withMouseAllMotion != 0 { + p.renderer.enableMouseAllMotion() + } + + // Initialize the program. + model := p.initialModel + if initCmd := model.Init(); initCmd != nil { + ch := make(chan struct{}) + handlers.add(ch) + + go func() { + defer close(ch) + + select { + case cmds <- initCmd: + case <-p.ctx.Done(): + } + }() + } + + // Start the renderer. + p.renderer.start() + + // Render the initial view. + p.renderer.write(model.View()) + + // Subscribe to user input. + if p.input != nil { + if err := p.initCancelReader(); err != nil { + return model, err + } + } + + // Handle resize events. + handlers.add(p.handleResize()) + + // Process commands. + handlers.add(p.handleCommands(cmds)) + + // Run event loop, handle updates and draw. + model, err := p.eventLoop(model, cmds) + killed := p.ctx.Err() != nil + if killed { + err = ErrProgramKilled + } else { + // Ensure we rendered the final state of the model. + p.renderer.write(model.View()) + } + + // Tear down. + p.cancel() + + // Check if the cancel reader has been setup before waiting and closing. + if p.cancelReader != nil { + // Wait for input loop to finish. + if p.cancelReader.Cancel() { + p.waitForReadLoop() + } + _ = p.cancelReader.Close() + } + + // Wait for all handlers to finish. + handlers.shutdown() + + // Restore terminal state. + p.shutdown(killed) + + return model, err +} + +// StartReturningModel initializes the program and runs its event loops, +// blocking until it gets terminated by either [Program.Quit], [Program.Kill], +// or its signal handler. Returns the final model. +// +// Deprecated: please use [Program.Run] instead. +func (p *Program) StartReturningModel() (Model, error) { + return p.Run() +} + +// Start initializes the program and runs its event loops, blocking until it +// gets terminated by either [Program.Quit], [Program.Kill], or its signal +// handler. +// +// Deprecated: please use [Program.Run] instead. func (p *Program) Start() error { - _, err := p.StartReturningModel() + _, err := p.Run() return err } @@ -552,10 +553,14 @@ func (p *Program) Start() error { // messages to be injected from outside the program for interoperability // purposes. // -// If the program is not running this this will be a no-op, so it's safe to -// send messages if the program is unstarted, or has exited. +// If the program hasn't started yet this will be a blocking operation. +// If the program has already been terminated this will be a no-op, so it's safe +// to send messages after the program has exited. func (p *Program) Send(msg Msg) { - p.msgs <- msg + select { + case <-p.ctx.Done(): + case p.msgs <- msg: + } } // Quit is a convenience function for quitting Bubble Tea programs. Use it @@ -571,9 +576,14 @@ func (p *Program) Quit() { // Kill stops the program immediately and restores the former terminal state. // The final render that you would normally see when quitting will be skipped. +// [program.Run] returns a [ErrProgramKilled] error. func (p *Program) Kill() { - p.killc <- true - p.shutdown(true) + p.cancel() +} + +// Wait waits/blocks until the underlying Program finished shutting down. +func (p *Program) Wait() { + <-p.finished } // shutdown performs operations to free up resources and restore the terminal @@ -586,102 +596,26 @@ func (p *Program) shutdown(kill bool) { p.renderer.stop() } } - p.ExitAltScreen() - p.DisableMouseCellMotion() - p.DisableMouseAllMotion() - _ = p.restoreTerminalState() -} - -// EnterAltScreen enters the alternate screen buffer, which consumes the entire -// terminal window. ExitAltScreen will return the terminal to its former state. -// -// Deprecated. Use the WithAltScreen ProgramOption instead. -func (p *Program) EnterAltScreen() { - p.mtx.Lock() - defer p.mtx.Unlock() - - if p.altScreenActive { - return - } - - enterAltScreen(p.output) - - p.altScreenActive = true - if p.renderer != nil { - p.renderer.setAltScreen(p.altScreenActive) - } -} - -// ExitAltScreen exits the alternate screen buffer. -// -// Deprecated. The altscreen will exited automatically when the program exits. -func (p *Program) ExitAltScreen() { - p.mtx.Lock() - defer p.mtx.Unlock() - - if !p.altScreenActive { - return - } - - exitAltScreen(p.output) - p.altScreenActive = false - if p.renderer != nil { - p.renderer.setAltScreen(p.altScreenActive) + _ = p.restoreTerminalState() + if p.restoreOutput != nil { + _ = p.restoreOutput() } -} - -// EnableMouseCellMotion enables mouse click, release, wheel and motion events -// if a mouse button is pressed (i.e., drag events). -// -// Deprecated. Use the WithMouseCellMotion ProgramOption instead. -func (p *Program) EnableMouseCellMotion() { - p.mtx.Lock() - defer p.mtx.Unlock() - fmt.Fprintf(p.output, te.CSI+te.EnableMouseCellMotionSeq) -} - -// DisableMouseCellMotion disables Mouse Cell Motion tracking. This will be -// called automatically when exiting a Bubble Tea program. -// -// Deprecated. The mouse will automatically be disabled when the program exits. -func (p *Program) DisableMouseCellMotion() { - p.mtx.Lock() - defer p.mtx.Unlock() - fmt.Fprintf(p.output, te.CSI+te.DisableMouseCellMotionSeq) -} - -// EnableMouseAllMotion enables mouse click, release, wheel and motion events, -// regardless of whether a mouse button is pressed. Many modern terminals -// support this, but not all. -// -// Deprecated. Use the WithMouseAllMotion ProgramOption instead. -func (p *Program) EnableMouseAllMotion() { - p.mtx.Lock() - defer p.mtx.Unlock() - fmt.Fprintf(p.output, te.CSI+te.EnableMouseAllMotionSeq) -} - -// DisableMouseAllMotion disables All Motion mouse tracking. This will be -// called automatically when exiting a Bubble Tea program. -// -// Deprecated. The mouse will automatically be disabled when the program exits. -func (p *Program) DisableMouseAllMotion() { - p.mtx.Lock() - defer p.mtx.Unlock() - fmt.Fprintf(p.output, te.CSI+te.DisableMouseAllMotionSeq) + p.finished <- struct{}{} } // ReleaseTerminal restores the original terminal state and cancels the input // reader. You can return control to the Program with RestoreTerminal. func (p *Program) ReleaseTerminal() error { p.ignoreSignals = true - p.cancelInput() - p.altScreenWasActive = p.altScreenActive - if p.altScreenActive { - p.ExitAltScreen() - time.Sleep(time.Millisecond * 10) // give the terminal a moment to catch up + p.cancelReader.Cancel() + p.waitForReadLoop() + + if p.renderer != nil { + p.renderer.stop() } + + p.altScreenWasActive = p.renderer.altScreen() return p.restoreTerminalState() } @@ -694,16 +628,68 @@ func (p *Program) RestoreTerminal() error { if err := p.initTerminal(); err != nil { return err } - if err := p.initCancelReader(); err != nil { return err } if p.altScreenWasActive { - p.EnterAltScreen() + p.renderer.enterAltScreen() + } else { + // entering alt screen already causes a repaint. + go p.Send(repaintMsg{}) + } + if p.renderer != nil { + p.renderer.start() } - go p.Send(repaintMsg{}) + // If the output is a terminal, it may have been resized while another + // process was at the foreground, in which case we may not have received + // SIGWINCH. Detect any size change now and propagate the new size as + // needed. + go p.checkResize() return nil } + +// Println prints above the Program. This output is unmanaged by the program +// and will persist across renders by the Program. +// +// If the altscreen is active no output will be printed. +func (p *Program) Println(args ...interface{}) { + p.msgs <- printLineMessage{ + messageBody: fmt.Sprint(args...), + } +} + +// Printf prints above the Program. It takes a format template followed by +// values similar to fmt.Printf. This output is unmanaged by the program and +// will persist across renders by the Program. +// +// Unlike fmt.Printf (but similar to log.Printf) the message will be print on +// its own line. +// +// If the altscreen is active no output will be printed. +func (p *Program) Printf(template string, args ...interface{}) { + p.msgs <- printLineMessage{ + messageBody: fmt.Sprintf(template, args...), + } +} + +// Adds a handler to the list of handlers. We wait for all handlers to terminate +// gracefully on shutdown. +func (h *handlers) add(ch chan struct{}) { + *h = append(*h, ch) +} + +// Shutdown waits for all handlers to terminate. +func (h handlers) shutdown() { + var wg sync.WaitGroup + for _, ch := range h { + wg.Add(1) + go func(ch chan struct{}) { + <-ch + wg.Done() + }(ch) + } + wg.Wait() +} diff --git a/vendor/github.com/charmbracelet/bubbletea/tty.go b/vendor/github.com/charmbracelet/bubbletea/tty.go index 17e508b97144a..3ab6639b759b2 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tty.go +++ b/vendor/github.com/charmbracelet/bubbletea/tty.go @@ -3,8 +3,12 @@ package tea import ( "errors" "io" + "os" + "time" + isatty "github.com/mattn/go-isatty" "github.com/muesli/cancelreader" + "golang.org/x/term" ) func (p *Program) initTerminal() error { @@ -20,14 +24,25 @@ func (p *Program) initTerminal() error { } } - hideCursor(p.output) + p.renderer.hideCursor() return nil } // restoreTerminalState restores the terminal to the state prior to running the // Bubble Tea program. -func (p Program) restoreTerminalState() error { - showCursor(p.output) +func (p *Program) restoreTerminalState() error { + if p.renderer != nil { + p.renderer.showCursor() + p.renderer.disableMouseCellMotion() + p.renderer.disableMouseAllMotion() + + if p.renderer.altScreen() { + p.renderer.exitAltScreen() + + // give the terminal a moment to catch up + time.Sleep(time.Millisecond * 10) + } + } if p.console != nil { err := p.console.Reset() @@ -48,33 +63,69 @@ func (p *Program) initCancelReader() error { } p.readLoopDone = make(chan struct{}) - go func() { - defer close(p.readLoopDone) + go p.readLoop() - for { - if p.ctx.Err() != nil { - return - } + return nil +} - msgs, err := readInputs(p.cancelReader) - if err != nil { - if !errors.Is(err, io.EOF) && !errors.Is(err, cancelreader.ErrCanceled) { - p.errs <- err - } +func (p *Program) readLoop() { + defer close(p.readLoopDone) - return - } + for { + if p.ctx.Err() != nil { + return + } - for _, msg := range msgs { - p.msgs <- msg + msgs, err := readInputs(p.cancelReader) + if err != nil { + if !errors.Is(err, io.EOF) && !errors.Is(err, cancelreader.ErrCanceled) { + select { + case <-p.ctx.Done(): + case p.errs <- err: + } } + + return } - }() - return nil + for _, msg := range msgs { + p.msgs <- msg + } + } } -// cancelInput cancels the input reader. -func (p *Program) cancelInput() { - p.cancelReader.Cancel() +// waitForReadLoop waits for the cancelReader to finish its read loop. +func (p *Program) waitForReadLoop() { + select { + case <-p.readLoopDone: + case <-time.After(500 * time.Millisecond): + // The read loop hangs, which means the input + // cancelReader's cancel function has returned true even + // though it was not able to cancel the read. + } +} + +// checkResize detects the current size of the output and informs the program +// via a WindowSizeMsg. +func (p *Program) checkResize() { + f, ok := p.output.TTY().(*os.File) + if !ok || !isatty.IsTerminal(f.Fd()) { + // can't query window size + return + } + + w, h, err := term.GetSize(int(f.Fd())) + if err != nil { + select { + case <-p.ctx.Done(): + case p.errs <- err: + } + + return + } + + p.Send(WindowSizeMsg{ + Width: w, + Height: h, + }) } diff --git a/vendor/github.com/charmbracelet/bubbletea/tty_unix.go b/vendor/github.com/charmbracelet/bubbletea/tty_unix.go index 74fe8dfdbcc70..b6f5ffdf439f2 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tty_unix.go +++ b/vendor/github.com/charmbracelet/bubbletea/tty_unix.go @@ -1,5 +1,5 @@ -//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build darwin dragonfly freebsd linux netbsd openbsd solaris +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || aix +// +build darwin dragonfly freebsd linux netbsd openbsd solaris aix package tea diff --git a/vendor/github.com/charmbracelet/bubbletea/tty_windows.go b/vendor/github.com/charmbracelet/bubbletea/tty_windows.go index 9cfc7d9c2b8fb..be415aef79192 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tty_windows.go +++ b/vendor/github.com/charmbracelet/bubbletea/tty_windows.go @@ -4,11 +4,9 @@ package tea import ( - "io" "os" "github.com/containerd/console" - "golang.org/x/sys/windows" ) func (p *Program) initInput() error { @@ -26,8 +24,6 @@ func (p *Program) initInput() error { p.console = c } - enableAnsiColors(p.output) - return nil } @@ -49,18 +45,3 @@ func openInputTTY() (*os.File, error) { } return f, nil } - -// enableAnsiColors enables support for ANSI color sequences in Windows -// default console. Note that this only works with Windows 10. -func enableAnsiColors(w io.Writer) { - f, ok := w.(*os.File) - if !ok { - return - } - - stdout := windows.Handle(f.Fd()) - var originalMode uint32 - - _ = windows.GetConsoleMode(stdout, &originalMode) - _ = windows.SetConsoleMode(stdout, originalMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING) -} diff --git a/vendor/github.com/charmbracelet/lipgloss/.gitignore b/vendor/github.com/charmbracelet/lipgloss/.gitignore new file mode 100644 index 0000000000000..a170af09c41f5 --- /dev/null +++ b/vendor/github.com/charmbracelet/lipgloss/.gitignore @@ -0,0 +1 @@ +ssh_example_ed25519* \ No newline at end of file diff --git a/vendor/github.com/charmbracelet/lipgloss/README.md b/vendor/github.com/charmbracelet/lipgloss/README.md index 039e28c7cd43b..a071564ecb9af 100644 --- a/vendor/github.com/charmbracelet/lipgloss/README.md +++ b/vendor/github.com/charmbracelet/lipgloss/README.md @@ -27,10 +27,9 @@ var style = lipgloss.NewStyle(). PaddingLeft(4). Width(22) -fmt.Println(style.Render("Hello, kitty.")) +fmt.Println(style.Render("Hello, kitty")) ``` - ## Colors Lip Gloss supports the following color profiles: @@ -59,7 +58,7 @@ lipgloss.Color("#04B575") // a green lipgloss.Color("#3C3C3C") // a dark gray ``` -...as well as a 1-bit Ascii profile, which is black and white only. +...as well as a 1-bit ASCII profile, which is black and white only. The terminal's color profile will be automatically detected, and colors outside the gamut of the current palette will be automatically coerced to their closest @@ -77,6 +76,29 @@ lipgloss.AdaptiveColor{Light: "236", Dark: "248"} The terminal's background color will automatically be detected and the appropriate color will be chosen at runtime. +### Complete Colors + +CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color +profiles. + +```go +lipgloss.CompleteColor{True: "#0000FF", ANSI256: "86", ANSI: "5"} +``` + +Automatic color degradation will not be performed in this case and it will be +based on the color specified. + +### Complete Adaptive Colors + +You can use CompleteColor with AdaptiveColor to specify the exact values for +light and dark backgrounds without automatic color degradation. + +```go +lipgloss.CompleteAdaptiveColor{ + Light: CompleteColor{TrueColor: "#d7ffae", ANSI256: "193", ANSI: "11"}, + Dark: CompleteColor{TrueColor: "#d75fee", ANSI256: "163", ANSI: "5"}, +} +``` ## Inline Formatting @@ -151,11 +173,11 @@ var style = lipgloss.NewStyle(). Setting a minimum width and height is simple and straightforward. ```go -var str = lipgloss.NewStyle(). +var style = lipgloss.NewStyle(). + SetString("What’s for lunch?"). Width(24). Height(32). - Foreground(lipgloss.Color("63")). - Render("What’s for lunch?") + Foreground(lipgloss.Color("63")) ``` @@ -218,7 +240,7 @@ var wildStyle = style.Copy().Blink(true) ``` `Copy()` performs a copy on the underlying data structure ensuring that you get -a true, dereferenced copy of a style. Without copying it's possible to mutate +a true, dereferenced copy of a style. Without copying, it's possible to mutate styles. @@ -274,20 +296,43 @@ someStyle.MaxWidth(5).MaxHeight(5).Render("yadda yadda") ## Rendering -Generally, you just call the `Render(string)` method on a `lipgloss.Style`: +Generally, you just call the `Render(string...)` method on a `lipgloss.Style`: ```go -fmt.Println(lipgloss.NewStyle().Bold(true).Render("Hello, kitty.")) +style := lipgloss.NewStyle().Bold(true).SetString("Hello,") +fmt.Println(style.Render("kitty.")) // Hello, kitty. +fmt.Println(style.Render("puppy.")) // Hello, puppy. ``` But you could also use the Stringer interface: ```go var style = lipgloss.NewStyle().SetString("你好,猫咪。").Bold(true) +fmt.Println(style) // 你好,猫咪。 +``` + +### Custom Renderers + +Custom renderers allow you to render to a specific outputs. This is +particularly important when you want to render to different outputs and +correctly detect the color profile and dark background status for each, such as +in a server-client situation. + +```go +func myLittleHandler(sess ssh.Session) { + // Create a renderer for the client. + renderer := lipgloss.NewRenderer(sess) + + // Create a new style on the renderer. + style := renderer.NewStyle().Background(lipgloss.AdaptiveColor{Light: "63", Dark: "228"}) -fmt.Printf("%s\n", style) + // Render. The color profile and dark background state will be correctly detected. + io.WriteString(sess, style.Render("Heyyyyyyy")) +} ``` +For an example on using a custom renderer over SSH with [Wish][wish] see the +[SSH example][ssh-example]. ## Utilities @@ -318,10 +363,11 @@ Sometimes you’ll want to know the width and height of text blocks when buildin your layouts. ```go -var block string = lipgloss.NewStyle(). +// Render a block of text. +var style = lipgloss.NewStyle(). Width(40). - Padding(2). - Render(someLongString) + Padding(2) +var block string = style.Render(someLongString) // Get the actual, physical dimensions of the text block. width := lipgloss.Width(block) @@ -386,18 +432,27 @@ the stylesheet-based Markdown renderer. [glamour]: https://github.com/charmbracelet/glamour +## Feedback + +We’d love to hear your thoughts on this project. Feel free to drop us a note! + +* [Twitter](https://twitter.com/charmcli) +* [The Fediverse](https://mastodon.social/@charmcli) +* [Discord](https://charm.sh/chat) + ## License [MIT](https://github.com/charmbracelet/lipgloss/raw/master/LICENSE) - *** Part of [Charm](https://charm.sh). -The Charm logo +The Charm logo Charm热爱开源 • Charm loves open source [docs]: https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc +[wish]: https://github.com/charmbracelet/wish +[ssh-example]: examples/ssh diff --git a/vendor/github.com/charmbracelet/lipgloss/align.go b/vendor/github.com/charmbracelet/lipgloss/align.go index 03e7889e9a846..c399703827db6 100644 --- a/vendor/github.com/charmbracelet/lipgloss/align.go +++ b/vendor/github.com/charmbracelet/lipgloss/align.go @@ -10,7 +10,7 @@ import ( // Perform text alignment. If the string is multi-lined, we also make all lines // the same width by padding them with spaces. If a termenv style is passed, // use that to style the spaces added. -func alignText(str string, pos Position, width int, style *termenv.Style) string { +func alignTextHorizontal(str string, pos Position, width int, style *termenv.Style) string { lines, widestLine := getLines(str) var b strings.Builder @@ -57,3 +57,26 @@ func alignText(str string, pos Position, width int, style *termenv.Style) string return b.String() } + +func alignTextVertical(str string, pos Position, height int, _ *termenv.Style) string { + strHeight := strings.Count(str, "\n") + 1 + if height < strHeight { + return str + } + + switch pos { + case Top: + return str + strings.Repeat("\n", height-strHeight) + case Center: + var topPadding, bottomPadding = (height - strHeight) / 2, (height - strHeight) / 2 + if strHeight+topPadding+bottomPadding > height { + topPadding-- + } else if strHeight+topPadding+bottomPadding < height { + bottomPadding++ + } + return strings.Repeat("\n", topPadding) + str + strings.Repeat("\n", bottomPadding) + case Bottom: + return strings.Repeat("\n", height-strHeight) + str + } + return str +} diff --git a/vendor/github.com/charmbracelet/lipgloss/borders.go b/vendor/github.com/charmbracelet/lipgloss/borders.go index a3284ac057b70..18964221a8f42 100644 --- a/vendor/github.com/charmbracelet/lipgloss/borders.go +++ b/vendor/github.com/charmbracelet/lipgloss/borders.go @@ -84,6 +84,39 @@ var ( BottomRight: "╯", } + blockBorder = Border{ + Top: "█", + Bottom: "█", + Left: "█", + Right: "█", + TopLeft: "█", + TopRight: "█", + BottomLeft: "█", + BottomRight: "█", + } + + outerHalfBlockBorder = Border{ + Top: "▀", + Bottom: "▄", + Left: "▌", + Right: "▐", + TopLeft: "▛", + TopRight: "▜", + BottomLeft: "▙", + BottomRight: "▟", + } + + innerHalfBlockBorder = Border{ + Top: "▄", + Bottom: "▀", + Left: "▐", + Right: "▌", + TopLeft: "▗", + TopRight: "▖", + BottomLeft: "▝", + BottomRight: "▘", + } + thickBorder = Border{ Top: "━", Bottom: "━", @@ -129,6 +162,21 @@ func RoundedBorder() Border { return roundedBorder } +// BlockBorder returns a border that takes the whole block. +func BlockBorder() Border { + return blockBorder +} + +// OuterHalfBlockBorder returns a half-block border that sits outside the frame. +func OuterHalfBlockBorder() Border { + return outerHalfBlockBorder +} + +// InnerHalfBlockBorder returns a half-block border that sits inside the frame. +func InnerHalfBlockBorder() Border { + return innerHalfBlockBorder +} + // ThickBorder returns a border that's thicker than the one returned by // NormalBorder. func ThickBorder() Border { @@ -199,7 +247,7 @@ func (s Style) applyBorder(str string) string { border.Right = " " } - // If corners should be render but are set with the empty string, fill them + // If corners should be rendered but are set with the empty string, fill them // with a single space. if hasTop && hasLeft && border.TopLeft == "" { border.TopLeft = " " @@ -250,7 +298,7 @@ func (s Style) applyBorder(str string) string { // Render top if hasTop { top := renderHorizontalEdge(border.TopLeft, border.Top, border.TopRight, width) - top = styleBorder(top, topFG, topBG) + top = s.styleBorder(top, topFG, topBG) out.WriteString(top) out.WriteRune('\n') } @@ -269,7 +317,7 @@ func (s Style) applyBorder(str string) string { if leftIndex >= len(leftRunes) { leftIndex = 0 } - out.WriteString(styleBorder(r, leftFG, leftBG)) + out.WriteString(s.styleBorder(r, leftFG, leftBG)) } out.WriteString(l) if hasRight { @@ -278,7 +326,7 @@ func (s Style) applyBorder(str string) string { if rightIndex >= len(rightRunes) { rightIndex = 0 } - out.WriteString(styleBorder(r, rightFG, rightBG)) + out.WriteString(s.styleBorder(r, rightFG, rightBG)) } if i < len(lines)-1 { out.WriteRune('\n') @@ -288,7 +336,7 @@ func (s Style) applyBorder(str string) string { // Render bottom if hasBottom { bottom := renderHorizontalEdge(border.BottomLeft, border.Bottom, border.BottomRight, width) - bottom = styleBorder(bottom, bottomFG, bottomBG) + bottom = s.styleBorder(bottom, bottomFG, bottomBG) out.WriteRune('\n') out.WriteString(bottom) } @@ -328,7 +376,7 @@ func renderHorizontalEdge(left, middle, right string, width int) string { } // Apply foreground and background styling to a border. -func styleBorder(border string, fg, bg TerminalColor) string { +func (s Style) styleBorder(border string, fg, bg TerminalColor) string { if fg == noColor && bg == noColor { return border } @@ -336,10 +384,10 @@ func styleBorder(border string, fg, bg TerminalColor) string { var style = termenv.Style{} if fg != noColor { - style = style.Foreground(ColorProfile().Color(fg.value())) + style = style.Foreground(fg.color(s.r)) } if bg != noColor { - style = style.Background(ColorProfile().Color(bg.value())) + style = style.Background(bg.color(s.r)) } return style.Styled(border) diff --git a/vendor/github.com/charmbracelet/lipgloss/color.go b/vendor/github.com/charmbracelet/lipgloss/color.go index 8a09d1bb42d70..ef7fd2796a005 100644 --- a/vendor/github.com/charmbracelet/lipgloss/color.go +++ b/vendor/github.com/charmbracelet/lipgloss/color.go @@ -1,152 +1,86 @@ package lipgloss import ( - "sync" + "strconv" - "github.com/lucasb-eyer/go-colorful" "github.com/muesli/termenv" ) -var ( - colorProfile termenv.Profile - getColorProfile sync.Once - explicitColorProfile bool - - hasDarkBackground bool - getBackgroundColor sync.Once - explicitBackgroundColor bool - - colorProfileMtx sync.Mutex -) - -// ColorProfile returns the detected termenv color profile. It will perform the -// actual check only once. -func ColorProfile() termenv.Profile { - if !explicitColorProfile { - getColorProfile.Do(func() { - colorProfile = termenv.EnvColorProfile() - }) - } - return colorProfile -} - -// SetColorProfile sets the color profile on a package-wide context. This -// function exists mostly for testing purposes so that you can assure you're -// testing against a specific profile. -// -// Outside of testing you likely won't want to use this function as -// ColorProfile() will detect and cache the terminal's color capabilities -// and choose the best available profile. -// -// Available color profiles are: -// -// termenv.Ascii (no color, 1-bit) -// termenv.ANSI (16 colors, 4-bit) -// termenv.ANSI256 (256 colors, 8-bit) -// termenv.TrueColor (16,777,216 colors, 24-bit) -// -// This function is thread-safe. -func SetColorProfile(p termenv.Profile) { - colorProfileMtx.Lock() - defer colorProfileMtx.Unlock() - colorProfile = p - explicitColorProfile = true -} - -// HasDarkBackground returns whether or not the terminal has a dark background. -func HasDarkBackground() bool { - if !explicitBackgroundColor { - getBackgroundColor.Do(func() { - hasDarkBackground = termenv.HasDarkBackground() - }) - } - - return hasDarkBackground -} - -// SetHasDarkBackground sets the value of the background color detection on a -// package-wide context. This function exists mostly for testing purposes so -// that you can assure you're testing against a specific background color -// setting. -// -// Outside of testing you likely won't want to use this function as -// HasDarkBackground() will detect and cache the terminal's current background -// color setting. -// -// This function is thread-safe. -func SetHasDarkBackground(b bool) { - colorProfileMtx.Lock() - defer colorProfileMtx.Unlock() - hasDarkBackground = b - explicitBackgroundColor = true -} - -// TerminalColor is a color intended to be rendered in the terminal. It -// satisfies the Go color.Color interface. +// TerminalColor is a color intended to be rendered in the terminal. type TerminalColor interface { - value() string - color() termenv.Color + color(*Renderer) termenv.Color RGBA() (r, g, b, a uint32) } +var noColor = NoColor{} + // NoColor is used to specify the absence of color styling. When this is active // foreground colors will be rendered with the terminal's default text color, // and background colors will not be drawn at all. // // Example usage: // -// var style = someStyle.Copy().Background(lipgloss.NoColor{}) -// +// var style = someStyle.Copy().Background(lipgloss.NoColor{}) type NoColor struct{} -func (n NoColor) value() string { - return "" -} - -func (n NoColor) color() termenv.Color { - return ColorProfile().Color("") +func (NoColor) color(*Renderer) termenv.Color { + return termenv.NoColor{} } // RGBA returns the RGBA value of this color. Because we have to return // something, despite this color being the absence of color, we're returning -// the same value that go-colorful returns on error: +// black with 100% opacity. // // Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF. +// +// Deprecated. func (n NoColor) RGBA() (r, g, b, a uint32) { return 0x0, 0x0, 0x0, 0xFFFF } -var noColor = NoColor{} - // Color specifies a color by hex or ANSI value. For example: // -// ansiColor := lipgloss.Color("21") -// hexColor := lipgloss.Color("#0000ff") -// +// ansiColor := lipgloss.Color("21") +// hexColor := lipgloss.Color("#0000ff") type Color string -func (c Color) value() string { - return string(c) +func (c Color) color(r *Renderer) termenv.Color { + return r.ColorProfile().Color(string(c)) } -func (c Color) color() termenv.Color { - return ColorProfile().Color(string(c)) +// RGBA returns the RGBA value of this color. This satisfies the Go Color +// interface. Note that on error we return black with 100% opacity, or: +// +// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF. +// +// Deprecated. +func (c Color) RGBA() (r, g, b, a uint32) { + return termenv.ConvertToRGB(c.color(renderer)).RGBA() +} + +// ANSIColor is a color specified by an ANSI color value. It's merely syntactic +// sugar for the more general Color function. Invalid colors will render as +// black. +// +// Example usage: +// +// // These two statements are equivalent. +// colorA := lipgloss.ANSIColor(21) +// colorB := lipgloss.Color("21") +type ANSIColor uint + +func (ac ANSIColor) color(r *Renderer) termenv.Color { + return Color(strconv.FormatUint(uint64(ac), 10)).color(r) } // RGBA returns the RGBA value of this color. This satisfies the Go Color // interface. Note that on error we return black with 100% opacity, or: // -// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF +// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF. // -// This is inline with go-colorful's default behavior. -func (c Color) RGBA() (r, g, b, a uint32) { - cf, err := colorful.Hex(c.value()) - if err != nil { - // If we ignore the return behavior and simply return what go-colorful - // give us for the color value we'd be returning exactly this, however - // we're being explicit here for the sake of clarity. - return colorful.Color{}.RGBA() - } +// Deprecated. +func (ac ANSIColor) RGBA() (r, g, b, a uint32) { + cf := Color(strconv.FormatUint(uint64(ac), 10)) return cf.RGBA() } @@ -156,34 +90,83 @@ func (c Color) RGBA() (r, g, b, a uint32) { // // Example usage: // -// color := lipgloss.AdaptiveColor{Light: "#0000ff", Dark: "#000099"} -// +// color := lipgloss.AdaptiveColor{Light: "#0000ff", Dark: "#000099"} type AdaptiveColor struct { Light string Dark string } -func (ac AdaptiveColor) value() string { - if HasDarkBackground() { - return ac.Dark +func (ac AdaptiveColor) color(r *Renderer) termenv.Color { + if r.HasDarkBackground() { + return Color(ac.Dark).color(r) } - return ac.Light + return Color(ac.Light).color(r) +} + +// RGBA returns the RGBA value of this color. This satisfies the Go Color +// interface. Note that on error we return black with 100% opacity, or: +// +// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF. +// +// Deprecated. +func (ac AdaptiveColor) RGBA() (r, g, b, a uint32) { + return termenv.ConvertToRGB(ac.color(renderer)).RGBA() } -func (ac AdaptiveColor) color() termenv.Color { - return ColorProfile().Color(ac.value()) +// CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color +// profiles. Automatic color degradation will not be performed. +type CompleteColor struct { + TrueColor string + ANSI256 string + ANSI string +} + +func (c CompleteColor) color(r *Renderer) termenv.Color { + p := r.ColorProfile() + switch p { + case termenv.TrueColor: + return p.Color(c.TrueColor) + case termenv.ANSI256: + return p.Color(c.ANSI256) + case termenv.ANSI: + return p.Color(c.ANSI) + default: + return termenv.NoColor{} + } } // RGBA returns the RGBA value of this color. This satisfies the Go Color // interface. Note that on error we return black with 100% opacity, or: // -// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF +// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF. +// CompleteAdaptiveColor specifies exact values for truecolor, ANSI256, and ANSI color // -// This is inline with go-colorful's default behavior. -func (ac AdaptiveColor) RGBA() (r, g, b, a uint32) { - cf, err := colorful.Hex(ac.value()) - if err != nil { - return colorful.Color{}.RGBA() +// Deprecated. +func (c CompleteColor) RGBA() (r, g, b, a uint32) { + return termenv.ConvertToRGB(c.color(renderer)).RGBA() +} + +// CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color +// profiles, with separate options for light and dark backgrounds. Automatic +// color degradation will not be performed. +type CompleteAdaptiveColor struct { + Light CompleteColor + Dark CompleteColor +} + +func (cac CompleteAdaptiveColor) color(r *Renderer) termenv.Color { + if r.HasDarkBackground() { + return cac.Dark.color(r) } - return cf.RGBA() + return cac.Light.color(r) +} + +// RGBA returns the RGBA value of this color. This satisfies the Go Color +// interface. Note that on error we return black with 100% opacity, or: +// +// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF. +// +// Deprecated. +func (cac CompleteAdaptiveColor) RGBA() (r, g, b, a uint32) { + return termenv.ConvertToRGB(cac.color(renderer)).RGBA() } diff --git a/vendor/github.com/charmbracelet/lipgloss/get.go b/vendor/github.com/charmbracelet/lipgloss/get.go index 879062278dda8..eb24a4ede3ec1 100644 --- a/vendor/github.com/charmbracelet/lipgloss/get.go +++ b/vendor/github.com/charmbracelet/lipgloss/get.go @@ -6,42 +6,42 @@ import ( "github.com/muesli/reflow/ansi" ) -// GetBold returns the style's bold value It no value is set false is returned. +// GetBold returns the style's bold value. If no value is set false is returned. func (s Style) GetBold() bool { return s.getAsBool(boldKey, false) } -// GetItalic returns the style's italic value. It no value is set false is +// GetItalic returns the style's italic value. If no value is set false is // returned. func (s Style) GetItalic() bool { return s.getAsBool(italicKey, false) } -// GetUnderline returns the style's underline value. It no value is set false is +// GetUnderline returns the style's underline value. If no value is set false is // returned. func (s Style) GetUnderline() bool { return s.getAsBool(underlineKey, false) } -// GetStrikethrough returns the style's strikethrough value. It no value is set false +// GetStrikethrough returns the style's strikethrough value. If no value is set false // is returned. func (s Style) GetStrikethrough() bool { return s.getAsBool(strikethroughKey, false) } -// GetReverse returns the style's reverse value. It no value is set false is +// GetReverse returns the style's reverse value. If no value is set false is // returned. func (s Style) GetReverse() bool { return s.getAsBool(reverseKey, false) } -// GetBlink returns the style's blink value. It no value is set false is +// GetBlink returns the style's blink value. If no value is set false is // returned. func (s Style) GetBlink() bool { return s.getAsBool(blinkKey, false) } -// GetFaint returns the style's faint value. It no value is set false is +// GetFaint returns the style's faint value. If no value is set false is // returned. func (s Style) GetFaint() bool { return s.getAsBool(faintKey, false) @@ -71,16 +71,36 @@ func (s Style) GetHeight() int { return s.getAsInt(heightKey) } -// GetAlign returns the style's implicit alignment setting. If no alignment is -// set Position.AlignLeft is returned. +// GetAlign returns the style's implicit horizontal alignment setting. +// If no alignment is set Position.Left is returned. func (s Style) GetAlign() Position { - v := s.getAsPosition(alignKey) + v := s.getAsPosition(alignHorizontalKey) if v == Position(0) { return Left } return v } +// GetAlignHorizontal returns the style's implicit horizontal alignment setting. +// If no alignment is set Position.Left is returned. +func (s Style) GetAlignHorizontal() Position { + v := s.getAsPosition(alignHorizontalKey) + if v == Position(0) { + return Left + } + return v +} + +// GetAlignVertical returns the style's implicit vertical alignment setting. +// If no alignment is set Position.Top is returned. +func (s Style) GetAlignVertical() Position { + v := s.getAsPosition(alignVerticalKey) + if v == Position(0) { + return Top + } + return v +} + // GetPadding returns the style's top, right, bottom, and left padding values, // in that order. 0 is returned for unset values. func (s Style) GetPadding() (top, right, bottom, left int) { @@ -180,7 +200,7 @@ func (s Style) GetVerticalMargins() int { // GetBorder returns the style's border style (type Border) and value for the // top, right, bottom, and left in that order. If no value is set for the // border style, Border{} is returned. For all other unset values false is -// returend. +// returned. func (s Style) GetBorder() (b Border, top, right, bottom, left bool) { return s.getBorderStyle(), s.getAsBool(borderTopKey, false), @@ -270,7 +290,16 @@ func (s Style) GetBorderLeftBackground() TerminalColor { // GetBorderTopWidth returns the width of the top border. If borders contain // runes of varying widths, the widest rune is returned. If no border exists on // the top edge, 0 is returned. +// +// Deprecated: This function simply calls Style.GetBorderTopSize. func (s Style) GetBorderTopWidth() int { + return s.GetBorderTopSize() +} + +// GetBorderTopSize returns the width of the top border. If borders contain +// runes of varying widths, the widest rune is returned. If no border exists on +// the top edge, 0 is returned. +func (s Style) GetBorderTopSize() int { if !s.getAsBool(borderTopKey, false) { return 0 } @@ -395,12 +424,12 @@ func (s Style) getAsBool(k propKey, defaultVal bool) bool { func (s Style) getAsColor(k propKey) TerminalColor { v, ok := s.rules[k] if !ok { - return NoColor{} + return noColor } if c, ok := v.(TerminalColor); ok { return c } - return NoColor{} + return noColor } func (s Style) getAsInt(k propKey) int { diff --git a/vendor/github.com/charmbracelet/lipgloss/join.go b/vendor/github.com/charmbracelet/lipgloss/join.go index 69ffdc9d01f82..cc16600ac60c4 100644 --- a/vendor/github.com/charmbracelet/lipgloss/join.go +++ b/vendor/github.com/charmbracelet/lipgloss/join.go @@ -17,15 +17,14 @@ import ( // // Example: // -// blockB := "...\n...\n..." -// blockA := "...\n...\n...\n...\n..." +// blockB := "...\n...\n..." +// blockA := "...\n...\n...\n...\n..." // -// // Join 20% from the top -// str := lipgloss.JoinHorizontal(0.2, blockA, blockB) -// -// // Join on the top edge -// str := lipgloss.JoinHorizontal(lipgloss.Top, blockA, blockB) +// // Join 20% from the top +// str := lipgloss.JoinHorizontal(0.2, blockA, blockB) // +// // Join on the top edge +// str := lipgloss.JoinHorizontal(lipgloss.Top, blockA, blockB) func JoinHorizontal(pos Position, strs ...string) string { if len(strs) == 0 { return "" @@ -106,15 +105,14 @@ func JoinHorizontal(pos Position, strs ...string) string { // // Example: // -// blockB := "...\n...\n..." -// blockA := "...\n...\n...\n...\n..." -// -// // Join 20% from the top -// str := lipgloss.JoinVertical(0.2, blockA, blockB) +// blockB := "...\n...\n..." +// blockA := "...\n...\n...\n...\n..." // -// // Join on the right edge -// str := lipgloss.JoinVertical(lipgloss.Right, blockA, blockB) +// // Join 20% from the top +// str := lipgloss.JoinVertical(0.2, blockA, blockB) // +// // Join on the right edge +// str := lipgloss.JoinVertical(lipgloss.Right, blockA, blockB) func JoinVertical(pos Position, strs ...string) string { if len(strs) == 0 { return "" diff --git a/vendor/github.com/charmbracelet/lipgloss/position.go b/vendor/github.com/charmbracelet/lipgloss/position.go index 2ecb897902aa9..28f5ccbd69d54 100644 --- a/vendor/github.com/charmbracelet/lipgloss/position.go +++ b/vendor/github.com/charmbracelet/lipgloss/position.go @@ -34,13 +34,26 @@ const ( // Place places a string or text block vertically in an unstyled box of a given // width or height. func Place(width, height int, hPos, vPos Position, str string, opts ...WhitespaceOption) string { - return PlaceVertical(height, vPos, PlaceHorizontal(width, hPos, str, opts...), opts...) + return renderer.Place(width, height, hPos, vPos, str, opts...) +} + +// Place places a string or text block vertically in an unstyled box of a given +// width or height. +func (r *Renderer) Place(width, height int, hPos, vPos Position, str string, opts ...WhitespaceOption) string { + return r.PlaceVertical(height, vPos, r.PlaceHorizontal(width, hPos, str, opts...), opts...) } // PlaceHorizontal places a string or text block horizontally in an unstyled // block of a given width. If the given width is shorter than the max width of -// the string (measured by it's longest line) this will be a noöp. +// the string (measured by its longest line) this will be a noop. func PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string { + return renderer.PlaceHorizontal(width, pos, str, opts...) +} + +// PlaceHorizontal places a string or text block horizontally in an unstyled +// block of a given width. If the given width is shorter than the max width of +// the string (measured by it's longest line) this will be a noöp. +func (r *Renderer) PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string { lines, contentWidth := getLines(str) gap := width - contentWidth @@ -48,10 +61,7 @@ func PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOpti return str } - ws := &whitespace{} - for _, opt := range opts { - opt(ws) - } + ws := newWhitespace(r, opts...) var b strings.Builder for i, l := range lines { @@ -89,8 +99,15 @@ func PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOpti // PlaceVertical places a string or text block vertically in an unstyled block // of a given height. If the given height is shorter than the height of the -// string (measured by it's newlines) then this will be a noöp. +// string (measured by its newlines) then this will be a noop. func PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string { + return renderer.PlaceVertical(height, pos, str, opts...) +} + +// PlaceVertical places a string or text block vertically in an unstyled block +// of a given height. If the given height is shorter than the height of the +// string (measured by it's newlines) then this will be a noöp. +func (r *Renderer) PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string { contentHeight := strings.Count(str, "\n") + 1 gap := height - contentHeight @@ -98,10 +115,7 @@ func PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOptio return str } - ws := &whitespace{} - for _, opt := range opts { - opt(ws) - } + ws := newWhitespace(r, opts...) _, width := getLines(str) emptyLine := ws.render(width) diff --git a/vendor/github.com/charmbracelet/lipgloss/renderer.go b/vendor/github.com/charmbracelet/lipgloss/renderer.go new file mode 100644 index 0000000000000..4bea8374d7b75 --- /dev/null +++ b/vendor/github.com/charmbracelet/lipgloss/renderer.go @@ -0,0 +1,143 @@ +package lipgloss + +import ( + "io" + + "github.com/muesli/termenv" +) + +// We're manually creating the struct here to avoid initializing the output and +// query the terminal multiple times. +var renderer = &Renderer{ + output: termenv.DefaultOutput(), +} + +// Renderer is a lipgloss terminal renderer. +type Renderer struct { + output *termenv.Output + hasDarkBackground *bool +} + +// RendererOption is a function that can be used to configure a [Renderer]. +type RendererOption func(r *Renderer) + +// DefaultRenderer returns the default renderer. +func DefaultRenderer() *Renderer { + return renderer +} + +// SetDefaultRenderer sets the default global renderer. +func SetDefaultRenderer(r *Renderer) { + renderer = r +} + +// NewRenderer creates a new Renderer. +// +// w will be used to determine the terminal's color capabilities. +func NewRenderer(w io.Writer, opts ...termenv.OutputOption) *Renderer { + r := &Renderer{ + output: termenv.NewOutput(w, opts...), + } + return r +} + +// Output returns the termenv output. +func (r *Renderer) Output() *termenv.Output { + return r.output +} + +// SetOutput sets the termenv output. +func (r *Renderer) SetOutput(o *termenv.Output) { + r.output = o +} + +// ColorProfile returns the detected termenv color profile. +func (r *Renderer) ColorProfile() termenv.Profile { + return r.output.Profile +} + +// ColorProfile returns the detected termenv color profile. +func ColorProfile() termenv.Profile { + return renderer.ColorProfile() +} + +// SetColorProfile sets the color profile on the renderer. This function exists +// mostly for testing purposes so that you can assure you're testing against +// a specific profile. +// +// Outside of testing you likely won't want to use this function as the color +// profile will detect and cache the terminal's color capabilities and choose +// the best available profile. +// +// Available color profiles are: +// +// termenv.Ascii // no color, 1-bit +// termenv.ANSI //16 colors, 4-bit +// termenv.ANSI256 // 256 colors, 8-bit +// termenv.TrueColor // 16,777,216 colors, 24-bit +// +// This function is thread-safe. +func (r *Renderer) SetColorProfile(p termenv.Profile) { + r.output.Profile = p +} + +// SetColorProfile sets the color profile on the default renderer. This +// function exists mostly for testing purposes so that you can assure you're +// testing against a specific profile. +// +// Outside of testing you likely won't want to use this function as the color +// profile will detect and cache the terminal's color capabilities and choose +// the best available profile. +// +// Available color profiles are: +// +// termenv.Ascii // no color, 1-bit +// termenv.ANSI //16 colors, 4-bit +// termenv.ANSI256 // 256 colors, 8-bit +// termenv.TrueColor // 16,777,216 colors, 24-bit +// +// This function is thread-safe. +func SetColorProfile(p termenv.Profile) { + renderer.SetColorProfile(p) +} + +// HasDarkBackground returns whether or not the terminal has a dark background. +func HasDarkBackground() bool { + return renderer.HasDarkBackground() +} + +// HasDarkBackground returns whether or not the renderer will render to a dark +// background. A dark background can either be auto-detected, or set explicitly +// on the renderer. +func (r *Renderer) HasDarkBackground() bool { + if r.hasDarkBackground != nil { + return *r.hasDarkBackground + } + return r.output.HasDarkBackground() +} + +// SetHasDarkBackground sets the background color detection value for the +// default renderer. This function exists mostly for testing purposes so that +// you can assure you're testing against a specific background color setting. +// +// Outside of testing you likely won't want to use this function as the +// backgrounds value will be automatically detected and cached against the +// terminal's current background color setting. +// +// This function is thread-safe. +func SetHasDarkBackground(b bool) { + renderer.SetHasDarkBackground(b) +} + +// SetHasDarkBackground sets the background color detection value on the +// renderer. This function exists mostly for testing purposes so that you can +// assure you're testing against a specific background color setting. +// +// Outside of testing you likely won't want to use this function as the +// backgrounds value will be automatically detected and cached against the +// terminal's current background color setting. +// +// This function is thread-safe. +func (r *Renderer) SetHasDarkBackground(b bool) { + r.hasDarkBackground = &b +} diff --git a/vendor/github.com/charmbracelet/lipgloss/runes.go b/vendor/github.com/charmbracelet/lipgloss/runes.go index 723f6dbf71236..7a49e326cc52d 100644 --- a/vendor/github.com/charmbracelet/lipgloss/runes.go +++ b/vendor/github.com/charmbracelet/lipgloss/runes.go @@ -4,7 +4,7 @@ import ( "strings" ) -// StyleRunes applys a given style to runes at the given indicesin the string. +// StyleRunes apply a given style to runes at the given indices in the string. // Note that you must provide styling options for both matched and unmatched // runes. Indices out of bounds will be ignored. func StyleRunes(str string, indices []int, matched, unmatched Style) string { diff --git a/vendor/github.com/charmbracelet/lipgloss/set.go b/vendor/github.com/charmbracelet/lipgloss/set.go index 2e7dfc8c9dc7a..f8bf9a22d6b43 100644 --- a/vendor/github.com/charmbracelet/lipgloss/set.go +++ b/vendor/github.com/charmbracelet/lipgloss/set.go @@ -1,7 +1,7 @@ package lipgloss // This could (should) probably just be moved into NewStyle(). We've broken it -// out so we can call it in a lazy way. +// out, so we can call it in a lazy way. func (s *Style) init() { if s.rules == nil { s.rules = make(rules) @@ -16,7 +16,7 @@ func (s *Style) set(key propKey, value interface{}) { case int: // We don't allow negative integers on any of our values, so just keep // them at zero or above. We could use uints instead, but the - // conversions are a little tedious so we're sticking with ints for + // conversions are a little tedious, so we're sticking with ints for // sake of usability. s.rules[key] = max(0, v) default: @@ -39,7 +39,7 @@ func (s Style) Italic(v bool) Style { // Underline sets an underline rule. By default, underlines will not be drawn on // whitespace like margins and padding. To change this behavior set -// renderUnderlinesOnSpaces. +// UnderlineSpaces. func (s Style) Underline(v bool) Style { s.set(underlineKey, v) return s @@ -47,7 +47,7 @@ func (s Style) Underline(v bool) Style { // Strikethrough sets a strikethrough rule. By default, strikes will not be // drawn on whitespace like margins and padding. To change this behavior set -// renderStrikethroughOnSpaces. +// StrikethroughSpaces. func (s Style) Strikethrough(v bool) Style { s.set(strikethroughKey, v) return s @@ -73,12 +73,11 @@ func (s Style) Faint(v bool) Style { // Foreground sets a foreground color. // -// // Sets the foreground to blue -// s := lipgloss.NewStyle().Foreground(lipgloss.Color("#0000ff")) -// -// // Removes the foreground color -// s.Foreground(lipgloss.NoColor) +// // Sets the foreground to blue +// s := lipgloss.NewStyle().Foreground(lipgloss.Color("#0000ff")) // +// // Removes the foreground color +// s.Foreground(lipgloss.NoColor) func (s Style) Foreground(c TerminalColor) Style { s.set(foregroundKey, c) return s @@ -97,7 +96,7 @@ func (s Style) Width(i int) Style { return s } -// Height sets the width of the block before applying margins. If the height of +// Height sets the height of the block before applying margins. If the height of // the text block is less than this value after applying padding (or not), the // block will be set to this height. func (s Style) Height(i int) Style { @@ -105,9 +104,31 @@ func (s Style) Height(i int) Style { return s } -// Align sets a text alignment rule. -func (s Style) Align(p Position) Style { - s.set(alignKey, p) +// Align is a shorthand method for setting horizontal and vertical alignment. +// +// With one argument, the position value is applied to the horizontal alignment. +// +// With two arguments, the value is applied to the vertical and horizontal +// alignments, in that order. +func (s Style) Align(p ...Position) Style { + if len(p) > 0 { + s.set(alignHorizontalKey, p[0]) + } + if len(p) > 1 { + s.set(alignVerticalKey, p[1]) + } + return s +} + +// AlignHorizontal sets a horizontal text alignment rule. +func (s Style) AlignHorizontal(p Position) Style { + s.set(alignHorizontalKey, p) + return s +} + +// AlignVertical sets a text alignment rule. +func (s Style) AlignVertical(p Position) Style { + s.set(alignVerticalKey, p) return s } @@ -230,7 +251,7 @@ func (s Style) MarginBackground(c TerminalColor) Style { return s } -// Border is shorthand for setting a the border style and which sides should +// Border is shorthand for setting the border style and which sides should // have a border at once. The variadic argument sides works as follows: // // With one value, the value is applied to all sides. @@ -248,12 +269,11 @@ func (s Style) MarginBackground(c TerminalColor) Style { // // Examples: // -// // Applies borders to the top and bottom only -// lipgloss.NewStyle().Border(lipgloss.NormalBorder(), true, false) -// -// // Applies rounded borders to the right and bottom only -// lipgloss.NewStyle().Border(lipgloss.RoundedBorder(), false, true, true, false) +// // Applies borders to the top and bottom only +// lipgloss.NewStyle().Border(lipgloss.NormalBorder(), true, false) // +// // Applies rounded borders to the right and bottom only +// lipgloss.NewStyle().Border(lipgloss.RoundedBorder(), false, true, true, false) func (s Style) Border(b Border, sides ...bool) Style { s.set(borderStyleKey, b) @@ -280,13 +300,13 @@ func (s Style) Border(b Border, sides ...bool) Style { // the border style, the border will be enabled for all sides during rendering. // // You can define border characters as you'd like, though several default -// styles are included: NormalBorder(), RoundedBorder(), ThickBorder(), and -// DoubleBorder(). +// styles are included: NormalBorder(), RoundedBorder(), BlockBorder(), +// OuterHalfBlockBorder(), InnerHalfBlockBorder(), ThickBorder(), +// and DoubleBorder(). // // Example: // -// lipgloss.NewStyle().BorderStyle(lipgloss.ThickBorder()) -// +// lipgloss.NewStyle().BorderStyle(lipgloss.ThickBorder()) func (s Style) BorderStyle(b Border) Style { s.set(borderStyleKey, b) return s @@ -445,10 +465,9 @@ func (s Style) BorderLeftBackground(c TerminalColor) Style { // // Example: // -// var userInput string = "..." -// var userStyle = text.Style{ /* ... */ } -// fmt.Println(userStyle.Inline(true).Render(userInput)) -// +// var userInput string = "..." +// var userStyle = text.Style{ /* ... */ } +// fmt.Println(userStyle.Inline(true).Render(userInput)) func (s Style) Inline(v bool) Style { o := s.Copy() o.set(inlineKey, v) @@ -464,18 +483,17 @@ func (s Style) Inline(v bool) Style { // // Example: // -// var userInput string = "..." -// var userStyle = text.Style{ /* ... */ } -// fmt.Println(userStyle.MaxWidth(16).Render(userInput)) -// +// var userInput string = "..." +// var userStyle = text.Style{ /* ... */ } +// fmt.Println(userStyle.MaxWidth(16).Render(userInput)) func (s Style) MaxWidth(n int) Style { o := s.Copy() o.set(maxWidthKey, n) return o } -// MaxHeight applies a max width to a given style. This is useful in enforcing -// a certain width at render time, particularly with arbitrary strings and +// MaxHeight applies a max height to a given style. This is useful in enforcing +// a certain height at render time, particularly with arbitrary strings and // styles. // // Because this in intended to be used at the time of render, this method will @@ -487,7 +505,7 @@ func (s Style) MaxHeight(n int) Style { } // UnderlineSpaces determines whether to underline spaces between words. By -// default this is true. Spaces can also be underlined without underlining the +// default, this is true. Spaces can also be underlined without underlining the // text itself. func (s Style) UnderlineSpaces(v bool) Style { s.set(underlineSpacesKey, v) @@ -495,13 +513,20 @@ func (s Style) UnderlineSpaces(v bool) Style { } // StrikethroughSpaces determines whether to apply strikethroughs to spaces -// between words. By default this is true. Spaces can also be struck without +// between words. By default, this is true. Spaces can also be struck without // underlining the text itself. func (s Style) StrikethroughSpaces(v bool) Style { s.set(strikethroughSpacesKey, v) return s } +// Renderer sets the renderer for the style. This is useful for changing the +// renderer for a style that is being used in a different context. +func (s Style) Renderer(r *Renderer) Style { + s.r = r + return s +} + // whichSidesInt is a helper method for setting values on sides of a block based // on the number of arguments. It follows the CSS shorthand rules for blocks // like margin, padding. and borders. Here are how the rules work: diff --git a/vendor/github.com/charmbracelet/lipgloss/style.go b/vendor/github.com/charmbracelet/lipgloss/style.go index a3a5e4afc531e..e94b86702d285 100644 --- a/vendor/github.com/charmbracelet/lipgloss/style.go +++ b/vendor/github.com/charmbracelet/lipgloss/style.go @@ -26,7 +26,8 @@ const ( backgroundKey widthKey heightKey - alignKey + alignHorizontalKey + alignVerticalKey // Padding. paddingTopKey @@ -74,26 +75,43 @@ const ( // A set of properties. type rules map[propKey]interface{} -// NewStyle returns a new, empty Style. While it's syntactic sugar for the +// NewStyle returns a new, empty Style. While it's syntactic sugar for the // Style{} primitive, it's recommended to use this function for creating styles -// incase the underlying implementation changes. +// in case the underlying implementation changes. It takes an optional string +// value to be set as the underlying string value for this style. func NewStyle() Style { - return Style{} + return renderer.NewStyle() +} + +// NewStyle returns a new, empty Style. While it's syntactic sugar for the +// Style{} primitive, it's recommended to use this function for creating styles +// in case the underlying implementation changes. It takes an optional string +// value to be set as the underlying string value for this style. +func (r *Renderer) NewStyle() Style { + s := Style{r: r} + return s } // Style contains a set of rules that comprise a style as a whole. type Style struct { + r *Renderer rules map[propKey]interface{} value string } +// joinString joins a list of strings into a single string separated with a +// space. +func joinString(strs ...string) string { + return strings.Join(strs, " ") +} + // SetString sets the underlying string value for this style. To render once // the underlying string is set, use the Style.String. This method is // a convenience for cases when having a stringer implementation is handy, such // as when using fmt.Sprintf. You can also simply define a style and render out // strings directly with Style.Render. -func (s Style) SetString(str string) Style { - s.value = str +func (s Style) SetString(strs ...string) Style { + s.value = joinString(strs...) return s } @@ -106,7 +124,7 @@ func (s Style) Value() string { // on the rules in this style. An underlying string value must be set with // Style.SetString prior to using this method. func (s Style) String() string { - return s.Render(s.value) + return s.Render() } // Copy returns a copy of this style, including any underlying string values. @@ -116,13 +134,14 @@ func (s Style) Copy() Style { for k, v := range s.rules { o.rules[k] = v } + o.r = s.r o.value = s.value return o } -// Inherit takes values from the style in the argument applies them to this -// style, overwriting existing definitions. Only values explicitly set on the -// style in argument will be applied. +// Inherit overlays the style in the argument onto this style by copying each explicitly +// set value from the argument style onto this style if it is not already explicitly set. +// Existing set values are kept intact and not overwritten. // // Margins, padding, and underlying string values are not inherited. func (s Style) Inherit(i Style) Style { @@ -137,8 +156,6 @@ func (s Style) Inherit(i Style) Style { // Padding is not inherited continue case backgroundKey: - s.rules[k] = v - // The margins also inherit the background color if !s.isSet(marginBackgroundKey) && !i.isSet(marginBackgroundKey) { s.rules[marginBackgroundKey] = v @@ -154,11 +171,20 @@ func (s Style) Inherit(i Style) Style { } // Render applies the defined style formatting to a given string. -func (s Style) Render(str string) string { +func (s Style) Render(strs ...string) string { + if s.r == nil { + s.r = renderer + } + if s.value != "" { + strs = append([]string{s.value}, strs...) + } + var ( - te termenv.Style - teSpace termenv.Style - teWhitespace termenv.Style + str = joinString(strs...) + + te = s.r.ColorProfile().String() + teSpace = s.r.ColorProfile().String() + teWhitespace = s.r.ColorProfile().String() bold = s.getAsBool(boldKey, false) italic = s.getAsBool(italicKey, false) @@ -171,9 +197,10 @@ func (s Style) Render(str string) string { fg = s.getAsColor(foregroundKey) bg = s.getAsColor(backgroundKey) - width = s.getAsInt(widthKey) - height = s.getAsInt(heightKey) - align = s.getAsPosition(alignKey) + width = s.getAsInt(widthKey) + height = s.getAsInt(heightKey) + horizontalAlign = s.getAsPosition(alignHorizontalKey) + verticalAlign = s.getAsPosition(alignVerticalKey) topPadding = s.getAsInt(paddingTopKey) rightPadding = s.getAsInt(paddingRightKey) @@ -227,24 +254,22 @@ func (s Style) Render(str string) string { } if fg != noColor { - fgc := fg.color() - te = te.Foreground(fgc) + te = te.Foreground(fg.color(s.r)) if styleWhitespace { - teWhitespace = teWhitespace.Foreground(fgc) + teWhitespace = teWhitespace.Foreground(fg.color(s.r)) } if useSpaceStyler { - teSpace = teSpace.Foreground(fgc) + teSpace = teSpace.Foreground(fg.color(s.r)) } } if bg != noColor { - bgc := bg.color() - te = te.Background(bgc) + te = te.Background(bg.color(s.r)) if colorWhitespace { - teWhitespace = teWhitespace.Background(bgc) + teWhitespace = teWhitespace.Background(bg.color(s.r)) } if useSpaceStyler { - teSpace = teSpace.Background(bgc) + teSpace = teSpace.Background(bg.color(s.r)) } } @@ -264,7 +289,7 @@ func (s Style) Render(str string) string { // Strip newlines in single line mode if inline { - str = strings.Replace(str, "\n", "", -1) + str = strings.ReplaceAll(str, "\n", "") } // Word wrap @@ -329,10 +354,7 @@ func (s Style) Render(str string) string { // Height if height > 0 { - h := strings.Count(str, "\n") + 1 - if height > h { - str += strings.Repeat("\n", height-h) - } + str = alignTextVertical(str, verticalAlign, height, nil) } // Set alignment. This will also pad short lines with spaces so that all @@ -346,7 +368,7 @@ func (s Style) Render(str string) string { if colorWhitespace || styleWhitespace { st = &teWhitespace } - str = alignText(str, align, width, st) + str = alignTextHorizontal(str, horizontalAlign, width, st) } } @@ -387,7 +409,7 @@ func (s Style) applyMargins(str string, inline bool) string { bgc := s.getAsColor(marginBackgroundKey) if bgc != noColor { - styler = styler.Background(bgc.color()) + styler = styler.Background(bgc.color(s.r)) } // Add left and right margin @@ -435,7 +457,7 @@ func padLeft(str string, n int, style *termenv.Style) string { return b.String() } -// Apply right right padding. +// Apply right padding. func padRight(str string, n int, style *termenv.Style) string { if n == 0 || str == "" { return str diff --git a/vendor/github.com/charmbracelet/lipgloss/unset.go b/vendor/github.com/charmbracelet/lipgloss/unset.go index 0a03720deba20..a8367898b82d8 100644 --- a/vendor/github.com/charmbracelet/lipgloss/unset.go +++ b/vendor/github.com/charmbracelet/lipgloss/unset.go @@ -66,9 +66,22 @@ func (s Style) UnsetHeight() Style { return s } -// UnsetAlign removes the text alignment style rule, if set. +// UnsetAlign removes the horizontal and vertical text alignment style rule, if set. func (s Style) UnsetAlign() Style { - delete(s.rules, alignKey) + delete(s.rules, alignHorizontalKey) + delete(s.rules, alignVerticalKey) + return s +} + +// UnsetAlignHorizontal removes the horizontal text alignment style rule, if set. +func (s Style) UnsetAlignHorizontal() Style { + delete(s.rules, alignHorizontalKey) + return s +} + +// UnsetAlignVertical removes the vertical text alignment style rule, if set. +func (s Style) UnsetAlignVertical() Style { + delete(s.rules, alignVerticalKey) return s } diff --git a/vendor/github.com/charmbracelet/lipgloss/whitespace.go b/vendor/github.com/charmbracelet/lipgloss/whitespace.go index 6c510a78d139f..b043e5652fe04 100644 --- a/vendor/github.com/charmbracelet/lipgloss/whitespace.go +++ b/vendor/github.com/charmbracelet/lipgloss/whitespace.go @@ -9,10 +9,25 @@ import ( // whitespace is a whitespace renderer. type whitespace struct { + re *Renderer style termenv.Style chars string } +// newWhitespace creates a new whitespace renderer. The order of the options +// matters, it you'r using WithWhitespaceRenderer, make sure it comes first as +// other options might depend on it. +func newWhitespace(r *Renderer, opts ...WhitespaceOption) *whitespace { + w := &whitespace{ + re: r, + style: r.ColorProfile().String(), + } + for _, opt := range opts { + opt(w) + } + return w +} + // Render whitespaces. func (w whitespace) render(width int) string { if w.chars == "" { @@ -49,14 +64,14 @@ type WhitespaceOption func(*whitespace) // WithWhitespaceForeground sets the color of the characters in the whitespace. func WithWhitespaceForeground(c TerminalColor) WhitespaceOption { return func(w *whitespace) { - w.style = w.style.Foreground(c.color()) + w.style = w.style.Foreground(c.color(w.re)) } } // WithWhitespaceBackground sets the background color of the whitespace. func WithWhitespaceBackground(c TerminalColor) WhitespaceOption { return func(w *whitespace) { - w.style = w.style.Background(c.color()) + w.style = w.style.Background(c.color(w.re)) } } diff --git a/vendor/github.com/containerd/console/.golangci.yml b/vendor/github.com/containerd/console/.golangci.yml index fcba5e885f0a0..abe3d84bb1648 100644 --- a/vendor/github.com/containerd/console/.golangci.yml +++ b/vendor/github.com/containerd/console/.golangci.yml @@ -1,16 +1,16 @@ linters: enable: - - structcheck - - varcheck - - staticcheck - - unconvert - gofmt - goimports - - golint - ineffassign - - vet - - unused - misspell + - revive + - staticcheck + - structcheck + - unconvert + - unused + - varcheck + - vet disable: - errcheck diff --git a/vendor/github.com/containerd/console/console.go b/vendor/github.com/containerd/console/console.go index f989d28a41cd4..810a71f48dd1c 100644 --- a/vendor/github.com/containerd/console/console.go +++ b/vendor/github.com/containerd/console/console.go @@ -78,7 +78,7 @@ func Current() (c Console) { } // ConsoleFromFile returns a console using the provided file -// nolint:golint +// nolint:revive func ConsoleFromFile(f File) (Console, error) { if err := checkConsole(f); err != nil { return nil, err diff --git a/vendor/github.com/containerd/console/console_linux.go b/vendor/github.com/containerd/console/console_linux.go index c1c839ee3ae02..28b77b7a38918 100644 --- a/vendor/github.com/containerd/console/console_linux.go +++ b/vendor/github.com/containerd/console/console_linux.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux /* diff --git a/vendor/github.com/containerd/console/console_unix.go b/vendor/github.com/containerd/console/console_unix.go index a08117695e320..161f5d126cbad 100644 --- a/vendor/github.com/containerd/console/console_unix.go +++ b/vendor/github.com/containerd/console/console_unix.go @@ -1,4 +1,5 @@ -// +build darwin freebsd linux netbsd openbsd solaris +//go:build darwin || freebsd || linux || netbsd || openbsd || zos +// +build darwin freebsd linux netbsd openbsd zos /* Copyright The containerd Authors. diff --git a/vendor/github.com/containerd/console/console_windows.go b/vendor/github.com/containerd/console/console_windows.go index 787c11fe56fd9..f48e48612870f 100644 --- a/vendor/github.com/containerd/console/console_windows.go +++ b/vendor/github.com/containerd/console/console_windows.go @@ -30,6 +30,10 @@ var ( ) func (m *master) initStdios() { + // Note: We discard console mode warnings, because in/out can be redirected. + // + // TODO: Investigate opening CONOUT$/CONIN$ to handle this correctly + m.in = windows.Handle(os.Stdin.Fd()) if err := windows.GetConsoleMode(m.in, &m.inMode); err == nil { // Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it. @@ -39,8 +43,6 @@ func (m *master) initStdios() { // Unconditionally set the console mode back even on failure because SetConsoleMode // remembers invalid bits on input handles. windows.SetConsoleMode(m.in, m.inMode) - } else { - fmt.Printf("failed to get console mode for stdin: %v\n", err) } m.out = windows.Handle(os.Stdout.Fd()) @@ -50,8 +52,6 @@ func (m *master) initStdios() { } else { windows.SetConsoleMode(m.out, m.outMode) } - } else { - fmt.Printf("failed to get console mode for stdout: %v\n", err) } m.err = windows.Handle(os.Stderr.Fd()) @@ -61,8 +61,6 @@ func (m *master) initStdios() { } else { windows.SetConsoleMode(m.err, m.errMode) } - } else { - fmt.Printf("failed to get console mode for stderr: %v\n", err) } } @@ -94,6 +92,8 @@ func (m *master) SetRaw() error { } func (m *master) Reset() error { + var errs []error + for _, s := range []struct { fd windows.Handle mode uint32 @@ -103,10 +103,16 @@ func (m *master) Reset() error { {m.err, m.errMode}, } { if err := windows.SetConsoleMode(s.fd, s.mode); err != nil { - return fmt.Errorf("unable to restore console mode: %w", err) + // we can't just abort on the first error, otherwise we might leave + // the console in an unexpected state. + errs = append(errs, fmt.Errorf("unable to restore console mode: %w", err)) } } + if len(errs) > 0 { + return errs[0] + } + return nil } diff --git a/vendor/github.com/containerd/console/console_zos.go b/vendor/github.com/containerd/console/console_zos.go deleted file mode 100644 index b348a839a03b8..0000000000000 --- a/vendor/github.com/containerd/console/console_zos.go +++ /dev/null @@ -1,163 +0,0 @@ -// +build zos - -/* - Copyright The containerd 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 console - -import ( - "fmt" - "os" - - "golang.org/x/sys/unix" -) - -// NewPty creates a new pty pair -// The master is returned as the first console and a string -// with the path to the pty slave is returned as the second -func NewPty() (Console, string, error) { - var f File - var err error - var slave string - for i := 0;; i++ { - ptyp := fmt.Sprintf("/dev/ptyp%04d", i) - f, err = os.OpenFile(ptyp, os.O_RDWR, 0600) - if err == nil { - slave = fmt.Sprintf("/dev/ttyp%04d", i) - break - } - if os.IsNotExist(err) { - return nil, "", err - } - // else probably Resource Busy - } - m, err := newMaster(f) - if err != nil { - return nil, "", err - } - return m, slave, nil -} - -type master struct { - f File - original *unix.Termios -} - -func (m *master) Read(b []byte) (int, error) { - return m.f.Read(b) -} - -func (m *master) Write(b []byte) (int, error) { - return m.f.Write(b) -} - -func (m *master) Close() error { - return m.f.Close() -} - -func (m *master) Resize(ws WinSize) error { - return tcswinsz(m.f.Fd(), ws) -} - -func (m *master) ResizeFrom(c Console) error { - ws, err := c.Size() - if err != nil { - return err - } - return m.Resize(ws) -} - -func (m *master) Reset() error { - if m.original == nil { - return nil - } - return tcset(m.f.Fd(), m.original) -} - -func (m *master) getCurrent() (unix.Termios, error) { - var termios unix.Termios - if err := tcget(m.f.Fd(), &termios); err != nil { - return unix.Termios{}, err - } - return termios, nil -} - -func (m *master) SetRaw() error { - rawState, err := m.getCurrent() - if err != nil { - return err - } - rawState = cfmakeraw(rawState) - rawState.Oflag = rawState.Oflag | unix.OPOST - return tcset(m.f.Fd(), &rawState) -} - -func (m *master) DisableEcho() error { - rawState, err := m.getCurrent() - if err != nil { - return err - } - rawState.Lflag = rawState.Lflag &^ unix.ECHO - return tcset(m.f.Fd(), &rawState) -} - -func (m *master) Size() (WinSize, error) { - return tcgwinsz(m.f.Fd()) -} - -func (m *master) Fd() uintptr { - return m.f.Fd() -} - -func (m *master) Name() string { - return m.f.Name() -} - -// checkConsole checks if the provided file is a console -func checkConsole(f File) error { - var termios unix.Termios - if tcget(f.Fd(), &termios) != nil { - return ErrNotAConsole - } - return nil -} - -func newMaster(f File) (Console, error) { - m := &master{ - f: f, - } - t, err := m.getCurrent() - if err != nil { - return nil, err - } - m.original = &t - return m, nil -} - -// ClearONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair -// created by us acts normally. In particular, a not-very-well-known default of -// Linux unix98 ptys is that they have +onlcr by default. While this isn't a -// problem for terminal emulators, because we relay data from the terminal we -// also relay that funky line discipline. -func ClearONLCR(fd uintptr) error { - return setONLCR(fd, false) -} - -// SetONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair -// created by us acts as intended for a terminal emulator. -func SetONLCR(fd uintptr) error { - return setONLCR(fd, true) -} diff --git a/vendor/github.com/containerd/console/pty_freebsd_cgo.go b/vendor/github.com/containerd/console/pty_freebsd_cgo.go index cbd3cd7ea43d8..22368623aab23 100644 --- a/vendor/github.com/containerd/console/pty_freebsd_cgo.go +++ b/vendor/github.com/containerd/console/pty_freebsd_cgo.go @@ -1,3 +1,4 @@ +//go:build freebsd && cgo // +build freebsd,cgo /* diff --git a/vendor/github.com/containerd/console/pty_freebsd_nocgo.go b/vendor/github.com/containerd/console/pty_freebsd_nocgo.go index b5e43181d4f36..ceb90a47b8189 100644 --- a/vendor/github.com/containerd/console/pty_freebsd_nocgo.go +++ b/vendor/github.com/containerd/console/pty_freebsd_nocgo.go @@ -1,3 +1,4 @@ +//go:build freebsd && !cgo // +build freebsd,!cgo /* diff --git a/vendor/github.com/containerd/console/pty_unix.go b/vendor/github.com/containerd/console/pty_unix.go index d5a6bd8ca2e80..f5a5b8058c650 100644 --- a/vendor/github.com/containerd/console/pty_unix.go +++ b/vendor/github.com/containerd/console/pty_unix.go @@ -1,4 +1,5 @@ -// +build darwin linux netbsd openbsd solaris +//go:build darwin || linux || netbsd || openbsd +// +build darwin linux netbsd openbsd /* Copyright The containerd Authors. diff --git a/vendor/github.com/containerd/console/pty_zos.go b/vendor/github.com/containerd/console/pty_zos.go new file mode 100644 index 0000000000000..58f59aba58c98 --- /dev/null +++ b/vendor/github.com/containerd/console/pty_zos.go @@ -0,0 +1,43 @@ +//go:build zos +// +build zos + +/* + Copyright The containerd 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 console + +import ( + "fmt" + "os" +) + +// openpt allocates a new pseudo-terminal by opening the first available /dev/ptypXX device +func openpt() (*os.File, error) { + var f *os.File + var err error + for i := 0; ; i++ { + ptyp := fmt.Sprintf("/dev/ptyp%04d", i) + f, err = os.OpenFile(ptyp, os.O_RDWR, 0600) + if err == nil { + break + } + if os.IsNotExist(err) { + return nil, err + } + // else probably Resource Busy + } + return f, nil +} diff --git a/vendor/github.com/containerd/console/tc_freebsd_cgo.go b/vendor/github.com/containerd/console/tc_freebsd_cgo.go index 0f3d27273094c..33282579411ee 100644 --- a/vendor/github.com/containerd/console/tc_freebsd_cgo.go +++ b/vendor/github.com/containerd/console/tc_freebsd_cgo.go @@ -1,3 +1,4 @@ +//go:build freebsd && cgo // +build freebsd,cgo /* diff --git a/vendor/github.com/containerd/console/tc_freebsd_nocgo.go b/vendor/github.com/containerd/console/tc_freebsd_nocgo.go index 087fc158a1696..18a9b9cbea97a 100644 --- a/vendor/github.com/containerd/console/tc_freebsd_nocgo.go +++ b/vendor/github.com/containerd/console/tc_freebsd_nocgo.go @@ -1,3 +1,4 @@ +//go:build freebsd && !cgo // +build freebsd,!cgo /* diff --git a/vendor/github.com/containerd/console/tc_openbsd_cgo.go b/vendor/github.com/containerd/console/tc_openbsd_cgo.go index f0cec06a72dc8..0e76f6cc3e0af 100644 --- a/vendor/github.com/containerd/console/tc_openbsd_cgo.go +++ b/vendor/github.com/containerd/console/tc_openbsd_cgo.go @@ -1,3 +1,4 @@ +//go:build openbsd && cgo // +build openbsd,cgo /* diff --git a/vendor/github.com/containerd/console/tc_openbsd_nocgo.go b/vendor/github.com/containerd/console/tc_openbsd_nocgo.go index daccce20585a7..dca92418b0e53 100644 --- a/vendor/github.com/containerd/console/tc_openbsd_nocgo.go +++ b/vendor/github.com/containerd/console/tc_openbsd_nocgo.go @@ -1,3 +1,4 @@ +//go:build openbsd && !cgo // +build openbsd,!cgo /* diff --git a/vendor/github.com/containerd/console/tc_solaris_cgo.go b/vendor/github.com/containerd/console/tc_solaris_cgo.go deleted file mode 100644 index e36a68edd1ed7..0000000000000 --- a/vendor/github.com/containerd/console/tc_solaris_cgo.go +++ /dev/null @@ -1,51 +0,0 @@ -// +build solaris,cgo - -/* - Copyright The containerd 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 console - -import ( - "os" - - "golang.org/x/sys/unix" -) - -//#include -import "C" - -const ( - cmdTcGet = unix.TCGETS - cmdTcSet = unix.TCSETS -) - -// ptsname retrieves the name of the first available pts for the given master. -func ptsname(f *os.File) (string, error) { - ptspath, err := C.ptsname(C.int(f.Fd())) - if err != nil { - return "", err - } - return C.GoString(ptspath), nil -} - -// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. -// unlockpt should be called before opening the slave side of a pty. -func unlockpt(f *os.File) error { - if _, err := C.grantpt(C.int(f.Fd())); err != nil { - return err - } - return nil -} diff --git a/vendor/github.com/containerd/console/tc_solaris_nocgo.go b/vendor/github.com/containerd/console/tc_solaris_nocgo.go deleted file mode 100644 index eb0bd2c36b830..0000000000000 --- a/vendor/github.com/containerd/console/tc_solaris_nocgo.go +++ /dev/null @@ -1,47 +0,0 @@ -// +build solaris,!cgo - -/* - Copyright The containerd 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. -*/ - -// -// Implementing the functions below requires cgo support. Non-cgo stubs -// versions are defined below to enable cross-compilation of source code -// that depends on these functions, but the resultant cross-compiled -// binaries cannot actually be used. If the stub function(s) below are -// actually invoked they will display an error message and cause the -// calling process to exit. -// - -package console - -import ( - "os" - - "golang.org/x/sys/unix" -) - -const ( - cmdTcGet = unix.TCGETS - cmdTcSet = unix.TCSETS -) - -func ptsname(f *os.File) (string, error) { - panic("ptsname() support requires cgo.") -} - -func unlockpt(f *os.File) error { - panic("unlockpt() support requires cgo.") -} diff --git a/vendor/github.com/containerd/console/tc_unix.go b/vendor/github.com/containerd/console/tc_unix.go index a6bf01e8d1a2b..f5053b2f39862 100644 --- a/vendor/github.com/containerd/console/tc_unix.go +++ b/vendor/github.com/containerd/console/tc_unix.go @@ -1,4 +1,5 @@ -// +build darwin freebsd linux netbsd openbsd solaris zos +//go:build darwin || freebsd || linux || netbsd || openbsd || zos +// +build darwin freebsd linux netbsd openbsd zos /* Copyright The containerd Authors. diff --git a/vendor/github.com/containerd/console/tc_zos.go b/vendor/github.com/containerd/console/tc_zos.go index 4262eaf4cc076..fc90ba5fb86e3 100644 --- a/vendor/github.com/containerd/console/tc_zos.go +++ b/vendor/github.com/containerd/console/tc_zos.go @@ -17,6 +17,9 @@ package console import ( + "os" + "strings" + "golang.org/x/sys/unix" ) @@ -24,3 +27,13 @@ const ( cmdTcGet = unix.TCGETS cmdTcSet = unix.TCSETS ) + +// unlockpt is a no-op on zos. +func unlockpt(_ *os.File) error { + return nil +} + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + return "/dev/ttyp" + strings.TrimPrefix(f.Name(), "/dev/ptyp"), nil +} diff --git a/vendor/github.com/evertras/bubble-table/table/border.go b/vendor/github.com/evertras/bubble-table/table/border.go index b31846b7052d9..9176e73086996 100644 --- a/vendor/github.com/evertras/bubble-table/table/border.go +++ b/vendor/github.com/evertras/bubble-table/table/border.go @@ -140,7 +140,8 @@ func (b *Border) styleBothWithFooter(original lipgloss.Style) lipgloss.Style { } // This function is long, but it's just repetitive... -// nolint:funlen +// +//nolint:funlen func (b *Border) generateMultiStyles() { b.styleMultiTopLeft = lipgloss.NewStyle().BorderStyle( lipgloss.Border{ @@ -354,7 +355,8 @@ func (b *borderStyleRow) inherit(s lipgloss.Style) { // There's a lot of branches here, but splitting it up further would make it // harder to follow. So just be careful with comments and make sure it's tested! -// nolint:nestif +// +//nolint:nestif func (m Model) styleHeaders() borderStyleRow { hasRows := len(m.GetVisibleRows()) > 0 singleColumn := len(m.columns) == 1 diff --git a/vendor/github.com/evertras/bubble-table/table/calc.go b/vendor/github.com/evertras/bubble-table/table/calc.go index b814c2c904107..3f0db046b286f 100644 --- a/vendor/github.com/evertras/bubble-table/table/calc.go +++ b/vendor/github.com/evertras/bubble-table/table/calc.go @@ -16,7 +16,9 @@ func max(x, y int) int { return y } -// nolint: varnamelen +// These var names are fine for this little function +// +//nolint:varnamelen func gcd(x, y int) int { if x == 0 { return y diff --git a/vendor/github.com/evertras/bubble-table/table/column.go b/vendor/github.com/evertras/bubble-table/table/column.go index 4328a4ff3e4bb..800fde7c0f0ef 100644 --- a/vendor/github.com/evertras/bubble-table/table/column.go +++ b/vendor/github.com/evertras/bubble-table/table/column.go @@ -14,6 +14,8 @@ type Column struct { filterable bool style lipgloss.Style + + fmtString string } // NewColumn creates a new fixed-width column with the given information. @@ -58,6 +60,19 @@ func (c Column) WithFiltered(filterable bool) Column { return c } +// WithFormatString sets the format string used by fmt.Sprintf to display the data. +// If not set, the default is "%v" for all data types. Intended mainly for +// numeric formatting. +// +// Since data is of the interface{} type, make sure that all data in the column +// is of the expected type or the format may fail. For example, hardcoding '3' +// instead of '3.0' and using '%.2f' will fail because '3' is an integer. +func (c Column) WithFormatString(fmtString string) Column { + c.fmtString = fmtString + + return c +} + func (c *Column) isFlex() bool { return c.flexFactor != 0 } diff --git a/vendor/github.com/evertras/bubble-table/table/data.go b/vendor/github.com/evertras/bubble-table/table/data.go index 44dc613e639b3..73d558a04ef5a 100644 --- a/vendor/github.com/evertras/bubble-table/table/data.go +++ b/vendor/github.com/evertras/bubble-table/table/data.go @@ -3,7 +3,8 @@ package table import "time" // This is just a bunch of data type checks, so... no linting here -// nolint: cyclop +// +//nolint:cyclop func asInt(data interface{}) (int64, bool) { switch val := data.(type) { case int: diff --git a/vendor/github.com/evertras/bubble-table/table/events.go b/vendor/github.com/evertras/bubble-table/table/events.go index 1a3d823e7a7a7..4b5e2b09d884b 100644 --- a/vendor/github.com/evertras/bubble-table/table/events.go +++ b/vendor/github.com/evertras/bubble-table/table/events.go @@ -48,3 +48,13 @@ type UserEventRowSelectToggled struct { RowIndex int IsSelected bool } + +// UserEventFilterInputFocused indicates that the user has focused the filter +// text input, so that any other typing will type into the filter field. Only +// activates for the built-in filter text box. +type UserEventFilterInputFocused struct{} + +// UserEventFilterInputUnfocused indicates that the user has unfocused the filter +// text input, which means the user is done typing into the filter field. Only +// activates for the built-in filter text box. +type UserEventFilterInputUnfocused struct{} diff --git a/vendor/github.com/evertras/bubble-table/table/filter.go b/vendor/github.com/evertras/bubble-table/table/filter.go index bde8eb24a056c..0ba233b865bb0 100644 --- a/vendor/github.com/evertras/bubble-table/table/filter.go +++ b/vendor/github.com/evertras/bubble-table/table/filter.go @@ -6,14 +6,15 @@ import ( ) func (m Model) getFilteredRows(rows []Row) []Row { - if !m.filtered || m.filterTextInput.Value() == "" { + filterInputValue := m.filterTextInput.Value() + if !m.filtered || filterInputValue == "" { return rows } filteredRows := make([]Row, 0) for _, row := range rows { - if isRowMatched(m.columns, row, m.filterTextInput.Value()) { + if isRowMatched(m.columns, row, filterInputValue) { filteredRows = append(filteredRows, row) } } @@ -28,6 +29,8 @@ func isRowMatched(columns []Column, row Row, filter string) bool { checkedAny := false + filterLower := strings.ToLower(filter) + for _, column := range columns { if !column.filterable { continue @@ -41,16 +44,26 @@ func isRowMatched(columns []Column, row Row, filter string) bool { continue } + // Extract internal StyledCell data + switch dataV := data.(type) { + case StyledCell: + data = dataV.Data + } + + var target string switch dataV := data.(type) { case string: - if strings.Contains(strings.ToLower(dataV), strings.ToLower(filter)) { - return true - } + target = dataV case fmt.Stringer: - if strings.Contains(strings.ToLower(dataV.String()), strings.ToLower(filter)) { - return true - } + target = dataV.String() + + default: + target = fmt.Sprintf("%v", data) + } + + if strings.Contains(strings.ToLower(target), filterLower) { + return true } } diff --git a/vendor/github.com/evertras/bubble-table/table/footer.go b/vendor/github.com/evertras/bubble-table/table/footer.go index df4011e38c787..ba657a1d28395 100644 --- a/vendor/github.com/evertras/bubble-table/table/footer.go +++ b/vendor/github.com/evertras/bubble-table/table/footer.go @@ -34,7 +34,15 @@ func (m Model) renderFooter(width int, includeTop bool) string { // paged feature enabled if m.pageSize != 0 { - sections = append(sections, fmt.Sprintf("%d/%d", m.CurrentPage(), m.MaxPages())) + str := fmt.Sprintf("%d/%d", m.CurrentPage(), m.MaxPages()) + if m.filtered && m.filterTextInput.Focused() { + // Need to apply inline style here in case of filter input cursor, because + // the input cursor resets the style after rendering. Note that Inline(true) + // creates a copy, so it's safe to use here without mutating the underlying + // base style. + str = m.baseStyle.Inline(true).Render(str) + } + sections = append(sections, str) } footerText := strings.Join(sections, " ") diff --git a/vendor/github.com/evertras/bubble-table/table/header.go b/vendor/github.com/evertras/bubble-table/table/header.go index bff033c63fdc2..fdd5ac0bde2f4 100644 --- a/vendor/github.com/evertras/bubble-table/table/header.go +++ b/vendor/github.com/evertras/bubble-table/table/header.go @@ -4,7 +4,8 @@ import "github.com/charmbracelet/lipgloss" // This is long and could use some refactoring in the future, but unsure of how // to pick it apart right now. -// nolint: funlen, cyclop +// +//nolint:funlen,cyclop func (m Model) renderHeaders() string { headerStrings := []string{} diff --git a/vendor/github.com/evertras/bubble-table/table/model.go b/vendor/github.com/evertras/bubble-table/table/model.go index 46709b9cce7ab..be25cf21603a4 100644 --- a/vendor/github.com/evertras/bubble-table/table/model.go +++ b/vendor/github.com/evertras/bubble-table/table/model.go @@ -20,6 +20,10 @@ type Model struct { columns []Column rows []Row + // Caches for optimizations + visibleRowCacheUpdated bool + visibleRowCache []Row + // Shown when data is missing from a row missingDataIndicator interface{} diff --git a/vendor/github.com/evertras/bubble-table/table/options.go b/vendor/github.com/evertras/bubble-table/table/options.go index 2383053c4b8e7..05f6fb8f50b9c 100644 --- a/vendor/github.com/evertras/bubble-table/table/options.go +++ b/vendor/github.com/evertras/bubble-table/table/options.go @@ -32,6 +32,7 @@ func (m Model) HeaderStyle(style lipgloss.Style) Model { // WithRows sets the rows to show as data in the table. func (m Model) WithRows(rows []Row) Model { m.rows = rows + m.visibleRowCacheUpdated = false if m.rowCursorIndex >= len(m.rows) { m.rowCursorIndex = len(m.rows) - 1 @@ -129,6 +130,7 @@ func (m Model) Focused(focused bool) Model { // Filtered allows the table to show rows that match the filter. func (m Model) Filtered(filtered bool) Model { m.filtered = filtered + m.visibleRowCacheUpdated = false return m } @@ -279,7 +281,12 @@ func (m Model) WithColumns(columns []Column) Model { // filtering rather than using the built-in default. This allows for external // text input controls to be used. func (m Model) WithFilterInput(input textinput.Model) Model { + if m.filterTextInput.Value() != input.Value() { + m.pageFirst() + } + m.filterTextInput = input + m.visibleRowCacheUpdated = false return m } @@ -288,8 +295,13 @@ func (m Model) WithFilterInput(input textinput.Model) Model { // applying it as if the user had typed it in. Useful for external filter inputs // that are not necessarily a text input. func (m Model) WithFilterInputValue(value string) Model { + if m.filterTextInput.Value() != value { + m.pageFirst() + } + m.filterTextInput.SetValue(value) m.filterTextInput.Blur() + m.visibleRowCacheUpdated = false return m } @@ -364,3 +376,18 @@ func (m Model) WithMissingDataIndicatorStyled(styled StyledCell) Model { return m } + +// WithAllRowsDeselected deselects any rows that are currently selected. +func (m Model) WithAllRowsDeselected() Model { + rows := m.GetVisibleRows() + + for i, row := range rows { + if row.selected { + rows[i] = row.Selected(false) + } + } + + m.rows = rows + + return m +} diff --git a/vendor/github.com/evertras/bubble-table/table/pagination.go b/vendor/github.com/evertras/bubble-table/table/pagination.go index 9300343f49a4b..6fce9b5191d71 100644 --- a/vendor/github.com/evertras/bubble-table/table/pagination.go +++ b/vendor/github.com/evertras/bubble-table/table/pagination.go @@ -14,11 +14,13 @@ func (m *Model) CurrentPage() int { // MaxPages returns the maximum number of pages that are visible. func (m *Model) MaxPages() int { - if m.pageSize == 0 || len(m.GetVisibleRows()) == 0 { + totalRows := len(m.GetVisibleRows()) + + if m.pageSize == 0 || totalRows == 0 { return 1 } - return (len(m.GetVisibleRows())-1)/m.pageSize + 1 + return (totalRows-1)/m.pageSize + 1 } // TotalRows returns the current total row count of the table. If the table is diff --git a/vendor/github.com/evertras/bubble-table/table/query.go b/vendor/github.com/evertras/bubble-table/table/query.go index 16b08fddc05fb..669915e729b85 100644 --- a/vendor/github.com/evertras/bubble-table/table/query.go +++ b/vendor/github.com/evertras/bubble-table/table/query.go @@ -25,6 +25,12 @@ func (m *Model) GetIsFilterActive() bool { return m.filterTextInput.Value() != "" } +// GetIsFilterInputFocused returns true if the table's built-in filter input is +// currently focused. +func (m *Model) GetIsFilterInputFocused() bool { + return m.filterTextInput.Focused() +} + // GetCurrentFilter returns the current filter text being applied, or an empty // string if none is applied. func (m *Model) GetCurrentFilter() string { @@ -32,7 +38,11 @@ func (m *Model) GetCurrentFilter() string { } // GetVisibleRows returns sorted and filtered rows. -func (m Model) GetVisibleRows() []Row { +func (m *Model) GetVisibleRows() []Row { + if m.visibleRowCacheUpdated { + return m.visibleRowCache + } + rows := make([]Row, len(m.rows)) copy(rows, m.rows) if m.filtered { @@ -40,6 +50,9 @@ func (m Model) GetVisibleRows() []Row { } rows = getSortedRows(m.sortOrder, rows) + m.visibleRowCache = rows + m.visibleRowCacheUpdated = true + return rows } diff --git a/vendor/github.com/evertras/bubble-table/table/row.go b/vendor/github.com/evertras/bubble-table/table/row.go index 2b08f3c539c5a..d063f68de080e 100644 --- a/vendor/github.com/evertras/bubble-table/table/row.go +++ b/vendor/github.com/evertras/bubble-table/table/row.go @@ -43,7 +43,7 @@ func (r Row) WithStyle(style lipgloss.Style) Row { return r } -// nolint: nestif // This has many ifs, but they're short +//nolint:nestif // This has many ifs, but they're short func (m Model) renderRowColumnData(row Row, column Column, rowStyle lipgloss.Style, borderStyle lipgloss.Style) string { cellStyle := rowStyle.Copy().Inherit(column.style).Inherit(m.baseStyle) @@ -71,12 +71,18 @@ func (m Model) renderRowColumnData(row Row, column Column, rowStyle lipgloss.Sty data = "" } + fmtString := "%v" + + if column.fmtString != "" { + fmtString = column.fmtString + } + switch entry := data.(type) { case StyledCell: - str = fmt.Sprintf("%v", entry.Data) + str = fmt.Sprintf(fmtString, entry.Data) cellStyle = entry.Style.Copy().Inherit(cellStyle) default: - str = fmt.Sprintf("%v", entry) + str = fmt.Sprintf(fmtString, entry) } } @@ -88,7 +94,8 @@ func (m Model) renderRowColumnData(row Row, column Column, rowStyle lipgloss.Sty // This is long and could use some refactoring in the future, but not quite sure // how to pick it apart yet. -// nolint: funlen, cyclop, gocognit +// +//nolint:funlen, cyclop, gocognit func (m Model) renderRow(rowIndex int, last bool) string { numColumns := len(m.columns) row := m.GetVisibleRows()[rowIndex] @@ -182,7 +189,8 @@ func (m Model) renderRow(rowIndex int, last bool) string { return lipgloss.JoinHorizontal(lipgloss.Bottom, columnStrings...) } -// Selected sets whether the row is selected or not. +// Selected returns a copy of the row that's set to be selected or deselected. +// The old row is not changed in-place. func (r Row) Selected(selected bool) Row { r.selected = selected diff --git a/vendor/github.com/evertras/bubble-table/table/sort.go b/vendor/github.com/evertras/bubble-table/table/sort.go index 494e34d48fe66..9a282cbdaefbf 100644 --- a/vendor/github.com/evertras/bubble-table/table/sort.go +++ b/vendor/github.com/evertras/bubble-table/table/sort.go @@ -34,6 +34,8 @@ func (m Model) SortByAsc(columnKey string) Model { }, } + m.visibleRowCacheUpdated = false + return m } @@ -49,6 +51,8 @@ func (m Model) SortByDesc(columnKey string) Model { }, } + m.visibleRowCacheUpdated = false + return m } @@ -62,6 +66,8 @@ func (m Model) ThenSortByAsc(columnKey string) Model { }, }, m.sortOrder...) + m.visibleRowCacheUpdated = false + return m } @@ -75,6 +81,8 @@ func (m Model) ThenSortByDesc(columnKey string) Model { }, }, m.sortOrder...) + m.visibleRowCacheUpdated = false + return m } diff --git a/vendor/github.com/evertras/bubble-table/table/update.go b/vendor/github.com/evertras/bubble-table/table/update.go index 071cfe6c6a15d..75998deb558ed 100644 --- a/vendor/github.com/evertras/bubble-table/table/update.go +++ b/vendor/github.com/evertras/bubble-table/table/update.go @@ -38,6 +38,7 @@ func (m *Model) toggleSelect() { rows[m.rowCursorIndex].selected = !currentSelectedState m.rows = rows + m.visibleRowCacheUpdated = false m.appendUserEvent(UserEventRowSelectToggled{ RowIndex: m.rowCursorIndex, @@ -55,11 +56,14 @@ func (m Model) updateFilterTextInput(msg tea.Msg) (Model, tea.Cmd) { } m.filterTextInput, cmd = m.filterTextInput.Update(msg) m.pageFirst() + m.visibleRowCacheUpdated = false return m, cmd } -// nolint: cyclop // This is a series of Matches tests with minimal logic +// This is a series of Matches tests with minimal logic +// +//nolint:cyclop func (m *Model) handleKeypress(msg tea.KeyMsg) { previousRowIndex := m.rowCursorIndex @@ -93,9 +97,11 @@ func (m *Model) handleKeypress(msg tea.KeyMsg) { if key.Matches(msg, m.keyMap.Filter) { m.filterTextInput.Focus() + m.appendUserEvent(UserEventFilterInputFocused{}) } if key.Matches(msg, m.keyMap.FilterClear) { + m.visibleRowCacheUpdated = false m.filterTextInput.Reset() } @@ -124,7 +130,14 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { } if m.filterTextInput.Focused() { - return m.updateFilterTextInput(msg) + var cmd tea.Cmd + m, cmd = m.updateFilterTextInput(msg) + + if !m.filterTextInput.Focused() { + m.appendUserEvent(UserEventFilterInputUnfocused{}) + } + + return m, cmd } switch msg := msg.(type) { diff --git a/vendor/github.com/evertras/bubble-table/table/view.go b/vendor/github.com/evertras/bubble-table/table/view.go index 8dcda1dc296ac..e6bb9d2ff7464 100644 --- a/vendor/github.com/evertras/bubble-table/table/view.go +++ b/vendor/github.com/evertras/bubble-table/table/view.go @@ -25,7 +25,7 @@ func (m Model) View() string { if m.headerVisible { rowStrs = append(rowStrs, headers) } else if endRowIndex-startRowIndex > 0 { - // nolint: gomnd // This is just getting the first newlined substring + //nolint: gomnd // This is just getting the first newlined substring split := strings.SplitN(headers, "\n", 2) rowStrs = append(rowStrs, split[0]) } diff --git a/vendor/github.com/mattn/go-localereader/README.md b/vendor/github.com/mattn/go-localereader/README.md new file mode 100644 index 0000000000000..9e1095327892d --- /dev/null +++ b/vendor/github.com/mattn/go-localereader/README.md @@ -0,0 +1,23 @@ +# go-localereader + +CodePage decoder for Windows + +## Usage + +``` +io.Copy(os.Stdout, localereader.NewAcpReader(bytes.Reader(bytesSjis))) +``` + +## Installation + +``` +$ go get github.com/mattn/go-localereader +``` + +## License + +MIT + +## Author + +Yasuhiro Matsumoto (a.k.a. mattn) diff --git a/vendor/github.com/mattn/go-localereader/localereader.go b/vendor/github.com/mattn/go-localereader/localereader.go new file mode 100644 index 0000000000000..47a31375ee373 --- /dev/null +++ b/vendor/github.com/mattn/go-localereader/localereader.go @@ -0,0 +1,19 @@ +package localereader + +import ( + "bytes" + "io" +) + +func NewReader(r io.Reader) io.Reader { + return newReader(r) +} + +func UTF8(b []byte) ([]byte, error) { + var buf bytes.Buffer + n, err := io.Copy(&buf, newReader(bytes.NewReader(b))) + if err != nil { + return nil, err + } + return buf.Bytes()[:n], nil +} diff --git a/vendor/github.com/mattn/go-localereader/localereader_unix.go b/vendor/github.com/mattn/go-localereader/localereader_unix.go new file mode 100644 index 0000000000000..d85e12e400ce9 --- /dev/null +++ b/vendor/github.com/mattn/go-localereader/localereader_unix.go @@ -0,0 +1,11 @@ +// +build !windows + +package localereader + +import ( + "io" +) + +func newReader(r io.Reader) io.Reader { + return r +} diff --git a/vendor/github.com/mattn/go-localereader/localereader_windows.go b/vendor/github.com/mattn/go-localereader/localereader_windows.go new file mode 100644 index 0000000000000..57c238fe806e5 --- /dev/null +++ b/vendor/github.com/mattn/go-localereader/localereader_windows.go @@ -0,0 +1,85 @@ +// +build windows + +package localereader + +import ( + "io" + "syscall" + "unicode/utf8" + "unsafe" + + "golang.org/x/text/transform" +) + +const ( + CP_ACP = 0 + MB_ERR_INVALID_CHARS = 8 +) + +var ( + modkernel32 = syscall.NewLazyDLL("kernel32.dll") + procMultiByteToWideChar = modkernel32.NewProc("MultiByteToWideChar") + procIsDBCSLeadByte = modkernel32.NewProc("IsDBCSLeadByte") +) + +type codepageDecoder struct { + transform.NopResetter + + cp int +} + +func (codepageDecoder) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { + r, size := rune(0), 0 +loop: + for ; nSrc < len(src); nSrc += size { + switch c0 := src[nSrc]; { + case c0 < utf8.RuneSelf: + r, size = rune(c0), 1 + + default: + br, _, _ := procIsDBCSLeadByte.Call(uintptr(src[nSrc])) + if br == 0 { + r = rune(src[nSrc]) + size = 1 + break + } + if nSrc >= len(src)-1 { + r = rune(src[nSrc]) + size = 1 + break + } + n, _, _ := procMultiByteToWideChar.Call(CP_ACP, 0, uintptr(unsafe.Pointer(&src[nSrc])), uintptr(2), uintptr(0), 0) + if n <= 0 { + err = syscall.GetLastError() + break + } + var us [1]uint16 + rc, _, _ := procMultiByteToWideChar.Call(CP_ACP, 0, uintptr(unsafe.Pointer(&src[nSrc])), uintptr(2), uintptr(unsafe.Pointer(&us[0])), 1) + if rc == 0 { + size = 1 + break + } + r = rune(us[0]) + size = 2 + } + if nDst+utf8.RuneLen(r) > len(dst) { + err = transform.ErrShortDst + break loop + } + nDst += utf8.EncodeRune(dst[nDst:], r) + } + return nDst, nSrc, err + +} + +func newReader(r io.Reader) io.Reader { + return transform.NewReader(r, NewAcpDecoder()) +} + +func NewCodePageDecoder(cp int) transform.Transformer { + return &codepageDecoder{cp: cp} +} + +func NewAcpDecoder() transform.Transformer { + return &codepageDecoder{cp: CP_ACP} +} diff --git a/vendor/github.com/mattn/go-runewidth/.travis.yml b/vendor/github.com/mattn/go-runewidth/.travis.yml deleted file mode 100644 index 6a21813a3e30b..0000000000000 --- a/vendor/github.com/mattn/go-runewidth/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: go -sudo: false -go: - - 1.13.x - - tip - -before_install: - - go get -t -v ./... - -script: - - go generate - - git diff --cached --exit-code - - ./go.test.sh - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/mattn/go-runewidth/README.md b/vendor/github.com/mattn/go-runewidth/README.md index aa56ab96c2b9d..5e2cfd98cb695 100644 --- a/vendor/github.com/mattn/go-runewidth/README.md +++ b/vendor/github.com/mattn/go-runewidth/README.md @@ -1,7 +1,7 @@ go-runewidth ============ -[![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth) +[![Build Status](https://github.com/mattn/go-runewidth/workflows/test/badge.svg?branch=master)](https://github.com/mattn/go-runewidth/actions?query=workflow%3Atest) [![Codecov](https://codecov.io/gh/mattn/go-runewidth/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-runewidth) [![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth) [![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth) diff --git a/vendor/github.com/mattn/go-runewidth/go.test.sh b/vendor/github.com/mattn/go-runewidth/go.test.sh deleted file mode 100644 index 012162b077c97..0000000000000 --- a/vendor/github.com/mattn/go-runewidth/go.test.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -set -e -echo "" > coverage.txt - -for d in $(go list ./... | grep -v vendor); do - go test -race -coverprofile=profile.out -covermode=atomic "$d" - if [ -f profile.out ]; then - cat profile.out >> coverage.txt - rm profile.out - fi -done diff --git a/vendor/github.com/mattn/go-runewidth/runewidth.go b/vendor/github.com/mattn/go-runewidth/runewidth.go index 3d7fa560b87fb..7dfbb3be91d5f 100644 --- a/vendor/github.com/mattn/go-runewidth/runewidth.go +++ b/vendor/github.com/mattn/go-runewidth/runewidth.go @@ -2,6 +2,7 @@ package runewidth import ( "os" + "strings" "github.com/rivo/uniseg" ) @@ -34,7 +35,13 @@ func handleEnv() { EastAsianWidth = env == "1" } // update DefaultCondition - DefaultCondition.EastAsianWidth = EastAsianWidth + if DefaultCondition.EastAsianWidth != EastAsianWidth { + DefaultCondition.EastAsianWidth = EastAsianWidth + if len(DefaultCondition.combinedLut) > 0 { + DefaultCondition.combinedLut = DefaultCondition.combinedLut[:0] + CreateLUT() + } + } } type interval struct { @@ -89,6 +96,7 @@ var nonprint = table{ // Condition have flag EastAsianWidth whether the current locale is CJK or not. type Condition struct { + combinedLut []byte EastAsianWidth bool StrictEmojiNeutral bool } @@ -104,10 +112,16 @@ func NewCondition() *Condition { // RuneWidth returns the number of cells in r. // See http://www.unicode.org/reports/tr11/ func (c *Condition) RuneWidth(r rune) int { + if r < 0 || r > 0x10FFFF { + return 0 + } + if len(c.combinedLut) > 0 { + return int(c.combinedLut[r>>1]>>(uint(r&1)*4)) & 3 + } // optimized version, verified by TestRuneWidthChecksums() if !c.EastAsianWidth { switch { - case r < 0x20 || r > 0x10FFFF: + case r < 0x20: return 0 case (r >= 0x7F && r <= 0x9F) || r == 0xAD: // nonprint return 0 @@ -124,7 +138,7 @@ func (c *Condition) RuneWidth(r rune) int { } } else { switch { - case r < 0 || r > 0x10FFFF || inTables(r, nonprint, combining): + case inTables(r, nonprint, combining): return 0 case inTable(r, narrow): return 1 @@ -138,6 +152,27 @@ func (c *Condition) RuneWidth(r rune) int { } } +// CreateLUT will create an in-memory lookup table of 557056 bytes for faster operation. +// This should not be called concurrently with other operations on c. +// If options in c is changed, CreateLUT should be called again. +func (c *Condition) CreateLUT() { + const max = 0x110000 + lut := c.combinedLut + if len(c.combinedLut) != 0 { + // Remove so we don't use it. + c.combinedLut = nil + } else { + lut = make([]byte, max/2) + } + for i := range lut { + i32 := int32(i * 2) + x0 := c.RuneWidth(i32) + x1 := c.RuneWidth(i32 + 1) + lut[i] = uint8(x0) | uint8(x1)<<4 + } + c.combinedLut = lut +} + // StringWidth return width as you can see func (c *Condition) StringWidth(s string) (width int) { g := uniseg.NewGraphemes(s) @@ -180,11 +215,47 @@ func (c *Condition) Truncate(s string, w int, tail string) string { return s[:pos] + tail } +// TruncateLeft cuts w cells from the beginning of the `s`. +func (c *Condition) TruncateLeft(s string, w int, prefix string) string { + if c.StringWidth(s) <= w { + return prefix + } + + var width int + pos := len(s) + + g := uniseg.NewGraphemes(s) + for g.Next() { + var chWidth int + for _, r := range g.Runes() { + chWidth = c.RuneWidth(r) + if chWidth > 0 { + break // See StringWidth() for details. + } + } + + if width+chWidth > w { + if width < w { + _, pos = g.Positions() + prefix += strings.Repeat(" ", width+chWidth-w) + } else { + pos, _ = g.Positions() + } + + break + } + + width += chWidth + } + + return prefix + s[pos:] +} + // Wrap return string wrapped with w cells func (c *Condition) Wrap(s string, w int) string { width := 0 out := "" - for _, r := range []rune(s) { + for _, r := range s { cw := c.RuneWidth(r) if r == '\n' { out += string(r) @@ -257,6 +328,11 @@ func Truncate(s string, w int, tail string) string { return DefaultCondition.Truncate(s, w, tail) } +// TruncateLeft cuts w cells from the beginning of the `s`. +func TruncateLeft(s string, w int, prefix string) string { + return DefaultCondition.TruncateLeft(s, w, prefix) +} + // Wrap return string wrapped with w cells func Wrap(s string, w int) string { return DefaultCondition.Wrap(s, w) @@ -271,3 +347,12 @@ func FillLeft(s string, w int) string { func FillRight(s string, w int) string { return DefaultCondition.FillRight(s, w) } + +// CreateLUT will create an in-memory lookup table of 557055 bytes for faster operation. +// This should not be called concurrently with other operations. +func CreateLUT() { + if len(DefaultCondition.combinedLut) > 0 { + return + } + DefaultCondition.CreateLUT() +} diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go b/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go index 7d99f6e521037..84b6528dfe914 100644 --- a/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go +++ b/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go @@ -1,3 +1,4 @@ +//go:build appengine // +build appengine package runewidth diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_js.go b/vendor/github.com/mattn/go-runewidth/runewidth_js.go index c5fdf40baa0e2..c2abbc2db30be 100644 --- a/vendor/github.com/mattn/go-runewidth/runewidth_js.go +++ b/vendor/github.com/mattn/go-runewidth/runewidth_js.go @@ -1,5 +1,5 @@ -// +build js -// +build !appengine +//go:build js && !appengine +// +build js,!appengine package runewidth diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_posix.go b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go index 480ad748538f8..5a31d738ecce4 100644 --- a/vendor/github.com/mattn/go-runewidth/runewidth_posix.go +++ b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go @@ -1,6 +1,5 @@ -// +build !windows -// +build !js -// +build !appengine +//go:build !windows && !js && !appengine +// +build !windows,!js,!appengine package runewidth diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_windows.go b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go index d6a61777d7b1c..5f987a310fe48 100644 --- a/vendor/github.com/mattn/go-runewidth/runewidth_windows.go +++ b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go @@ -1,5 +1,5 @@ -// +build windows -// +build !appengine +//go:build windows && !appengine +// +build windows,!appengine package runewidth diff --git a/vendor/github.com/muesli/cancelreader/cancelreader.go b/vendor/github.com/muesli/cancelreader/cancelreader.go index 61280a6168d6d..18d38255318f9 100644 --- a/vendor/github.com/muesli/cancelreader/cancelreader.go +++ b/vendor/github.com/muesli/cancelreader/cancelreader.go @@ -34,8 +34,8 @@ type File interface { // false. However, after calling Cancel(), new Read() calls immediately return // errCanceled and don't consume any data anymore. type fallbackCancelReader struct { - r io.Reader - canceled bool + r io.Reader + cancelMixin } // newFallbackCancelReader is a fallback for NewReader that cannot actually @@ -46,16 +46,25 @@ func newFallbackCancelReader(reader io.Reader) (CancelReader, error) { } func (r *fallbackCancelReader) Read(data []byte) (int, error) { - if r.canceled { + if r.isCanceled() { return 0, ErrCanceled } - return r.r.Read(data) + n, err := r.r.Read(data) + /* + If the underlying reader is a blocking reader (e.g. an open connection), + it might happen that 1 goroutine cancels the reader while its stuck in + the read call waiting for something. + If that happens, we should still cancel the read. + */ + if r.isCanceled() { + return 0, ErrCanceled + } + return n, err // nolint: wrapcheck } func (r *fallbackCancelReader) Cancel() bool { - r.canceled = true - + r.setCanceled() return false } diff --git a/vendor/github.com/muesli/cancelreader/cancelreader_bsd.go b/vendor/github.com/muesli/cancelreader/cancelreader_bsd.go index 7450136964cce..3ddb6cff0b30b 100644 --- a/vendor/github.com/muesli/cancelreader/cancelreader_bsd.go +++ b/vendor/github.com/muesli/cancelreader/cancelreader_bsd.go @@ -42,6 +42,7 @@ func NewReader(reader io.Reader) (CancelReader, error) { r.cancelSignalReader, r.cancelSignalWriter, err = os.Pipe() if err != nil { + _ = unix.Close(kQueue) return nil, err } diff --git a/vendor/github.com/muesli/cancelreader/cancelreader_default.go b/vendor/github.com/muesli/cancelreader/cancelreader_default.go index f0d9eae6852b3..8e275fa4411b3 100644 --- a/vendor/github.com/muesli/cancelreader/cancelreader_default.go +++ b/vendor/github.com/muesli/cancelreader/cancelreader_default.go @@ -3,9 +3,7 @@ package cancelreader -import ( - "io" -) +import "io" // NewReader returns a fallbackCancelReader that satisfies the CancelReader but // does not actually support cancellation. diff --git a/vendor/github.com/muesli/cancelreader/cancelreader_linux.go b/vendor/github.com/muesli/cancelreader/cancelreader_linux.go index 6f4e8c7929a89..09f7369f31a27 100644 --- a/vendor/github.com/muesli/cancelreader/cancelreader_linux.go +++ b/vendor/github.com/muesli/cancelreader/cancelreader_linux.go @@ -37,6 +37,7 @@ func NewReader(reader io.Reader) (CancelReader, error) { r.cancelSignalReader, r.cancelSignalWriter, err = os.Pipe() if err != nil { + _ = unix.Close(epoll) return nil, err } @@ -45,7 +46,8 @@ func NewReader(reader io.Reader) (CancelReader, error) { Fd: int32(file.Fd()), }) if err != nil { - return nil, fmt.Errorf("add reader to epoll interrest list") + _ = unix.Close(epoll) + return nil, fmt.Errorf("add reader to epoll interest list") } err = unix.EpollCtl(epoll, unix.EPOLL_CTL_ADD, int(r.cancelSignalReader.Fd()), &unix.EpollEvent{ @@ -53,7 +55,8 @@ func NewReader(reader io.Reader) (CancelReader, error) { Fd: int32(r.cancelSignalReader.Fd()), }) if err != nil { - return nil, fmt.Errorf("add reader to epoll interrest list") + _ = unix.Close(epoll) + return nil, fmt.Errorf("add reader to epoll interest list") } return r, nil diff --git a/vendor/github.com/muesli/termenv/README.md b/vendor/github.com/muesli/termenv/README.md index aeece334776c6..29dcf01764edc 100644 --- a/vendor/github.com/muesli/termenv/README.md +++ b/vendor/github.com/muesli/termenv/README.md @@ -31,17 +31,15 @@ color conversions. go get github.com/muesli/termenv ``` -## Query Terminal Support - -`termenv` can query the terminal it is running in, so you can safely use -advanced features, like RGB colors. `ColorProfile` returns the color profile -supported by the terminal: +## Usage ```go -profile := termenv.ColorProfile() +output := termenv.NewOutput(os.Stdout) ``` -This returns one of the supported color profiles: +`termenv` queries the terminal's capabilities it is running in, so you can +safely use advanced features, like RGB colors or ANSI styles. `output.Profile` +returns the supported profile: - `termenv.Ascii` - no ANSI support detected, ASCII only - `termenv.ANSI` - 16 color ANSI support @@ -57,39 +55,46 @@ app is running in a light- or dark-themed environment: ```go // Returns terminal's foreground color -color := termenv.ForegroundColor() +color := output.ForegroundColor() // Returns terminal's background color -color := termenv.BackgroundColor() +color := output.BackgroundColor() // Returns whether terminal uses a dark-ish background -darkTheme := termenv.HasDarkBackground() +darkTheme := output.HasDarkBackground() +``` + +### Manual Profile Selection + +If you don't want to rely on the automatic detection, you can manually select +the profile you want to use: + +```go +output := termenv.NewOutput(os.Stdout, termenv.WithProfile(termenv.TrueColor)) ``` ## Colors -`termenv` supports multiple color profiles: ANSI (16 colors), ANSI Extended -(256 colors), and TrueColor (24-bit RGB). Colors will automatically be degraded -to the best matching available color in the desired profile: +`termenv` supports multiple color profiles: Ascii (black & white only), +ANSI (16 colors), ANSI Extended (256 colors), and TrueColor (24-bit RGB). Colors +will automatically be degraded to the best matching available color in the +desired profile: `TrueColor` => `ANSI 256 Colors` => `ANSI 16 Colors` => `Ascii` ```go -s := termenv.String("Hello World") - -// Retrieve color profile supported by terminal -p := termenv.ColorProfile() +s := output.String("Hello World") // Supports hex values // Will automatically degrade colors on terminals not supporting RGB -s.Foreground(p.Color("#abcdef")) +s.Foreground(output.Color("#abcdef")) // but also supports ANSI colors (0-255) -s.Background(p.Color("69")) +s.Background(output.Color("69")) // ...or the color.Color interface -s.Foreground(p.FromColor(color.RGBA{255, 128, 0, 255})) +s.Foreground(output.FromColor(color.RGBA{255, 128, 0, 255})) // Combine fore- & background colors -s.Foreground(p.Color("#ffffff")).Background(p.Color("#0000ff")) +s.Foreground(output.Color("#ffffff")).Background(output.Color("#0000ff")) // Supports the fmt.Stringer interface fmt.Println(s) @@ -100,7 +105,7 @@ fmt.Println(s) You can use a chainable syntax to compose your own styles: ```go -s := termenv.String("foobar") +s := output.String("foobar") // Text styles s.Bold() @@ -122,9 +127,11 @@ s.Bold().Underline() ## Template Helpers +`termenv` provides a set of helper functions to style your Go templates: + ```go // load template helpers -f := termenv.TemplateFuncs(termenv.ColorProfile()) +f := output.TemplateFuncs() tpl := template.New("tpl").Funcs(f) // apply bold style in a template @@ -153,155 +160,150 @@ Other available helper functions are: `Faint`, `Italic`, `CrossOut`, ```go // Move the cursor to a given position -termenv.MoveCursor(row, column) +output.MoveCursor(row, column) // Save the cursor position -termenv.SaveCursorPosition() +output.SaveCursorPosition() // Restore a saved cursor position -termenv.RestoreCursorPosition() +output.RestoreCursorPosition() // Move the cursor up a given number of lines -termenv.CursorUp(n) +output.CursorUp(n) // Move the cursor down a given number of lines -termenv.CursorDown(n) +output.CursorDown(n) // Move the cursor up a given number of lines -termenv.CursorForward(n) +output.CursorForward(n) // Move the cursor backwards a given number of cells -termenv.CursorBack(n) +output.CursorBack(n) // Move the cursor down a given number of lines and place it at the beginning // of the line -termenv.CursorNextLine(n) +output.CursorNextLine(n) // Move the cursor up a given number of lines and place it at the beginning of // the line -termenv.CursorPrevLine(n) +output.CursorPrevLine(n) ``` ## Screen ```go // Reset the terminal to its default style, removing any active styles -termenv.Reset() +output.Reset() // RestoreScreen restores a previously saved screen state -termenv.RestoreScreen() +output.RestoreScreen() // SaveScreen saves the screen state -termenv.SaveScreen() +output.SaveScreen() // Switch to the altscreen. The former view can be restored with ExitAltScreen() -termenv.AltScreen() +output.AltScreen() // Exit the altscreen and return to the former terminal view -termenv.ExitAltScreen() +output.ExitAltScreen() // Clear the visible portion of the terminal -termenv.ClearScreen() +output.ClearScreen() // Clear the current line -termenv.ClearLine() +output.ClearLine() // Clear a given number of lines -termenv.ClearLines(n) +output.ClearLines(n) // Set the scrolling region of the terminal -termenv.ChangeScrollingRegion(top, bottom) +output.ChangeScrollingRegion(top, bottom) // Insert the given number of lines at the top of the scrollable region, pushing // lines below down -termenv.InsertLines(n) +output.InsertLines(n) // Delete the given number of lines, pulling any lines in the scrollable region // below up -termenv.DeleteLines(n) +output.DeleteLines(n) ``` ## Session ```go // SetWindowTitle sets the terminal window title -termenv.SetWindowTitle(title) +output.SetWindowTitle(title) // SetForegroundColor sets the default foreground color -termenv.SetForegroundColor(color) +output.SetForegroundColor(color) // SetBackgroundColor sets the default background color -termenv.SetBackgroundColor(color) +output.SetBackgroundColor(color) // SetCursorColor sets the cursor color -termenv.SetCursorColor(color) +output.SetCursorColor(color) // Hide the cursor -termenv.HideCursor() +output.HideCursor() // Show the cursor -termenv.ShowCursor() +output.ShowCursor() + +// Copy to clipboard +output.Copy(message) + +// Copy to primary clipboard (X11) +output.CopyPrimary(message) + +// Trigger notification +output.Notify(title, body) ``` ## Mouse ```go // Enable X10 mouse mode, only button press events are sent -termenv.EnableMousePress() +output.EnableMousePress() // Disable X10 mouse mode -termenv.DisableMousePress() +output.DisableMousePress() // Enable Mouse Tracking mode -termenv.EnableMouse() +output.EnableMouse() // Disable Mouse Tracking mode -termenv.DisableMouse() +output.DisableMouse() // Enable Hilite Mouse Tracking mode -termenv.EnableMouseHilite() +output.EnableMouseHilite() // Disable Hilite Mouse Tracking mode -termenv.DisableMouseHilite() +output.DisableMouseHilite() // Enable Cell Motion Mouse Tracking mode -termenv.EnableMouseCellMotion() +output.EnableMouseCellMotion() // Disable Cell Motion Mouse Tracking mode -termenv.DisableMouseCellMotion() +output.DisableMouseCellMotion() // Enable All Motion Mouse mode -termenv.EnableMouseAllMotion() +output.EnableMouseAllMotion() // Disable All Motion Mouse mode -termenv.DisableMouseAllMotion() +output.DisableMouseAllMotion() ``` -## Optional Feature Support - -| Terminal | Alt Screen | Query Color Scheme | Query Cursor Position | Set Window Title | Change Cursor Color | Change Default Foreground Setting | Change Default Background Setting | -| ---------------- | :--------: | :----------------: | :-------------------: | :--------------: | :-----------------: | :-------------------------------: | :-------------------------------: | -| alacritty | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| foot | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| kitty | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| Konsole | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | -| rxvt | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | -| screen | ✅ | ⛔[^mux] | ✅ | ✅ | ❌ | ❌ | ✅ | -| st | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| tmux | ✅ | ⛔[^mux] | ✅ | ✅ | ✅ | ✅ | ✅ | -| vte-based[^vte] | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| wezterm | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| xterm | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | -| Linux Console | ✅ | ❌ | ✅ | ⛔ | ❌ | ❌ | ❌ | -| Apple Terminal | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | -| iTerm | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | -| Windows cmd | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | -| Windows Terminal | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | +## Bracketed Paste -[^vte]: This covers all vte-based terminals, including Gnome Terminal, guake, Pantheon Terminal, Terminator, Tilix, XFCE Terminal. -[^mux]: Unavailable as multiplexers (like tmux or screen) can be connected to multiple terminals (with different color settings) at the same time. +```go +// Enables bracketed paste mode +termenv.EnableBracketedPaste() -You can help improve this list! Check out [how to](ansi_compat.md) and open an issue or pull request. +// Disables bracketed paste mode +termenv.DisableBracketedPaste() +``` + +## Terminal Feature Support ### Color Support @@ -309,6 +311,73 @@ You can help improve this list! Check out [how to](ansi_compat.md) and open an i - 8-bit (256): rxvt, screen, xterm, Apple Terminal - 4-bit (16): Linux Console +### Control Sequences + +
+Click to show feature matrix + +| Terminal | Query Color Scheme | Query Cursor Position | Set Window Title | Change Cursor Color | Change Default Foreground Setting | Change Default Background Setting | Bracketed Paste | Extended Mouse (SGR) | Pixels Mouse (SGR-Pixels) | +| ---------------- | :----------------: | :-------------------: | :--------------: | :-----------------: | :-------------------------------: | :-------------------------------: | :-------------: | :------------------: | :-----------------------: | +| alacritty | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | +| foot | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| kitty | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Konsole | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | +| rxvt | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | +| urxvt | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | +| screen | ⛔[^mux] | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | +| st | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | +| tmux | ⛔[^mux] | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | +| vte-based[^vte] | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | +| wezterm | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| xterm | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | +| Linux Console | ❌ | ✅ | ⛔ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| Apple Terminal | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | +| iTerm | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | +| Windows cmd | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| Windows Terminal | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | + +[^vte]: This covers all vte-based terminals, including Gnome Terminal, guake, Pantheon Terminal, Terminator, Tilix, XFCE Terminal. +[^mux]: Unavailable as multiplexers (like tmux or screen) can be connected to multiple terminals (with different color settings) at the same time. + +You can help improve this list! Check out [how to](ansi_compat.md) and open an issue or pull request. + +
+ +### System Commands + +
+Click to show feature matrix + +| Terminal | Copy to Clipboard (OSC52) | Hyperlinks (OSC8) | Notifications (OSC777) | +| ---------------- | :-----------------------: | :---------------: | :--------------------: | +| alacritty | ✅ | ❌[^alacritty] | ❌ | +| foot | ✅ | ✅ | ✅ | +| kitty | ✅ | ✅ | ✅ | +| Konsole | ❌[^konsole] | ✅ | ❌ | +| rxvt | ❌ | ❌ | ❌ | +| urxvt | ✅[^urxvt] | ❌ | ✅ | +| screen | ✅ | ❌[^screen] | ❌ | +| st | ✅ | ❌ | ❌ | +| tmux | ✅ | ❌[^tmux] | ❌ | +| vte-based[^vte] | ❌[^vte] | ✅ | ❌ | +| wezterm | ✅ | ✅ | ❌ | +| xterm | ✅ | ❌ | ❌ | +| Linux Console | ⛔ | ⛔ | ❌ | +| Apple Terminal | ✅[^apple] | ❌ | ❌ | +| iTerm | ✅ | ✅ | ❌ | +| Windows cmd | ❌ | ❌ | ❌ | +| Windows Terminal | ✅ | ✅ | ❌ | + +[^vte]: This covers all vte-based terminals, including Gnome Terminal, guake, Pantheon Terminal, Terminator, Tilix, XFCE Terminal. OSC52 is not supported, see [issue#2495](https://gitlab.gnome.org/GNOME/vte/-/issues/2495). +[^urxvt]: Workaround for urxvt not supporting OSC52. See [this](https://unix.stackexchange.com/a/629485) for more information. +[^konsole]: OSC52 is not supported, for more info see [bug#372116](https://bugs.kde.org/show_bug.cgi?id=372116). +[^apple]: OSC52 works with a [workaround](https://github.com/roy2220/osc52pty). +[^tmux]: OSC8 is not supported, for more info see [issue#911](https://github.com/tmux/tmux/issues/911). +[^screen]: OSC8 is not supported, for more info see [bug#50952](https://savannah.gnu.org/bugs/index.php?50952). +[^alacritty]: OSC8 is not supported, for more info see [issue#922](https://github.com/alacritty/alacritty/issues/922). + +
+ ## Platform Support `termenv` works on Unix systems (like Linux, macOS, or BSD) and Windows. While @@ -316,13 +385,16 @@ terminal applications on Unix support ANSI styling out-of-the-box, on Windows you need to enable ANSI processing in your application first: ```go - mode, err := termenv.EnableWindowsANSIConsole() + restoreConsole, err := termenv.EnableVirtualTerminalProcessing(termenv.DefaultOutput()) if err != nil { panic(err) } - defer termenv.RestoreWindowsConsole(mode) + defer restoreConsole() ``` +The above code is safe to include on non-Windows systems or when os.Stdout does +not refer to a terminal (e.g. in tests). + ## Color Chart ![ANSI color chart](https://github.com/muesli/termenv/raw/master/examples/color-chart/color-chart.png) @@ -351,8 +423,8 @@ out these projects: Got some feedback or suggestions? Please open an issue or drop me a note! -* [Twitter](https://twitter.com/mueslix) -* [The Fediverse](https://mastodon.social/@fribbledom) +- [Twitter](https://twitter.com/mueslix) +- [The Fediverse](https://mastodon.social/@fribbledom) ## License diff --git a/vendor/github.com/muesli/termenv/ansi_compat.md b/vendor/github.com/muesli/termenv/ansi_compat.md index 67ef4e49c3094..6b68a3a9ad17a 100644 --- a/vendor/github.com/muesli/termenv/ansi_compat.md +++ b/vendor/github.com/muesli/termenv/ansi_compat.md @@ -46,3 +46,20 @@ This command should set the window title to "Test": ```bash echo -ne "\033]2;Test\007" && sleep 10 ``` + +## Bracketed paste + +Enter this command, then paste a word from the clipboard. The text +displayed on the terminal should contain the codes `200~` and `201~`: + +```bash +echo -ne "\033[?2004h" && sleep 10 +``` + +## Trigger Notification + +This command should trigger a notification: + +```bash +echo -ne "\033]777;notify;Title;Body\033\\" +``` diff --git a/vendor/github.com/muesli/termenv/color.go b/vendor/github.com/muesli/termenv/color.go index 7cf7dae3f82d1..1a216e93a78a7 100644 --- a/vendor/github.com/muesli/termenv/color.go +++ b/vendor/github.com/muesli/termenv/color.go @@ -3,9 +3,7 @@ package termenv import ( "errors" "fmt" - "image/color" "math" - "strconv" "strings" "github.com/lucasb-eyer/go-colorful" @@ -69,74 +67,8 @@ func ConvertToRGB(c Color) colorful.Color { return ch } -// Convert transforms a given Color to a Color supported within the Profile. -func (p Profile) Convert(c Color) Color { - if p == Ascii { - return NoColor{} - } - - switch v := c.(type) { - case ANSIColor: - return v - - case ANSI256Color: - if p == ANSI { - return ansi256ToANSIColor(v) - } - return v - - case RGBColor: - h, err := colorful.Hex(string(v)) - if err != nil { - return nil - } - if p < TrueColor { - ac := hexToANSI256Color(h) - if p == ANSI { - return ansi256ToANSIColor(ac) - } - return ac - } - return v - } - - return c -} - -// Color creates a Color from a string. Valid inputs are hex colors, as well as -// ANSI color codes (0-15, 16-255). -func (p Profile) Color(s string) Color { - if len(s) == 0 { - return nil - } - - var c Color - if strings.HasPrefix(s, "#") { - c = RGBColor(s) - } else { - i, err := strconv.Atoi(s) - if err != nil { - return nil - } - - if i < 16 { - c = ANSIColor(i) - } else { - c = ANSI256Color(i) - } - } - - return p.Convert(c) -} - -// FromColor creates a Color from a color.Color. -func (p Profile) FromColor(c color.Color) Color { - col, _ := colorful.MakeColor(c) - return p.Color(col.Hex()) -} - // Sequence returns the ANSI Sequence for the color. -func (c NoColor) Sequence(bg bool) string { +func (c NoColor) Sequence(_ bool) string { return "" } @@ -185,12 +117,12 @@ func xTermColor(s string) (RGBColor, error) { } switch { - case strings.HasSuffix(s, "\a"): - s = strings.TrimSuffix(s, "\a") - case strings.HasSuffix(s, "\033"): - s = strings.TrimSuffix(s, "\033") - case strings.HasSuffix(s, "\033\\"): - s = strings.TrimSuffix(s, "\033\\") + case strings.HasSuffix(s, string(BEL)): + s = strings.TrimSuffix(s, string(BEL)) + case strings.HasSuffix(s, string(ESC)): + s = strings.TrimSuffix(s, string(ESC)) + case strings.HasSuffix(s, ST): + s = strings.TrimSuffix(s, ST) default: return RGBColor(""), ErrInvalidColor } diff --git a/vendor/github.com/muesli/termenv/copy.go b/vendor/github.com/muesli/termenv/copy.go new file mode 100644 index 0000000000000..4bf5c9fea4586 --- /dev/null +++ b/vendor/github.com/muesli/termenv/copy.go @@ -0,0 +1,37 @@ +package termenv + +import ( + "strings" + + "github.com/aymanbagabas/go-osc52/v2" +) + +// Copy copies text to clipboard using OSC 52 escape sequence. +func (o Output) Copy(str string) { + s := osc52.New(str) + if strings.HasPrefix(o.environ.Getenv("TERM"), "screen") { + s = s.Screen() + } + _, _ = s.WriteTo(o) +} + +// CopyPrimary copies text to primary clipboard (X11) using OSC 52 escape +// sequence. +func (o Output) CopyPrimary(str string) { + s := osc52.New(str).Primary() + if strings.HasPrefix(o.environ.Getenv("TERM"), "screen") { + s = s.Screen() + } + _, _ = s.WriteTo(o) +} + +// Copy copies text to clipboard using OSC 52 escape sequence. +func Copy(str string) { + output.Copy(str) +} + +// CopyPrimary copies text to primary clipboard (X11) using OSC 52 escape +// sequence. +func CopyPrimary(str string) { + output.CopyPrimary(str) +} diff --git a/vendor/github.com/muesli/termenv/hyperlink.go b/vendor/github.com/muesli/termenv/hyperlink.go new file mode 100644 index 0000000000000..97e760a3bfc8d --- /dev/null +++ b/vendor/github.com/muesli/termenv/hyperlink.go @@ -0,0 +1,11 @@ +package termenv + +// Hyperlink creates a hyperlink using OSC8. +func Hyperlink(link, name string) string { + return output.Hyperlink(link, name) +} + +// Hyperlink creates a hyperlink using OSC8. +func (o *Output) Hyperlink(link, name string) string { + return OSC + "8;;" + link + ST + name + OSC + "8;;" + ST +} diff --git a/vendor/github.com/muesli/termenv/notification.go b/vendor/github.com/muesli/termenv/notification.go new file mode 100644 index 0000000000000..2a8cf06a9d5f7 --- /dev/null +++ b/vendor/github.com/muesli/termenv/notification.go @@ -0,0 +1,11 @@ +package termenv + +// Notify triggers a notification using OSC777. +func Notify(title, body string) { + output.Notify(title, body) +} + +// Notify triggers a notification using OSC777. +func (o *Output) Notify(title, body string) { + _, _ = o.WriteString(OSC + "777;notify;" + title + ";" + body + ST) +} diff --git a/vendor/github.com/muesli/termenv/output.go b/vendor/github.com/muesli/termenv/output.go new file mode 100644 index 0000000000000..e22d369c9f04a --- /dev/null +++ b/vendor/github.com/muesli/termenv/output.go @@ -0,0 +1,197 @@ +package termenv + +import ( + "io" + "os" + "sync" +) + +var ( + // output is the default global output. + output = NewOutput(os.Stdout) +) + +// File represents a file descriptor. +type File interface { + io.ReadWriter + Fd() uintptr +} + +// OutputOption sets an option on Output. +type OutputOption = func(*Output) + +// Output is a terminal output. +type Output struct { + Profile + tty io.Writer + environ Environ + + assumeTTY bool + unsafe bool + cache bool + fgSync *sync.Once + fgColor Color + bgSync *sync.Once + bgColor Color +} + +// Environ is an interface for getting environment variables. +type Environ interface { + Environ() []string + Getenv(string) string +} + +type osEnviron struct{} + +func (oe *osEnviron) Environ() []string { + return os.Environ() +} + +func (oe *osEnviron) Getenv(key string) string { + return os.Getenv(key) +} + +// DefaultOutput returns the default global output. +func DefaultOutput() *Output { + return output +} + +// SetDefaultOutput sets the default global output. +func SetDefaultOutput(o *Output) { + output = o +} + +// NewOutput returns a new Output for the given file descriptor. +func NewOutput(tty io.Writer, opts ...OutputOption) *Output { + o := &Output{ + tty: tty, + environ: &osEnviron{}, + Profile: -1, + fgSync: &sync.Once{}, + fgColor: NoColor{}, + bgSync: &sync.Once{}, + bgColor: NoColor{}, + } + + if o.tty == nil { + o.tty = os.Stdout + } + for _, opt := range opts { + opt(o) + } + if o.Profile < 0 { + o.Profile = o.EnvColorProfile() + } + + return o +} + +// WithEnvironment returns a new OutputOption for the given environment. +func WithEnvironment(environ Environ) OutputOption { + return func(o *Output) { + o.environ = environ + } +} + +// WithProfile returns a new OutputOption for the given profile. +func WithProfile(profile Profile) OutputOption { + return func(o *Output) { + o.Profile = profile + } +} + +// WithColorCache returns a new OutputOption with fore- and background color values +// pre-fetched and cached. +func WithColorCache(v bool) OutputOption { + return func(o *Output) { + o.cache = v + + // cache the values now + _ = o.ForegroundColor() + _ = o.BackgroundColor() + } +} + +// WithTTY returns a new OutputOption to assume whether or not the output is a TTY. +// This is useful when mocking console output. +func WithTTY(v bool) OutputOption { + return func(o *Output) { + o.assumeTTY = v + } +} + +// WithUnsafe returns a new OutputOption with unsafe mode enabled. Unsafe mode doesn't +// check whether or not the terminal is a TTY. +// +// This option supersedes WithTTY. +// +// This is useful when mocking console output and enforcing ANSI escape output +// e.g. on SSH sessions. +func WithUnsafe() OutputOption { + return func(o *Output) { + o.unsafe = true + } +} + +// ForegroundColor returns the terminal's default foreground color. +func (o *Output) ForegroundColor() Color { + f := func() { + if !o.isTTY() { + return + } + + o.fgColor = o.foregroundColor() + } + + if o.cache { + o.fgSync.Do(f) + } else { + f() + } + + return o.fgColor +} + +// BackgroundColor returns the terminal's default background color. +func (o *Output) BackgroundColor() Color { + f := func() { + if !o.isTTY() { + return + } + + o.bgColor = o.backgroundColor() + } + + if o.cache { + o.bgSync.Do(f) + } else { + f() + } + + return o.bgColor +} + +// HasDarkBackground returns whether terminal uses a dark-ish background. +func (o *Output) HasDarkBackground() bool { + c := ConvertToRGB(o.BackgroundColor()) + _, _, l := c.Hsl() + return l < 0.5 +} + +// TTY returns the terminal's file descriptor. This may be nil if the output is +// not a terminal. +func (o Output) TTY() File { + if f, ok := o.tty.(File); ok { + return f + } + return nil +} + +func (o Output) Write(p []byte) (int, error) { + return o.tty.Write(p) +} + +// WriteString writes the given string to the output. +func (o Output) WriteString(s string) (int, error) { + return o.Write([]byte(s)) +} diff --git a/vendor/github.com/muesli/termenv/profile.go b/vendor/github.com/muesli/termenv/profile.go new file mode 100644 index 0000000000000..fa128e209167e --- /dev/null +++ b/vendor/github.com/muesli/termenv/profile.go @@ -0,0 +1,97 @@ +package termenv + +import ( + "image/color" + "strconv" + "strings" + + "github.com/lucasb-eyer/go-colorful" +) + +// Profile is a color profile: Ascii, ANSI, ANSI256, or TrueColor. +type Profile int + +const ( + // TrueColor, 24-bit color profile + TrueColor = Profile(iota) + // ANSI256, 8-bit color profile + ANSI256 + // ANSI, 4-bit color profile + ANSI + // Ascii, uncolored profile + Ascii //nolint:revive +) + +// String returns a new Style. +func (p Profile) String(s ...string) Style { + return Style{ + profile: p, + string: strings.Join(s, " "), + } +} + +// Convert transforms a given Color to a Color supported within the Profile. +func (p Profile) Convert(c Color) Color { + if p == Ascii { + return NoColor{} + } + + switch v := c.(type) { + case ANSIColor: + return v + + case ANSI256Color: + if p == ANSI { + return ansi256ToANSIColor(v) + } + return v + + case RGBColor: + h, err := colorful.Hex(string(v)) + if err != nil { + return nil + } + if p != TrueColor { + ac := hexToANSI256Color(h) + if p == ANSI { + return ansi256ToANSIColor(ac) + } + return ac + } + return v + } + + return c +} + +// Color creates a Color from a string. Valid inputs are hex colors, as well as +// ANSI color codes (0-15, 16-255). +func (p Profile) Color(s string) Color { + if len(s) == 0 { + return nil + } + + var c Color + if strings.HasPrefix(s, "#") { + c = RGBColor(s) + } else { + i, err := strconv.Atoi(s) + if err != nil { + return nil + } + + if i < 16 { + c = ANSIColor(i) + } else { + c = ANSI256Color(i) + } + } + + return p.Convert(c) +} + +// FromColor creates a Color from a color.Color. +func (p Profile) FromColor(c color.Color) Color { + col, _ := colorful.MakeColor(c) + return p.Color(col.Hex()) +} diff --git a/vendor/github.com/muesli/termenv/screen.go b/vendor/github.com/muesli/termenv/screen.go index 897bebcdc4764..a71181b61f2a4 100644 --- a/vendor/github.com/muesli/termenv/screen.go +++ b/vendor/github.com/muesli/termenv/screen.go @@ -32,16 +32,20 @@ const ( EraseEntireLineSeq = "2K" // Mouse. - EnableMousePressSeq = "?9h" // press only (X10) - DisableMousePressSeq = "?9l" - EnableMouseSeq = "?1000h" // press, release, wheel - DisableMouseSeq = "?1000l" - EnableMouseHiliteSeq = "?1001h" // highlight - DisableMouseHiliteSeq = "?1001l" - EnableMouseCellMotionSeq = "?1002h" // press, release, move on pressed, wheel - DisableMouseCellMotionSeq = "?1002l" - EnableMouseAllMotionSeq = "?1003h" // press, release, move, wheel - DisableMouseAllMotionSeq = "?1003l" + EnableMousePressSeq = "?9h" // press only (X10) + DisableMousePressSeq = "?9l" + EnableMouseSeq = "?1000h" // press, release, wheel + DisableMouseSeq = "?1000l" + EnableMouseHiliteSeq = "?1001h" // highlight + DisableMouseHiliteSeq = "?1001l" + EnableMouseCellMotionSeq = "?1002h" // press, release, move on pressed, wheel + DisableMouseCellMotionSeq = "?1002l" + EnableMouseAllMotionSeq = "?1003h" // press, release, move, wheel + DisableMouseAllMotionSeq = "?1003l" + EnableMouseExtendedModeSeq = "?1006h" // press, release, move, wheel, extended coordinates + DisableMouseExtendedModeSeq = "?1006l" + EnableMousePixelsModeSeq = "?1016h" // press, release, move, wheel, extended pixel coordinates + DisableMousePixelsModeSeq = "?1016l" // Screen. RestoreScreenSeq = "?47l" @@ -49,210 +53,538 @@ const ( AltScreenSeq = "?1049h" ExitAltScreenSeq = "?1049l" + // Bracketed paste. + // https://en.wikipedia.org/wiki/Bracketed-paste + EnableBracketedPasteSeq = "?2004h" + DisableBracketedPasteSeq = "?2004l" + StartBracketedPasteSeq = "200~" + EndBracketedPasteSeq = "201~" + // Session. - SetWindowTitleSeq = "2;%s\007" - SetForegroundColorSeq = "10;%s\007" - SetBackgroundColorSeq = "11;%s\007" - SetCursorColorSeq = "12;%s\007" + SetWindowTitleSeq = "2;%s" + string(BEL) + SetForegroundColorSeq = "10;%s" + string(BEL) + SetBackgroundColorSeq = "11;%s" + string(BEL) + SetCursorColorSeq = "12;%s" + string(BEL) ShowCursorSeq = "?25h" HideCursorSeq = "?25l" ) // Reset the terminal to its default style, removing any active styles. +func (o Output) Reset() { + fmt.Fprint(o.tty, CSI+ResetSeq+"m") +} + +// SetForegroundColor sets the default foreground color. +func (o Output) SetForegroundColor(color Color) { + fmt.Fprintf(o.tty, OSC+SetForegroundColorSeq, color) +} + +// SetBackgroundColor sets the default background color. +func (o Output) SetBackgroundColor(color Color) { + fmt.Fprintf(o.tty, OSC+SetBackgroundColorSeq, color) +} + +// SetCursorColor sets the cursor color. +func (o Output) SetCursorColor(color Color) { + fmt.Fprintf(o.tty, OSC+SetCursorColorSeq, color) +} + +// RestoreScreen restores a previously saved screen state. +func (o Output) RestoreScreen() { + fmt.Fprint(o.tty, CSI+RestoreScreenSeq) +} + +// SaveScreen saves the screen state. +func (o Output) SaveScreen() { + fmt.Fprint(o.tty, CSI+SaveScreenSeq) +} + +// AltScreen switches to the alternate screen buffer. The former view can be +// restored with ExitAltScreen(). +func (o Output) AltScreen() { + fmt.Fprint(o.tty, CSI+AltScreenSeq) +} + +// ExitAltScreen exits the alternate screen buffer and returns to the former +// terminal view. +func (o Output) ExitAltScreen() { + fmt.Fprint(o.tty, CSI+ExitAltScreenSeq) +} + +// ClearScreen clears the visible portion of the terminal. +func (o Output) ClearScreen() { + fmt.Fprintf(o.tty, CSI+EraseDisplaySeq, 2) + o.MoveCursor(1, 1) +} + +// MoveCursor moves the cursor to a given position. +func (o Output) MoveCursor(row int, column int) { + fmt.Fprintf(o.tty, CSI+CursorPositionSeq, row, column) +} + +// HideCursor hides the cursor. +func (o Output) HideCursor() { + fmt.Fprint(o.tty, CSI+HideCursorSeq) +} + +// ShowCursor shows the cursor. +func (o Output) ShowCursor() { + fmt.Fprint(o.tty, CSI+ShowCursorSeq) +} + +// SaveCursorPosition saves the cursor position. +func (o Output) SaveCursorPosition() { + fmt.Fprint(o.tty, CSI+SaveCursorPositionSeq) +} + +// RestoreCursorPosition restores a saved cursor position. +func (o Output) RestoreCursorPosition() { + fmt.Fprint(o.tty, CSI+RestoreCursorPositionSeq) +} + +// CursorUp moves the cursor up a given number of lines. +func (o Output) CursorUp(n int) { + fmt.Fprintf(o.tty, CSI+CursorUpSeq, n) +} + +// CursorDown moves the cursor down a given number of lines. +func (o Output) CursorDown(n int) { + fmt.Fprintf(o.tty, CSI+CursorDownSeq, n) +} + +// CursorForward moves the cursor up a given number of lines. +func (o Output) CursorForward(n int) { + fmt.Fprintf(o.tty, CSI+CursorForwardSeq, n) +} + +// CursorBack moves the cursor backwards a given number of cells. +func (o Output) CursorBack(n int) { + fmt.Fprintf(o.tty, CSI+CursorBackSeq, n) +} + +// CursorNextLine moves the cursor down a given number of lines and places it at +// the beginning of the line. +func (o Output) CursorNextLine(n int) { + fmt.Fprintf(o.tty, CSI+CursorNextLineSeq, n) +} + +// CursorPrevLine moves the cursor up a given number of lines and places it at +// the beginning of the line. +func (o Output) CursorPrevLine(n int) { + fmt.Fprintf(o.tty, CSI+CursorPreviousLineSeq, n) +} + +// ClearLine clears the current line. +func (o Output) ClearLine() { + fmt.Fprint(o.tty, CSI+EraseEntireLineSeq) +} + +// ClearLineLeft clears the line to the left of the cursor. +func (o Output) ClearLineLeft() { + fmt.Fprint(o.tty, CSI+EraseLineLeftSeq) +} + +// ClearLineRight clears the line to the right of the cursor. +func (o Output) ClearLineRight() { + fmt.Fprint(o.tty, CSI+EraseLineRightSeq) +} + +// ClearLines clears a given number of lines. +func (o Output) ClearLines(n int) { + clearLine := fmt.Sprintf(CSI+EraseLineSeq, 2) + cursorUp := fmt.Sprintf(CSI+CursorUpSeq, 1) + fmt.Fprint(o.tty, clearLine+strings.Repeat(cursorUp+clearLine, n)) +} + +// ChangeScrollingRegion sets the scrolling region of the terminal. +func (o Output) ChangeScrollingRegion(top, bottom int) { + fmt.Fprintf(o.tty, CSI+ChangeScrollingRegionSeq, top, bottom) +} + +// InsertLines inserts the given number of lines at the top of the scrollable +// region, pushing lines below down. +func (o Output) InsertLines(n int) { + fmt.Fprintf(o.tty, CSI+InsertLineSeq, n) +} + +// DeleteLines deletes the given number of lines, pulling any lines in +// the scrollable region below up. +func (o Output) DeleteLines(n int) { + fmt.Fprintf(o.tty, CSI+DeleteLineSeq, n) +} + +// EnableMousePress enables X10 mouse mode. Button press events are sent only. +func (o Output) EnableMousePress() { + fmt.Fprint(o.tty, CSI+EnableMousePressSeq) +} + +// DisableMousePress disables X10 mouse mode. +func (o Output) DisableMousePress() { + fmt.Fprint(o.tty, CSI+DisableMousePressSeq) +} + +// EnableMouse enables Mouse Tracking mode. +func (o Output) EnableMouse() { + fmt.Fprint(o.tty, CSI+EnableMouseSeq) +} + +// DisableMouse disables Mouse Tracking mode. +func (o Output) DisableMouse() { + fmt.Fprint(o.tty, CSI+DisableMouseSeq) +} + +// EnableMouseHilite enables Hilite Mouse Tracking mode. +func (o Output) EnableMouseHilite() { + fmt.Fprint(o.tty, CSI+EnableMouseHiliteSeq) +} + +// DisableMouseHilite disables Hilite Mouse Tracking mode. +func (o Output) DisableMouseHilite() { + fmt.Fprint(o.tty, CSI+DisableMouseHiliteSeq) +} + +// EnableMouseCellMotion enables Cell Motion Mouse Tracking mode. +func (o Output) EnableMouseCellMotion() { + fmt.Fprint(o.tty, CSI+EnableMouseCellMotionSeq) +} + +// DisableMouseCellMotion disables Cell Motion Mouse Tracking mode. +func (o Output) DisableMouseCellMotion() { + fmt.Fprint(o.tty, CSI+DisableMouseCellMotionSeq) +} + +// EnableMouseAllMotion enables All Motion Mouse mode. +func (o Output) EnableMouseAllMotion() { + fmt.Fprint(o.tty, CSI+EnableMouseAllMotionSeq) +} + +// DisableMouseAllMotion disables All Motion Mouse mode. +func (o Output) DisableMouseAllMotion() { + fmt.Fprint(o.tty, CSI+DisableMouseAllMotionSeq) +} + +// EnableMouseExtendedMotion enables Extended Mouse mode (SGR). This should be +// enabled in conjunction with EnableMouseCellMotion, and EnableMouseAllMotion. +func (o Output) EnableMouseExtendedMode() { + fmt.Fprint(o.tty, CSI+EnableMouseExtendedModeSeq) +} + +// DisableMouseExtendedMotion disables Extended Mouse mode (SGR). +func (o Output) DisableMouseExtendedMode() { + fmt.Fprint(o.tty, CSI+DisableMouseExtendedModeSeq) +} + +// EnableMousePixelsMotion enables Pixel Motion Mouse mode (SGR-Pixels). This +// should be enabled in conjunction with EnableMouseCellMotion, and +// EnableMouseAllMotion. +func (o Output) EnableMousePixelsMode() { + fmt.Fprint(o.tty, CSI+EnableMousePixelsModeSeq) +} + +// DisableMousePixelsMotion disables Pixel Motion Mouse mode (SGR-Pixels). +func (o Output) DisableMousePixelsMode() { + fmt.Fprint(o.tty, CSI+DisableMousePixelsModeSeq) +} + +// SetWindowTitle sets the terminal window title. +func (o Output) SetWindowTitle(title string) { + fmt.Fprintf(o.tty, OSC+SetWindowTitleSeq, title) +} + +// EnableBracketedPaste enables bracketed paste. +func (o Output) EnableBracketedPaste() { + fmt.Fprintf(o.tty, CSI+EnableBracketedPasteSeq) +} + +// DisableBracketedPaste disables bracketed paste. +func (o Output) DisableBracketedPaste() { + fmt.Fprintf(o.tty, CSI+DisableBracketedPasteSeq) +} + +// Legacy functions. + +// Reset the terminal to its default style, removing any active styles. +// +// Deprecated: please use termenv.Output instead. func Reset() { - fmt.Print(CSI + ResetSeq + "m") + output.Reset() } // SetForegroundColor sets the default foreground color. +// +// Deprecated: please use termenv.Output instead. func SetForegroundColor(color Color) { - fmt.Printf(OSC+SetForegroundColorSeq, color) + output.SetForegroundColor(color) } // SetBackgroundColor sets the default background color. +// +// Deprecated: please use termenv.Output instead. func SetBackgroundColor(color Color) { - fmt.Printf(OSC+SetBackgroundColorSeq, color) + output.SetBackgroundColor(color) } // SetCursorColor sets the cursor color. +// +// Deprecated: please use termenv.Output instead. func SetCursorColor(color Color) { - fmt.Printf(OSC+SetCursorColorSeq, color) + output.SetCursorColor(color) } // RestoreScreen restores a previously saved screen state. +// +// Deprecated: please use termenv.Output instead. func RestoreScreen() { - fmt.Print(CSI + RestoreScreenSeq) + output.RestoreScreen() } // SaveScreen saves the screen state. +// +// Deprecated: please use termenv.Output instead. func SaveScreen() { - fmt.Print(CSI + SaveScreenSeq) + output.SaveScreen() } // AltScreen switches to the alternate screen buffer. The former view can be // restored with ExitAltScreen(). +// +// Deprecated: please use termenv.Output instead. func AltScreen() { - fmt.Print(CSI + AltScreenSeq) + output.AltScreen() } // ExitAltScreen exits the alternate screen buffer and returns to the former // terminal view. +// +// Deprecated: please use termenv.Output instead. func ExitAltScreen() { - fmt.Print(CSI + ExitAltScreenSeq) + output.ExitAltScreen() } // ClearScreen clears the visible portion of the terminal. +// +// Deprecated: please use termenv.Output instead. func ClearScreen() { - fmt.Printf(CSI+EraseDisplaySeq, 2) - MoveCursor(1, 1) + output.ClearScreen() } // MoveCursor moves the cursor to a given position. +// +// Deprecated: please use termenv.Output instead. func MoveCursor(row int, column int) { - fmt.Printf(CSI+CursorPositionSeq, row, column) + output.MoveCursor(row, column) } // HideCursor hides the cursor. +// +// Deprecated: please use termenv.Output instead. func HideCursor() { - fmt.Printf(CSI + HideCursorSeq) + output.HideCursor() } // ShowCursor shows the cursor. +// +// Deprecated: please use termenv.Output instead. func ShowCursor() { - fmt.Printf(CSI + ShowCursorSeq) + output.ShowCursor() } // SaveCursorPosition saves the cursor position. +// +// Deprecated: please use termenv.Output instead. func SaveCursorPosition() { - fmt.Print(CSI + SaveCursorPositionSeq) + output.SaveCursorPosition() } // RestoreCursorPosition restores a saved cursor position. +// +// Deprecated: please use termenv.Output instead. func RestoreCursorPosition() { - fmt.Print(CSI + RestoreCursorPositionSeq) + output.RestoreCursorPosition() } // CursorUp moves the cursor up a given number of lines. +// +// Deprecated: please use termenv.Output instead. func CursorUp(n int) { - fmt.Printf(CSI+CursorUpSeq, n) + output.CursorUp(n) } // CursorDown moves the cursor down a given number of lines. +// +// Deprecated: please use termenv.Output instead. func CursorDown(n int) { - fmt.Printf(CSI+CursorDownSeq, n) + output.CursorDown(n) } // CursorForward moves the cursor up a given number of lines. +// +// Deprecated: please use termenv.Output instead. func CursorForward(n int) { - fmt.Printf(CSI+CursorForwardSeq, n) + output.CursorForward(n) } // CursorBack moves the cursor backwards a given number of cells. +// +// Deprecated: please use termenv.Output instead. func CursorBack(n int) { - fmt.Printf(CSI+CursorBackSeq, n) + output.CursorBack(n) } // CursorNextLine moves the cursor down a given number of lines and places it at // the beginning of the line. +// +// Deprecated: please use termenv.Output instead. func CursorNextLine(n int) { - fmt.Printf(CSI+CursorNextLineSeq, n) + output.CursorNextLine(n) } // CursorPrevLine moves the cursor up a given number of lines and places it at // the beginning of the line. +// +// Deprecated: please use termenv.Output instead. func CursorPrevLine(n int) { - fmt.Printf(CSI+CursorPreviousLineSeq, n) + output.CursorPrevLine(n) } // ClearLine clears the current line. +// +// Deprecated: please use termenv.Output instead. func ClearLine() { - fmt.Print(CSI + EraseEntireLineSeq) + output.ClearLine() } // ClearLineLeft clears the line to the left of the cursor. +// +// Deprecated: please use termenv.Output instead. func ClearLineLeft() { - fmt.Print(CSI + EraseLineLeftSeq) + output.ClearLineLeft() } // ClearLineRight clears the line to the right of the cursor. +// +// Deprecated: please use termenv.Output instead. func ClearLineRight() { - fmt.Print(CSI + EraseLineRightSeq) + output.ClearLineRight() } // ClearLines clears a given number of lines. +// +// Deprecated: please use termenv.Output instead. func ClearLines(n int) { - clearLine := fmt.Sprintf(CSI+EraseLineSeq, 2) - cursorUp := fmt.Sprintf(CSI+CursorUpSeq, 1) - fmt.Print(clearLine + strings.Repeat(cursorUp+clearLine, n)) + output.ClearLines(n) } // ChangeScrollingRegion sets the scrolling region of the terminal. +// +// Deprecated: please use termenv.Output instead. func ChangeScrollingRegion(top, bottom int) { - fmt.Printf(CSI+ChangeScrollingRegionSeq, top, bottom) + output.ChangeScrollingRegion(top, bottom) } // InsertLines inserts the given number of lines at the top of the scrollable // region, pushing lines below down. +// +// Deprecated: please use termenv.Output instead. func InsertLines(n int) { - fmt.Printf(CSI+InsertLineSeq, n) + output.InsertLines(n) } // DeleteLines deletes the given number of lines, pulling any lines in // the scrollable region below up. +// +// Deprecated: please use termenv.Output instead. func DeleteLines(n int) { - fmt.Printf(CSI+DeleteLineSeq, n) + output.DeleteLines(n) } // EnableMousePress enables X10 mouse mode. Button press events are sent only. +// +// Deprecated: please use termenv.Output instead. func EnableMousePress() { - fmt.Print(CSI + EnableMousePressSeq) + output.EnableMousePress() } // DisableMousePress disables X10 mouse mode. +// +// Deprecated: please use termenv.Output instead. func DisableMousePress() { - fmt.Print(CSI + DisableMousePressSeq) + output.DisableMousePress() } // EnableMouse enables Mouse Tracking mode. +// +// Deprecated: please use termenv.Output instead. func EnableMouse() { - fmt.Print(CSI + EnableMouseSeq) + output.EnableMouse() } // DisableMouse disables Mouse Tracking mode. +// +// Deprecated: please use termenv.Output instead. func DisableMouse() { - fmt.Print(CSI + DisableMouseSeq) + output.DisableMouse() } // EnableMouseHilite enables Hilite Mouse Tracking mode. +// +// Deprecated: please use termenv.Output instead. func EnableMouseHilite() { - fmt.Print(CSI + EnableMouseHiliteSeq) + output.EnableMouseHilite() } // DisableMouseHilite disables Hilite Mouse Tracking mode. +// +// Deprecated: please use termenv.Output instead. func DisableMouseHilite() { - fmt.Print(CSI + DisableMouseHiliteSeq) + output.DisableMouseHilite() } // EnableMouseCellMotion enables Cell Motion Mouse Tracking mode. +// +// Deprecated: please use termenv.Output instead. func EnableMouseCellMotion() { - fmt.Print(CSI + EnableMouseCellMotionSeq) + output.EnableMouseCellMotion() } // DisableMouseCellMotion disables Cell Motion Mouse Tracking mode. +// +// Deprecated: please use termenv.Output instead. func DisableMouseCellMotion() { - fmt.Print(CSI + DisableMouseCellMotionSeq) + output.DisableMouseCellMotion() } // EnableMouseAllMotion enables All Motion Mouse mode. +// +// Deprecated: please use termenv.Output instead. func EnableMouseAllMotion() { - fmt.Print(CSI + EnableMouseAllMotionSeq) + output.EnableMouseAllMotion() } // DisableMouseAllMotion disables All Motion Mouse mode. +// +// Deprecated: please use termenv.Output instead. func DisableMouseAllMotion() { - fmt.Print(CSI + DisableMouseAllMotionSeq) + output.DisableMouseAllMotion() } // SetWindowTitle sets the terminal window title. +// +// Deprecated: please use termenv.Output instead. func SetWindowTitle(title string) { - fmt.Printf(OSC+SetWindowTitleSeq, title) + output.SetWindowTitle(title) +} + +// EnableBracketedPaste enables bracketed paste. +// +// Deprecated: please use termenv.Output instead. +func EnableBracketedPaste() { + output.EnableBracketedPaste() +} + +// DisableBracketedPaste disables bracketed paste. +// +// Deprecated: please use termenv.Output instead. +func DisableBracketedPaste() { + output.DisableBracketedPaste() } diff --git a/vendor/github.com/muesli/termenv/style.go b/vendor/github.com/muesli/termenv/style.go index 577aa29e357fc..83b0b4d7ac455 100644 --- a/vendor/github.com/muesli/termenv/style.go +++ b/vendor/github.com/muesli/termenv/style.go @@ -22,6 +22,7 @@ const ( // Style is a string that various rendering styles can be applied to. type Style struct { + profile Profile string styles []string } @@ -29,7 +30,8 @@ type Style struct { // String returns a new Style. func String(s ...string) Style { return Style{ - string: strings.Join(s, " "), + profile: ANSI, + string: strings.Join(s, " "), } } @@ -39,6 +41,9 @@ func (t Style) String() string { // Styled renders s with all applied styles. func (t Style) Styled(s string) string { + if t.profile == Ascii { + return s + } if len(t.styles) == 0 { return s } diff --git a/vendor/github.com/muesli/termenv/templatehelper.go b/vendor/github.com/muesli/termenv/templatehelper.go index 4c716e2eab075..d75b0796cc6cf 100644 --- a/vendor/github.com/muesli/termenv/templatehelper.go +++ b/vendor/github.com/muesli/termenv/templatehelper.go @@ -4,11 +4,20 @@ import ( "text/template" ) +// TemplateFuncs returns template helpers for the given output. +func (o Output) TemplateFuncs() template.FuncMap { + return TemplateFuncs(o.Profile) +} + // TemplateFuncs contains a few useful template helpers. func TemplateFuncs(p Profile) template.FuncMap { + if p == Ascii { + return noopTemplateFuncs + } + return template.FuncMap{ "Color": func(values ...interface{}) string { - s := String(values[len(values)-1].(string)) + s := p.String(values[len(values)-1].(string)) switch len(values) { case 2: s = s.Foreground(p.Color(values[0].(string))) @@ -21,7 +30,7 @@ func TemplateFuncs(p Profile) template.FuncMap { return s.String() }, "Foreground": func(values ...interface{}) string { - s := String(values[len(values)-1].(string)) + s := p.String(values[len(values)-1].(string)) if len(values) == 2 { s = s.Foreground(p.Color(values[0].(string))) } @@ -29,27 +38,49 @@ func TemplateFuncs(p Profile) template.FuncMap { return s.String() }, "Background": func(values ...interface{}) string { - s := String(values[len(values)-1].(string)) + s := p.String(values[len(values)-1].(string)) if len(values) == 2 { s = s.Background(p.Color(values[0].(string))) } return s.String() }, - "Bold": styleFunc(Style.Bold), - "Faint": styleFunc(Style.Faint), - "Italic": styleFunc(Style.Italic), - "Underline": styleFunc(Style.Underline), - "Overline": styleFunc(Style.Overline), - "Blink": styleFunc(Style.Blink), - "Reverse": styleFunc(Style.Reverse), - "CrossOut": styleFunc(Style.CrossOut), + "Bold": styleFunc(p, Style.Bold), + "Faint": styleFunc(p, Style.Faint), + "Italic": styleFunc(p, Style.Italic), + "Underline": styleFunc(p, Style.Underline), + "Overline": styleFunc(p, Style.Overline), + "Blink": styleFunc(p, Style.Blink), + "Reverse": styleFunc(p, Style.Reverse), + "CrossOut": styleFunc(p, Style.CrossOut), } } -func styleFunc(f func(Style) Style) func(...interface{}) string { +func styleFunc(p Profile, f func(Style) Style) func(...interface{}) string { return func(values ...interface{}) string { - s := String(values[0].(string)) + s := p.String(values[0].(string)) return f(s).String() } } + +var noopTemplateFuncs = template.FuncMap{ + "Color": noColorFunc, + "Foreground": noColorFunc, + "Background": noColorFunc, + "Bold": noStyleFunc, + "Faint": noStyleFunc, + "Italic": noStyleFunc, + "Underline": noStyleFunc, + "Overline": noStyleFunc, + "Blink": noStyleFunc, + "Reverse": noStyleFunc, + "CrossOut": noStyleFunc, +} + +func noColorFunc(values ...interface{}) string { + return values[len(values)-1].(string) +} + +func noStyleFunc(values ...interface{}) string { + return values[0].(string) +} diff --git a/vendor/github.com/muesli/termenv/termenv.go b/vendor/github.com/muesli/termenv/termenv.go index 9161d9920f6bd..4ceb271772daf 100644 --- a/vendor/github.com/muesli/termenv/termenv.go +++ b/vendor/github.com/muesli/termenv/termenv.go @@ -2,7 +2,6 @@ package termenv import ( "errors" - "os" "github.com/mattn/go-isatty" ) @@ -12,66 +11,61 @@ var ( ErrStatusReport = errors.New("unable to retrieve status report") ) -// Profile is a color profile: Ascii, ANSI, ANSI256, or TrueColor. -type Profile int - const ( + // Escape character + ESC = '\x1b' + // Bell + BEL = '\a' // Control Sequence Introducer - CSI = "\x1b[" + CSI = string(ESC) + "[" // Operating System Command - OSC = "\x1b]" - - // Ascii, uncolored profile. - Ascii = Profile(iota) //nolint:revive - // ANSI, 4-bit color profile - ANSI - // ANSI256, 8-bit color profile - ANSI256 - // TrueColor, 24-bit color profile - TrueColor + OSC = string(ESC) + "]" + // String Terminator + ST = string(ESC) + `\` ) -func isTTY(fd uintptr) bool { - if len(os.Getenv("CI")) > 0 { +func (o *Output) isTTY() bool { + if o.assumeTTY || o.unsafe { + return true + } + if len(o.environ.Getenv("CI")) > 0 { + return false + } + if o.TTY() == nil { return false } - return isatty.IsTerminal(fd) + return isatty.IsTerminal(o.TTY().Fd()) } // ColorProfile returns the supported color profile: // Ascii, ANSI, ANSI256, or TrueColor. func ColorProfile() Profile { - if !isTTY(os.Stdout.Fd()) { - return Ascii - } - - return colorProfile() + return output.ColorProfile() } // ForegroundColor returns the terminal's default foreground color. func ForegroundColor() Color { - if !isTTY(os.Stdout.Fd()) { - return NoColor{} - } - - return foregroundColor() + return output.ForegroundColor() } // BackgroundColor returns the terminal's default background color. func BackgroundColor() Color { - if !isTTY(os.Stdout.Fd()) { - return NoColor{} - } - - return backgroundColor() + return output.BackgroundColor() } // HasDarkBackground returns whether terminal uses a dark-ish background. func HasDarkBackground() bool { - c := ConvertToRGB(BackgroundColor()) - _, _, l := c.Hsl() - return l < 0.5 + return output.HasDarkBackground() +} + +// EnvNoColor returns true if the environment variables explicitly disable color output +// by setting NO_COLOR (https://no-color.org/) +// or CLICOLOR/CLICOLOR_FORCE (https://bixense.com/clicolors/) +// If NO_COLOR is set, this will return true, ignoring CLICOLOR/CLICOLOR_FORCE +// If CLICOLOR=="0", it will be true only if CLICOLOR_FORCE is also "0" or is unset. +func (o *Output) EnvNoColor() bool { + return o.environ.Getenv("NO_COLOR") != "" || (o.environ.Getenv("CLICOLOR") == "0" && !o.cliColorForced()) } // EnvNoColor returns true if the environment variables explicitly disable color output @@ -80,7 +74,7 @@ func HasDarkBackground() bool { // If NO_COLOR is set, this will return true, ignoring CLICOLOR/CLICOLOR_FORCE // If CLICOLOR=="0", it will be true only if CLICOLOR_FORCE is also "0" or is unset. func EnvNoColor() bool { - return os.Getenv("NO_COLOR") != "" || (os.Getenv("CLICOLOR") == "0" && !cliColorForced()) + return output.EnvNoColor() } // EnvColorProfile returns the color profile based on environment variables set @@ -91,18 +85,29 @@ func EnvNoColor() bool { // If the terminal does not support any colors, but CLICOLOR_FORCE is set and not "0" // then the ANSI color profile will be returned. func EnvColorProfile() Profile { - if EnvNoColor() { + return output.EnvColorProfile() +} + +// EnvColorProfile returns the color profile based on environment variables set +// Supports NO_COLOR (https://no-color.org/) +// and CLICOLOR/CLICOLOR_FORCE (https://bixense.com/clicolors/) +// If none of these environment variables are set, this behaves the same as ColorProfile() +// It will return the Ascii color profile if EnvNoColor() returns true +// If the terminal does not support any colors, but CLICOLOR_FORCE is set and not "0" +// then the ANSI color profile will be returned. +func (o *Output) EnvColorProfile() Profile { + if o.EnvNoColor() { return Ascii } - p := ColorProfile() - if cliColorForced() && p == Ascii { + p := o.ColorProfile() + if o.cliColorForced() && p == Ascii { return ANSI } return p } -func cliColorForced() bool { - if forced := os.Getenv("CLICOLOR_FORCE"); forced != "" { +func (o *Output) cliColorForced() bool { + if forced := o.environ.Getenv("CLICOLOR_FORCE"); forced != "" { return forced != "0" } return false diff --git a/vendor/github.com/muesli/termenv/termenv_js.go b/vendor/github.com/muesli/termenv/termenv_js.go deleted file mode 100644 index ef7bb5c7d6888..0000000000000 --- a/vendor/github.com/muesli/termenv/termenv_js.go +++ /dev/null @@ -1,18 +0,0 @@ -//go:build js -// +build js - -package termenv - -func colorProfile() Profile { - return ANSI256 -} - -func foregroundColor() Color { - // default gray - return ANSIColor(7) -} - -func backgroundColor() Color { - // default black - return ANSIColor(0) -} diff --git a/vendor/github.com/muesli/termenv/termenv_other.go b/vendor/github.com/muesli/termenv/termenv_other.go new file mode 100644 index 0000000000000..93a43b6acbfb3 --- /dev/null +++ b/vendor/github.com/muesli/termenv/termenv_other.go @@ -0,0 +1,30 @@ +//go:build js || plan9 || aix +// +build js plan9 aix + +package termenv + +import "io" + +// ColorProfile returns the supported color profile: +// ANSI256 +func (o Output) ColorProfile() Profile { + return ANSI256 +} + +func (o Output) foregroundColor() Color { + // default gray + return ANSIColor(7) +} + +func (o Output) backgroundColor() Color { + // default black + return ANSIColor(0) +} + +// EnableVirtualTerminalProcessing enables virtual terminal processing on +// Windows for w and returns a function that restores w to its previous state. +// On non-Windows platforms, or if w does not refer to a terminal, then it +// returns a non-nil no-op function and no error. +func EnableVirtualTerminalProcessing(w io.Writer) (func() error, error) { + return func() error { return nil }, nil +} diff --git a/vendor/github.com/muesli/termenv/termenv_unix.go b/vendor/github.com/muesli/termenv/termenv_unix.go index 5ee7c0811422d..24d519a5d3d54 100644 --- a/vendor/github.com/muesli/termenv/termenv_unix.go +++ b/vendor/github.com/muesli/termenv/termenv_unix.go @@ -5,7 +5,7 @@ package termenv import ( "fmt" - "os" + "io" "strconv" "strings" "time" @@ -18,9 +18,19 @@ const ( OSCTimeout = 5 * time.Second ) -func colorProfile() Profile { - term := os.Getenv("TERM") - colorTerm := os.Getenv("COLORTERM") +// ColorProfile returns the supported color profile: +// Ascii, ANSI, ANSI256, or TrueColor. +func (o *Output) ColorProfile() Profile { + if !o.isTTY() { + return Ascii + } + + if o.environ.Getenv("GOOGLE_CLOUD_SHELL") == "true" { + return TrueColor + } + + term := o.environ.Getenv("TERM") + colorTerm := o.environ.Getenv("COLORTERM") switch strings.ToLower(colorTerm) { case "24bit": @@ -28,7 +38,7 @@ func colorProfile() Profile { case "truecolor": if strings.HasPrefix(term, "screen") { // tmux supports TrueColor, screen only ANSI256 - if os.Getenv("TERM_PROGRAM") != "tmux" { + if o.environ.Getenv("TERM_PROGRAM") != "tmux" { return ANSI256 } } @@ -40,7 +50,7 @@ func colorProfile() Profile { } switch term { - case "xterm-kitty": + case "xterm-kitty", "wezterm": return TrueColor case "linux": return ANSI @@ -59,8 +69,8 @@ func colorProfile() Profile { return Ascii } -func foregroundColor() Color { - s, err := termStatusReport(10) +func (o Output) foregroundColor() Color { + s, err := o.termStatusReport(10) if err == nil { c, err := xTermColor(s) if err == nil { @@ -68,7 +78,7 @@ func foregroundColor() Color { } } - colorFGBG := os.Getenv("COLORFGBG") + colorFGBG := o.environ.Getenv("COLORFGBG") if strings.Contains(colorFGBG, ";") { c := strings.Split(colorFGBG, ";") i, err := strconv.Atoi(c[0]) @@ -81,8 +91,8 @@ func foregroundColor() Color { return ANSIColor(7) } -func backgroundColor() Color { - s, err := termStatusReport(11) +func (o Output) backgroundColor() Color { + s, err := o.termStatusReport(11) if err == nil { c, err := xTermColor(s) if err == nil { @@ -90,7 +100,7 @@ func backgroundColor() Color { } } - colorFGBG := os.Getenv("COLORFGBG") + colorFGBG := o.environ.Getenv("COLORFGBG") if strings.Contains(colorFGBG, ";") { c := strings.Split(colorFGBG, ";") i, err := strconv.Atoi(c[len(c)-1]) @@ -103,7 +113,8 @@ func backgroundColor() Color { return ANSIColor(0) } -func waitForData(fd uintptr, timeout time.Duration) error { +func (o *Output) waitForData(timeout time.Duration) error { + fd := o.TTY().Fd() tv := unix.NsecToTimeval(int64(timeout)) var readfds unix.FdSet readfds.Set(int(fd)) @@ -126,13 +137,15 @@ func waitForData(fd uintptr, timeout time.Duration) error { return nil } -func readNextByte(f *os.File) (byte, error) { - if err := waitForData(f.Fd(), OSCTimeout); err != nil { - return 0, err +func (o *Output) readNextByte() (byte, error) { + if !o.unsafe { + if err := o.waitForData(OSCTimeout); err != nil { + return 0, err + } } var b [1]byte - n, err := f.Read(b[:]) + n, err := o.TTY().Read(b[:]) if err != nil { return 0, err } @@ -145,17 +158,17 @@ func readNextByte(f *os.File) (byte, error) { } // readNextResponse reads either an OSC response or a cursor position response: -// * OSC response: "\x1b]11;rgb:1111/1111/1111\x1b\\" -// * cursor position response: "\x1b[42;1R" -func readNextResponse(fd *os.File) (response string, isOSC bool, err error) { - start, err := readNextByte(fd) +// - OSC response: "\x1b]11;rgb:1111/1111/1111\x1b\\" +// - cursor position response: "\x1b[42;1R" +func (o *Output) readNextResponse() (response string, isOSC bool, err error) { + start, err := o.readNextByte() if err != nil { return "", false, err } // first byte must be ESC - for start != '\033' { - start, err = readNextByte(fd) + for start != ESC { + start, err = o.readNextByte() if err != nil { return "", false, err } @@ -164,7 +177,7 @@ func readNextResponse(fd *os.File) (response string, isOSC bool, err error) { response += string(start) // next byte is either '[' (cursor position response) or ']' (OSC response) - tpe, err := readNextByte(fd) + tpe, err := o.readNextByte() if err != nil { return "", false, err } @@ -182,7 +195,7 @@ func readNextResponse(fd *os.File) (response string, isOSC bool, err error) { } for { - b, err := readNextByte(os.Stdout) + b, err := o.readNextByte() if err != nil { return "", false, err } @@ -191,7 +204,7 @@ func readNextResponse(fd *os.File) (response string, isOSC bool, err error) { if oscResponse { // OSC can be terminated by BEL (\a) or ST (ESC) - if b == '\a' || strings.HasSuffix(response, "\033") { + if b == BEL || strings.HasSuffix(response, string(ESC)) { return response, true, nil } } else { @@ -210,42 +223,50 @@ func readNextResponse(fd *os.File) (response string, isOSC bool, err error) { return "", false, ErrStatusReport } -func termStatusReport(sequence int) (string, error) { +func (o Output) termStatusReport(sequence int) (string, error) { // screen/tmux can't support OSC, because they can be connected to multiple // terminals concurrently. - term := os.Getenv("TERM") - if strings.HasPrefix(term, "screen") { + term := o.environ.Getenv("TERM") + if strings.HasPrefix(term, "screen") || strings.HasPrefix(term, "tmux") { return "", ErrStatusReport } - // if in background, we can't control the terminal - if !isForeground(unix.Stdout) { + tty := o.TTY() + if tty == nil { return "", ErrStatusReport } - t, err := unix.IoctlGetTermios(unix.Stdout, tcgetattr) - if err != nil { - return "", ErrStatusReport - } - defer unix.IoctlSetTermios(unix.Stdout, tcsetattr, t) //nolint:errcheck + if !o.unsafe { + fd := int(tty.Fd()) + // if in background, we can't control the terminal + if !isForeground(fd) { + return "", ErrStatusReport + } - noecho := *t - noecho.Lflag = noecho.Lflag &^ unix.ECHO - noecho.Lflag = noecho.Lflag &^ unix.ICANON - if err := unix.IoctlSetTermios(unix.Stdout, tcsetattr, &noecho); err != nil { - return "", ErrStatusReport + t, err := unix.IoctlGetTermios(fd, tcgetattr) + if err != nil { + return "", fmt.Errorf("%s: %s", ErrStatusReport, err) + } + defer unix.IoctlSetTermios(fd, tcsetattr, t) //nolint:errcheck + + noecho := *t + noecho.Lflag = noecho.Lflag &^ unix.ECHO + noecho.Lflag = noecho.Lflag &^ unix.ICANON + if err := unix.IoctlSetTermios(fd, tcsetattr, &noecho); err != nil { + return "", fmt.Errorf("%s: %s", ErrStatusReport, err) + } } // first, send OSC query, which is ignored by terminal which do not support it - fmt.Printf("\033]%d;?\033\\", sequence) + fmt.Fprintf(tty, OSC+"%d;?"+ST, sequence) // then, query cursor position, should be supported by all terminals - fmt.Printf("\033[6n") + fmt.Fprintf(tty, CSI+"6n") // read the next response - res, isOSC, err := readNextResponse(os.Stdout) + res, isOSC, err := o.readNextResponse() if err != nil { - return "", err + return "", fmt.Errorf("%s: %s", ErrStatusReport, err) } // if this is not OSC response, then the terminal does not support it @@ -254,7 +275,7 @@ func termStatusReport(sequence int) (string, error) { } // read the cursor query response next and discard the result - _, _, err = readNextResponse(os.Stdout) + _, _, err = o.readNextResponse() if err != nil { return "", err } @@ -262,3 +283,11 @@ func termStatusReport(sequence int) (string, error) { // fmt.Println("Rcvd", res[1:]) return res, nil } + +// EnableVirtualTerminalProcessing enables virtual terminal processing on +// Windows for w and returns a function that restores w to its previous state. +// On non-Windows platforms, or if w does not refer to a terminal, then it +// returns a non-nil no-op function and no error. +func EnableVirtualTerminalProcessing(_ io.Writer) (func() error, error) { + return func() error { return nil }, nil +} diff --git a/vendor/github.com/muesli/termenv/termenv_windows.go b/vendor/github.com/muesli/termenv/termenv_windows.go index 587779c2342a3..1d9c6187120de 100644 --- a/vendor/github.com/muesli/termenv/termenv_windows.go +++ b/vendor/github.com/muesli/termenv/termenv_windows.go @@ -4,22 +4,26 @@ package termenv import ( - "os" + "fmt" "strconv" "golang.org/x/sys/windows" ) -func colorProfile() Profile { - if os.Getenv("ConEmuANSI") == "ON" { +func (o *Output) ColorProfile() Profile { + if !o.isTTY() { + return Ascii + } + + if o.environ.Getenv("ConEmuANSI") == "ON" { return TrueColor } winVersion, _, buildNumber := windows.RtlGetNtVersionNumbers() if buildNumber < 10586 || winVersion < 10 { // No ANSI support before Windows 10 build 10586. - if os.Getenv("ANSICON") != "" { - conVersion := os.Getenv("ANSICON_VER") + if o.environ.Getenv("ANSICON") != "" { + conVersion := o.environ.Getenv("ANSICON_VER") cv, err := strconv.ParseInt(conVersion, 10, 64) if err != nil || cv < 181 { // No 8 bit color support before v1.81 release. @@ -39,12 +43,12 @@ func colorProfile() Profile { return TrueColor } -func foregroundColor() Color { +func (o Output) foregroundColor() Color { // default gray return ANSIColor(7) } -func backgroundColor() Color { +func (o Output) backgroundColor() Color { // default black return ANSIColor(0) } @@ -87,3 +91,49 @@ func RestoreWindowsConsole(mode uint32) error { return windows.SetConsoleMode(handle, mode) } + +// EnableVirtualTerminalProcessing enables virtual terminal processing on +// Windows for o and returns a function that restores o to its previous state. +// On non-Windows platforms, or if o does not refer to a terminal, then it +// returns a non-nil no-op function and no error. +func EnableVirtualTerminalProcessing(o *Output) (restoreFunc func() error, err error) { + // There is nothing to restore until we set the console mode. + restoreFunc = func() error { + return nil + } + + // If o is not a tty, then there is nothing to do. + tty := o.TTY() + if tty == nil { + return + } + + // Get the current console mode. If there is an error, assume that o is not + // a terminal, discard the error, and return. + var mode uint32 + if err2 := windows.GetConsoleMode(windows.Handle(tty.Fd()), &mode); err2 != nil { + return + } + + // If virtual terminal processing is already set, then there is nothing to + // do and nothing to restore. + if mode&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING == windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING { + return + } + + // Enable virtual terminal processing. See + // https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences + if err2 := windows.SetConsoleMode(windows.Handle(tty.Fd()), mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err2 != nil { + err = fmt.Errorf("windows.SetConsoleMode: %w", err2) + return + } + + // Set the restore function. We maintain a reference to the tty in the + // closure (rather than just its handle) to ensure that the tty is not + // closed by a finalizer. + restoreFunc = func() error { + return windows.SetConsoleMode(windows.Handle(tty.Fd()), mode) + } + + return +} diff --git a/vendor/modules.txt b/vendor/modules.txt index a36eeb6d3ac8d..80383e7265685 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -130,8 +130,9 @@ github.com/armon/go-metrics # github.com/atotto/clipboard v0.1.4 ## explicit github.com/atotto/clipboard -# github.com/aws/amazon-ec2-instance-selector/v2 v2.4.1 -## explicit; go 1.18 +# github.com/aws/amazon-ec2-instance-selector/v2 v2.4.2-0.20231216170552-14d4dfcbaadf +## explicit; go 1.20 +github.com/aws/amazon-ec2-instance-selector/v2/pkg/awsapi github.com/aws/amazon-ec2-instance-selector/v2/pkg/bytequantity github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli github.com/aws/amazon-ec2-instance-selector/v2/pkg/ec2pricing @@ -186,8 +187,6 @@ github.com/aws/aws-sdk-go/service/ec2/ec2iface github.com/aws/aws-sdk-go/service/elb github.com/aws/aws-sdk-go/service/elbv2 github.com/aws/aws-sdk-go/service/kms -github.com/aws/aws-sdk-go/service/pricing -github.com/aws/aws-sdk-go/service/pricing/pricingiface github.com/aws/aws-sdk-go/service/sso github.com/aws/aws-sdk-go/service/sso/ssoiface github.com/aws/aws-sdk-go/service/ssooidc @@ -305,6 +304,11 @@ github.com/aws/aws-sdk-go-v2/service/internal/s3shared/config github.com/aws/aws-sdk-go-v2/service/kms github.com/aws/aws-sdk-go-v2/service/kms/internal/endpoints github.com/aws/aws-sdk-go-v2/service/kms/types +# github.com/aws/aws-sdk-go-v2/service/pricing v1.21.6 +## explicit; go 1.15 +github.com/aws/aws-sdk-go-v2/service/pricing +github.com/aws/aws-sdk-go-v2/service/pricing/internal/endpoints +github.com/aws/aws-sdk-go-v2/service/pricing/types # github.com/aws/aws-sdk-go-v2/service/route53 v1.40.4 ## explicit; go 1.20 github.com/aws/aws-sdk-go-v2/service/route53 @@ -370,6 +374,9 @@ github.com/aws/smithy-go/time github.com/aws/smithy-go/transport/http github.com/aws/smithy-go/transport/http/internal/io github.com/aws/smithy-go/waiter +# github.com/aymanbagabas/go-osc52/v2 v2.0.1 +## explicit; go 1.16 +github.com/aymanbagabas/go-osc52/v2 # github.com/beorn7/perks v1.0.1 ## explicit; go 1.11 github.com/beorn7/perks/quantile @@ -400,22 +407,24 @@ github.com/chai2010/gettext-go github.com/chai2010/gettext-go/mo github.com/chai2010/gettext-go/plural github.com/chai2010/gettext-go/po -# github.com/charmbracelet/bubbles v0.13.0 -## explicit; go 1.13 +# github.com/charmbracelet/bubbles v0.16.1 +## explicit; go 1.17 +github.com/charmbracelet/bubbles/cursor github.com/charmbracelet/bubbles/help github.com/charmbracelet/bubbles/key github.com/charmbracelet/bubbles/list github.com/charmbracelet/bubbles/paginator +github.com/charmbracelet/bubbles/runeutil github.com/charmbracelet/bubbles/spinner github.com/charmbracelet/bubbles/textinput github.com/charmbracelet/bubbles/viewport -# github.com/charmbracelet/bubbletea v0.21.0 -## explicit; go 1.13 +# github.com/charmbracelet/bubbletea v0.24.2 +## explicit; go 1.17 github.com/charmbracelet/bubbletea -# github.com/charmbracelet/lipgloss v0.5.0 -## explicit; go 1.15 +# github.com/charmbracelet/lipgloss v0.7.1 +## explicit; go 1.17 github.com/charmbracelet/lipgloss -# github.com/containerd/console v1.0.3 +# github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 ## explicit; go 1.13 github.com/containerd/console # github.com/containerd/containerd v1.7.12 @@ -521,7 +530,7 @@ github.com/evanphx/json-patch ## explicit; go 1.18 github.com/evanphx/json-patch/v5 github.com/evanphx/json-patch/v5/internal/json -# github.com/evertras/bubble-table v0.14.4 +# github.com/evertras/bubble-table v0.15.2 ## explicit; go 1.17 github.com/evertras/bubble-table/table # github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d @@ -857,10 +866,13 @@ github.com/magiconair/properties github.com/mailru/easyjson/buffer github.com/mailru/easyjson/jlexer github.com/mailru/easyjson/jwriter -# github.com/mattn/go-isatty v0.0.17 +# github.com/mattn/go-isatty v0.0.18 ## explicit; go 1.15 github.com/mattn/go-isatty -# github.com/mattn/go-runewidth v0.0.13 +# github.com/mattn/go-localereader v0.0.1 +## explicit +github.com/mattn/go-localereader +# github.com/mattn/go-runewidth v0.0.14 ## explicit; go 1.9 github.com/mattn/go-runewidth # github.com/miekg/dns v1.1.57 @@ -911,7 +923,7 @@ github.com/morikuni/aec ## explicit; go 1.17 github.com/muesli/ansi github.com/muesli/ansi/compressor -# github.com/muesli/cancelreader v0.2.0 +# github.com/muesli/cancelreader v0.2.2 ## explicit; go 1.17 github.com/muesli/cancelreader # github.com/muesli/reflow v0.3.0 @@ -920,8 +932,8 @@ github.com/muesli/reflow/ansi github.com/muesli/reflow/truncate github.com/muesli/reflow/wordwrap github.com/muesli/reflow/wrap -# github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 -## explicit; go 1.13 +# github.com/muesli/termenv v0.15.2 +## explicit; go 1.17 github.com/muesli/termenv # github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 ## explicit From 907e58b7d4163880ad2324ac731a0135c584c447 Mon Sep 17 00:00:00 2001 From: Peter Rifel Date: Tue, 9 Apr 2024 21:39:57 -0500 Subject: [PATCH 12/14] Update instance selector to aws-sdk-go-v2 --- cmd/kops/toolbox_instance-selector.go | 42 ++++++++++++---------- cmd/kops/toolbox_instance-selector_test.go | 3 +- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/cmd/kops/toolbox_instance-selector.go b/cmd/kops/toolbox_instance-selector.go index d08a56c585237..112b0cc29fb57 100644 --- a/cmd/kops/toolbox_instance-selector.go +++ b/cmd/kops/toolbox_instance-selector.go @@ -24,6 +24,7 @@ import ( "github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli" "github.com/aws/amazon-ec2-instance-selector/v2/pkg/selector" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/kops/pkg/apis/kops" @@ -69,8 +70,6 @@ const ( nodeVolumeSize = "node-volume-size" nodeSecurityGroups = "node-security-groups" clusterAutoscaler = "cluster-autoscaler" - usageClassSpot = "spot" - usageClassOndemand = "on-demand" dryRun = "dry-run" output = "output" cpuArchitectureAMD64 = "amd64" @@ -158,8 +157,8 @@ func NewCmdToolboxInstanceSelector(f commandutils.Factory, out io.Writer) *cobra cpuArchs := []string{cpuArchitectureAMD64, cpuArchitectureARM64} cpuArchDefault := cpuArchitectureAMD64 placementGroupStrategies := []string{"cluster", "partition", "spread"} - usageClasses := []string{usageClassSpot, usageClassOndemand} - usageClassDefault := usageClassOndemand + usageClasses := []string{string(ec2types.UsageClassTypeSpot), string(ec2types.UsageClassTypeOnDemand)} + usageClassDefault := ec2types.UsageClassTypeOnDemand outputDefault := "yaml" dryRunDefault := false clusterAutoscalerDefault := true @@ -205,7 +204,8 @@ func NewCmdToolboxInstanceSelector(f commandutils.Factory, out io.Writer) *cobra commandline.Command.RegisterFlagCompletionFunc(placementGroupStrategy, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return placementGroupStrategies, cobra.ShellCompDirectiveNoFileComp }) - commandline.StringOptionsFlag(usageClass, nil, &usageClassDefault, fmt.Sprintf("Usage class: [%s]", strings.Join(usageClasses, ", ")), usageClasses) + ucDefault := string(usageClassDefault) + commandline.StringOptionsFlag(usageClass, nil, &ucDefault, fmt.Sprintf("Usage class: [%s]", strings.Join(usageClasses, ", ")), usageClasses) commandline.Command.RegisterFlagCompletionFunc(usageClass, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return usageClasses, cobra.ShellCompDirectiveNoFileComp }) @@ -271,12 +271,10 @@ func RunToolboxInstanceSelector(ctx context.Context, f commandutils.Factory, out return fmt.Errorf("error initializing AWS client: %v", err) } - sess, err := cloud.Session() + instanceSelector, err := selector.New(ctx, cloud.Config()) if err != nil { - return err + return fmt.Errorf("error initializing instance selector: %v", err) } - instanceSelector := selector.New(sess) - igCount := options.InstanceGroupCount filters := getFilters(commandline, region, zones) mutatedFilters := filters @@ -294,7 +292,7 @@ func RunToolboxInstanceSelector(ctx context.Context, f commandutils.Factory, out if igCount != 1 { igNameForRun = fmt.Sprintf("%s%d", options.InstanceGroupName, i+1) } - selectedInstanceTypes, err := instanceSelector.Filter(mutatedFilters) + selectedInstanceTypes, err := instanceSelector.Filter(ctx, mutatedFilters) if err != nil { return fmt.Errorf("error finding matching instance types: %w", err) } @@ -398,21 +396,29 @@ func retrieveClusterRefs(ctx context.Context, f commandutils.Factory, clusterNam func getFilters(commandline *cli.CommandLineInterface, region string, zones []string) selector.Filters { flags := commandline.Flags + var cpuArch ec2types.ArchitectureType + if v, ok := flags[cpuArchitecture]; ok { + cpuArch = ec2types.ArchitectureType(v.(string)) + } + var uc ec2types.UsageClassType + if v, ok := flags[usageClass]; ok { + uc = ec2types.UsageClassType(v.(string)) + } return selector.Filters{ - VCpusRange: commandline.IntRangeMe(flags[vcpus]), + VCpusRange: commandline.Int32RangeMe(flags[vcpus]), MemoryRange: commandline.ByteQuantityRangeMe(flags[memory]), VCpusToMemoryRatio: commandline.Float64Me(flags[vcpusToMemoryRatio]), - CPUArchitecture: commandline.StringMe(flags[cpuArchitecture]), - GpusRange: commandline.IntRangeMe(flags[gpus]), + CPUArchitecture: &cpuArch, + GpusRange: commandline.Int32RangeMe(flags[gpus]), GpuMemoryRange: commandline.ByteQuantityRangeMe(flags[gpuMemory]), PlacementGroupStrategy: commandline.StringMe(flags[placementGroupStrategy]), - UsageClass: commandline.StringMe(flags[usageClass]), + UsageClass: &uc, EnaSupport: commandline.BoolMe(flags[enaSupport]), Burstable: commandline.BoolMe(flags[burstSupport]), Region: commandline.StringMe(region), AvailabilityZones: commandline.StringSliceMe(zones), MaxResults: commandline.IntMe(flags[maxResults]), - NetworkInterfaces: commandline.IntRangeMe(flags[networkInterfaces]), + NetworkInterfaces: commandline.Int32RangeMe(flags[networkInterfaces]), NetworkPerformance: commandline.IntRangeMe(flags[networkPerformance]), AllowList: commandline.RegexMe(flags[allowList]), DenyList: commandline.RegexMe(flags[denyList]), @@ -510,11 +516,11 @@ func decorateWithInstanceGroupSpecs(instanceGroup *kops.InstanceGroup, instanceG } // decorateWithMixedInstancesPolicy adds a mixed instance policy based on usageClass to the instance-group -func decorateWithMixedInstancesPolicy(instanceGroup *kops.InstanceGroup, usageClass string, instanceSelections []string) (*kops.InstanceGroup, error) { +func decorateWithMixedInstancesPolicy(instanceGroup *kops.InstanceGroup, usageClass ec2types.UsageClassType, instanceSelections []string) (*kops.InstanceGroup, error) { ig := instanceGroup ig.Spec.MachineType = instanceSelections[0] - if usageClass == usageClassSpot { + if usageClass == ec2types.UsageClassTypeSpot { ondemandBase := int64(0) ondemandAboveBase := int64(0) spotAllocationStrategy := "capacity-optimized" @@ -524,7 +530,7 @@ func decorateWithMixedInstancesPolicy(instanceGroup *kops.InstanceGroup, usageCl OnDemandAboveBase: &ondemandAboveBase, SpotAllocationStrategy: &spotAllocationStrategy, } - } else if usageClass == usageClassOndemand { + } else if usageClass == ec2types.UsageClassTypeOnDemand { ig.Spec.MixedInstancesPolicy = &kops.MixedInstancesPolicySpec{ Instances: instanceSelections, } diff --git a/cmd/kops/toolbox_instance-selector_test.go b/cmd/kops/toolbox_instance-selector_test.go index 1d56202692816..ee1061f09d0bb 100644 --- a/cmd/kops/toolbox_instance-selector_test.go +++ b/cmd/kops/toolbox_instance-selector_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/aws/amazon-ec2-instance-selector/v2/pkg/cli" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "k8s.io/kops/pkg/apis/kops" ) @@ -175,7 +176,7 @@ func TestDecorateWithInstanceGroupSpecs(t *testing.T) { func TestDecorateWithMixedInstancesPolicy(t *testing.T) { selectedInstanceTypes := []string{"m3.medium", "m4.medium", "m5.medium"} - usageClasses := []string{"spot", "on-demand"} + usageClasses := []ec2types.UsageClassType{ec2types.UsageClassTypeSpot, ec2types.UsageClassTypeOnDemand} for _, usageClass := range usageClasses { actualIG, err := decorateWithMixedInstancesPolicy(&kops.InstanceGroup{}, usageClass, selectedInstanceTypes) if err != nil { From 8180a600b54b34ccfac2b9d0269d26a5886a5578 Mon Sep 17 00:00:00 2001 From: Peter Rifel Date: Wed, 10 Apr 2024 23:14:38 -0400 Subject: [PATCH 13/14] Fix panic --- pkg/resources/aws/aws.go | 2 +- pkg/resources/aws/subnet.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/resources/aws/aws.go b/pkg/resources/aws/aws.go index c1096839c4096..ef40bb13ff4cc 100644 --- a/pkg/resources/aws/aws.go +++ b/pkg/resources/aws/aws.go @@ -489,7 +489,7 @@ func DumpInstance(op *resources.DumpOperation, r *resources.Resource) error { data["raw"] = r.Obj op.Dump.Resources = append(op.Dump.Resources, data) - ec2Instance := r.Obj.(*ec2types.Instance) + ec2Instance := r.Obj.(ec2types.Instance) i := &resources.Instance{ Name: r.ID, } diff --git a/pkg/resources/aws/subnet.go b/pkg/resources/aws/subnet.go index 261c32c6454f0..6160bd745ef9d 100644 --- a/pkg/resources/aws/subnet.go +++ b/pkg/resources/aws/subnet.go @@ -30,7 +30,7 @@ func DumpSubnet(op *resources.DumpOperation, r *resources.Resource) error { data["raw"] = r.Obj op.Dump.Resources = append(op.Dump.Resources, data) - ec2Subnet := r.Obj.(*ec2types.Subnet) + ec2Subnet := r.Obj.(ec2types.Subnet) s := &resources.Subnet{ ID: aws.ToString(ec2Subnet.SubnetId), Zone: aws.ToString(ec2Subnet.AvailabilityZone), From 78d8fb6cdd3e330aba86298701d1e4667bc17d52 Mon Sep 17 00:00:00 2001 From: Peter Rifel Date: Wed, 10 Apr 2024 23:14:46 -0400 Subject: [PATCH 14/14] Revoke security groups via Rule ID --- pkg/resources/aws/securitygroup.go | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/pkg/resources/aws/securitygroup.go b/pkg/resources/aws/securitygroup.go index a181b1f117653..c2df8f6192c99 100644 --- a/pkg/resources/aws/securitygroup.go +++ b/pkg/resources/aws/securitygroup.go @@ -56,16 +56,36 @@ func DeleteSecurityGroup(cloud fi.Cloud, t *resources.Resource) error { if len(response.SecurityGroups) != 1 { return fmt.Errorf("found multiple SecurityGroups with ID %q", id) } - sg := response.SecurityGroups[0] - if len(sg.IpPermissions) != 0 { + ruleReqest := &ec2.DescribeSecurityGroupRulesInput{ + Filters: []ec2types.Filter{ + {Name: aws.String("group-id"), Values: []string{id}}, + }, + } + ruleResp, err := c.EC2().DescribeSecurityGroupRules(ctx, ruleReqest) + if err != nil { + if awsup.AWSErrorCode(err) == "InvalidGroup.NotFound" { + klog.V(2).Infof("Got InvalidGroup.NotFound error describing rules for SecurityGroup %q; will treat as already-deleted", id) + return nil + } + return fmt.Errorf("error describing SecurityGroup rules %q: %v", id, err) + } + + ingressRuleIDs := make([]string, 0) + for _, rule := range ruleResp.SecurityGroupRules { + if !aws.ToBool(rule.IsEgress) { + ingressRuleIDs = append(ingressRuleIDs, aws.ToString(rule.SecurityGroupRuleId)) + } + } + + if len(ingressRuleIDs) != 0 { revoke := &ec2.RevokeSecurityGroupIngressInput{ - GroupId: &id, - IpPermissions: sg.IpPermissions, + GroupId: aws.String(id), + SecurityGroupRuleIds: ingressRuleIDs, } _, err = c.EC2().RevokeSecurityGroupIngress(ctx, revoke) if err != nil { - return fmt.Errorf("cannot revoke ingress for ID %q: %v", id, err) + return fmt.Errorf("cannot revoke ingress for ID %q with rule IDs %v: %v", id, ingressRuleIDs, err) } } }