2026-05-11 22:37:05 -07:00
// Copyright IBM Corp. 2021, 2026
2023-06-05 09:52:48 -07:00
// SPDX-License-Identifier: MPL-2.0
2023-03-20 11:02:17 -07:00
package host
2021-09-22 17:00:37 -07:00
import (
"context"
2021-10-06 12:47:05 -07:00
"errors"
2021-09-24 12:47:57 -07:00
"fmt"
2021-10-07 12:40:55 -07:00
"sort"
2021-09-22 17:00:37 -07:00
"testing"
2024-09-12 16:46:55 +01:00
"time"
2021-09-22 17:00:37 -07:00
2023-07-04 11:03:24 -07:00
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
2024-09-12 16:46:55 +01:00
"github.com/aws/aws-sdk-go-v2/service/iam"
iamTypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
"github.com/google/go-cmp/cmp"
2023-06-02 08:55:14 -07:00
"github.com/hashicorp/boundary-plugin-aws/internal/credential"
2021-09-22 17:00:37 -07:00
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs"
2021-10-06 16:53:11 -07:00
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets"
2021-09-22 17:00:37 -07:00
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
2023-09-22 12:26:08 -07:00
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
2021-09-22 17:00:37 -07:00
"github.com/stretchr/testify/require"
2022-03-10 11:00:57 -08:00
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
2024-09-12 16:46:55 +01:00
"google.golang.org/protobuf/testing/protocmp"
2021-09-22 17:00:37 -07:00
"google.golang.org/protobuf/types/known/structpb"
)
2024-09-12 16:46:55 +01:00
func TestPluginOnCreateCatalogSuccess ( t * testing . T ) {
tests := [] struct {
name string
req * pb . OnCreateCatalogRequest
credOpts [] credential . AwsCredentialPersistedStateOption
catalogOpts [] awsCatalogPersistedStateOption
expRsp * pb . OnCreateCatalogResponse
}{
{
name : "usingStaticCredentials" ,
req : & pb . OnCreateCatalogRequest {
Catalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
},
},
},
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIAfoo" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bar" ),
},
},
},
},
catalogOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesOutput ( & ec2 . DescribeInstancesOutput {}),
)),
},
expRsp : & pb . OnCreateCatalogResponse {
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIAfoo" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bar" ),
},
},
},
},
},
{
name : "usingRotatedStaticCredentials" ,
req : & pb . OnCreateCatalogRequest {
Catalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
},
},
},
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIAfoo" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bar" ),
},
},
},
},
catalogOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesOutput ( & ec2 . DescribeInstancesOutput {}),
)),
},
credOpts : [] credential . AwsCredentialPersistedStateOption {
credential . WithStateTestOpts ([] awsutil . Option {
awsutil . WithSTSAPIFunc ( awsutil . NewMockSTS ()),
awsutil . WithIAMAPIFunc (
awsutil . NewMockIAM (
awsutil . WithGetUserOutput (
& iam . GetUserOutput {
User : & iamTypes . User {
Arn : aws . String ( "arn:aws:iam::123456789012:user/JohnDoe" ),
UserId : aws . String ( "AIDAJQABLZS4A3QDU576Q" ),
UserName : aws . String ( "JohnDoe" ),
},
},
),
awsutil . WithCreateAccessKeyOutput (
& iam . CreateAccessKeyOutput {
AccessKey : & iamTypes . AccessKey {
AccessKeyId : aws . String ( "AKIArotated" ),
SecretAccessKey : aws . String ( "rotated_secret" ),
UserName : aws . String ( "JohnDoe" ),
},
},
),
),
),
}),
},
expRsp : & pb . OnCreateCatalogResponse {
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIArotated" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "rotated_secret" ),
},
},
},
},
},
{
name : "usingAssumeRole" ,
req : & pb . OnCreateCatalogRequest {
Catalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
credential . ConstRoleArn : structpb . NewStringValue ( "arn:0123:test:rolearn" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
},
},
},
},
},
catalogOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesOutput ( & ec2 . DescribeInstancesOutput {}),
)),
},
expRsp : & pb . OnCreateCatalogResponse { Persisted : & pb . HostCatalogPersisted { Secrets : & structpb . Struct {}}},
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
p := & HostPlugin {
testCredStateOpts : tt . credOpts ,
testCatalogStateOpts : tt . catalogOpts ,
}
rsp , err := p . OnCreateCatalog ( context . Background (), tt . req )
require . NoError ( t , err )
delete ( rsp . GetPersisted (). GetSecrets (). GetFields (), credential . ConstCredsLastRotatedTime )
require . Empty ( t , cmp . Diff ( tt . expRsp , rsp , protocmp . Transform ()))
})
}
}
func TestPluginOnUpdateCatalogSuccess ( t * testing . T ) {
// static -> static rotated
// static -> dynamic
// static rotated -> static
// static rotated -> dynamic
// dynamic -> static
// dynamic -> static rotated
tests := [] struct {
name string
req * pb . OnUpdateCatalogRequest
credOpts [] credential . AwsCredentialPersistedStateOption
catalogOpts [] awsCatalogPersistedStateOption
expRsp * pb . OnUpdateCatalogResponse
}{
{
name : "staticCredentialToStaticRotated" ,
req : & pb . OnUpdateCatalogRequest {
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
},
},
},
},
NewCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
},
},
},
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIAnewcred" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "newcred" ),
},
},
},
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIApersisted" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "persisted" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( time . Time {}. Format ( time . RFC3339Nano )),
},
},
},
},
catalogOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesOutput ( & ec2 . DescribeInstancesOutput {}),
)),
},
credOpts : [] credential . AwsCredentialPersistedStateOption {
credential . WithStateTestOpts ([] awsutil . Option {
awsutil . WithSTSAPIFunc ( awsutil . NewMockSTS ()),
awsutil . WithIAMAPIFunc (
awsutil . NewMockIAM (
awsutil . WithGetUserOutput (
& iam . GetUserOutput {
User : & iamTypes . User {
Arn : aws . String ( "arn:aws:iam::123456789012:user/JohnDoe" ),
UserId : aws . String ( "AIDAJQABLZS4A3QDU576Q" ),
UserName : aws . String ( "JohnDoe" ),
},
},
),
awsutil . WithCreateAccessKeyOutput (
& iam . CreateAccessKeyOutput {
AccessKey : & iamTypes . AccessKey {
AccessKeyId : aws . String ( "AKIArotated" ),
SecretAccessKey : aws . String ( "rotated_secret" ),
UserName : aws . String ( "JohnDoe" ),
},
},
),
),
),
}),
},
expRsp : & pb . OnUpdateCatalogResponse {
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIArotated" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "rotated_secret" ),
},
},
},
},
},
{
name : "staticCredentialToAssumeRole" ,
req : & pb . OnUpdateCatalogRequest {
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
},
},
},
},
NewCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
credential . ConstRoleArn : structpb . NewStringValue ( "arn:0123:test:rolearn" ),
},
},
},
},
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIApersisted" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "persisted" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( time . Time {}. Format ( time . RFC3339Nano )),
},
},
},
},
catalogOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesOutput ( & ec2 . DescribeInstancesOutput {}),
)),
},
expRsp : & pb . OnUpdateCatalogResponse {
Persisted : & pb . HostCatalogPersisted { Secrets : & structpb . Struct {}},
},
},
{
name : "staticRotatedCredentialToStatic" ,
req : & pb . OnUpdateCatalogRequest {
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
},
},
},
},
NewCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
},
},
},
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIAnotrotated" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "notrotated" ),
},
},
},
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIApersisted" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "persisted" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( time . Now (). Format ( time . RFC3339Nano )),
},
},
},
},
catalogOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesOutput ( & ec2 . DescribeInstancesOutput {}),
)),
},
credOpts : [] credential . AwsCredentialPersistedStateOption {
credential . WithStateTestOpts ([] awsutil . Option {
awsutil . WithIAMAPIFunc ( awsutil . NewMockIAM ()),
}),
},
expRsp : & pb . OnUpdateCatalogResponse {
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIAnotrotated" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "notrotated" ),
},
},
},
},
},
{
name : "staticRotatedCredentialToAssumeRole" ,
req : & pb . OnUpdateCatalogRequest {
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
},
},
},
},
NewCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
credential . ConstRoleArn : structpb . NewStringValue ( "arn:0123:test:rolearn" ),
},
},
},
},
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIApersisted" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "persisted" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( time . Now (). Format ( time . RFC3339Nano )),
},
},
},
},
catalogOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesOutput ( & ec2 . DescribeInstancesOutput {}),
)),
},
credOpts : [] credential . AwsCredentialPersistedStateOption {
credential . WithStateTestOpts ([] awsutil . Option {
awsutil . WithIAMAPIFunc ( awsutil . NewMockIAM ()),
}),
},
expRsp : & pb . OnUpdateCatalogResponse {
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {},
},
},
},
{
name : "assumeRoleCredentialToStatic" ,
req : & pb . OnUpdateCatalogRequest {
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
credential . ConstRoleArn : structpb . NewStringValue ( "arn:0123:test:rolearn" ),
},
},
},
},
NewCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
},
},
},
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIAnew" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "new" ),
},
},
},
Persisted : & pb . HostCatalogPersisted { Secrets : & structpb . Struct {}},
},
catalogOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesOutput ( & ec2 . DescribeInstancesOutput {}),
)),
},
expRsp : & pb . OnUpdateCatalogResponse {
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIAnew" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "new" ),
},
},
},
},
},
{
name : "assumeRoleCredentialToStaticRotated" ,
req : & pb . OnUpdateCatalogRequest {
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
credential . ConstRoleArn : structpb . NewStringValue ( "arn:0123:test:rolearn" ),
},
},
},
},
NewCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
},
},
},
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIAnew" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "new" ),
},
},
},
Persisted : & pb . HostCatalogPersisted { Secrets : & structpb . Struct {}},
},
catalogOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesOutput ( & ec2 . DescribeInstancesOutput {}),
)),
},
credOpts : [] credential . AwsCredentialPersistedStateOption {
credential . WithStateTestOpts ([] awsutil . Option {
awsutil . WithSTSAPIFunc ( awsutil . NewMockSTS ()),
awsutil . WithIAMAPIFunc (
awsutil . NewMockIAM (
awsutil . WithGetUserOutput (
& iam . GetUserOutput {
User : & iamTypes . User {
Arn : aws . String ( "arn:aws:iam::123456789012:user/JohnDoe" ),
UserId : aws . String ( "AIDAJQABLZS4A3QDU576Q" ),
UserName : aws . String ( "JohnDoe" ),
},
},
),
awsutil . WithCreateAccessKeyOutput (
& iam . CreateAccessKeyOutput {
AccessKey : & iamTypes . AccessKey {
AccessKeyId : aws . String ( "AKIArotated" ),
SecretAccessKey : aws . String ( "rotated_secret" ),
UserName : aws . String ( "JohnDoe" ),
},
},
),
),
),
}),
},
expRsp : & pb . OnUpdateCatalogResponse {
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIArotated" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "rotated_secret" ),
},
},
},
},
},
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
p := & HostPlugin {
testCredStateOpts : tt . credOpts ,
testCatalogStateOpts : tt . catalogOpts ,
}
rsp , err := p . OnUpdateCatalog ( context . Background (), tt . req )
require . NoError ( t , err )
delete ( rsp . GetPersisted (). GetSecrets (). GetFields (), credential . ConstCredsLastRotatedTime )
require . Empty ( t , cmp . Diff ( tt . expRsp , rsp , protocmp . Transform ()))
})
}
}
2021-10-07 12:40:55 -07:00
func TestPluginOnCreateCatalogErr ( t * testing . T ) {
2021-09-27 16:47:51 -07:00
cases := [] struct {
2022-03-10 11:00:57 -08:00
name string
req * pb . OnCreateCatalogRequest
2023-03-20 11:02:17 -07:00
credOpts [] credential . AwsCredentialPersistedStateOption
catalogOpts [] awsCatalogPersistedStateOption
2022-03-10 11:00:57 -08:00
expectedErrContains string
expectedErrCode codes . Code
2021-09-27 16:47:51 -07:00
}{
{
2022-03-10 11:00:57 -08:00
name : "nil catalog" ,
req : & pb . OnCreateCatalogRequest {},
2024-09-12 16:46:55 +01:00
expectedErrContains : "catalog is required" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-09-27 16:47:51 -07:00
},
{
name : "nil attributes" ,
req : & pb . OnCreateCatalogRequest {
Catalog : & hostcatalogs . HostCatalog {
Secrets : new ( structpb . Struct ),
},
},
2022-03-10 11:00:57 -08:00
expectedErrContains : "attributes are required" ,
expectedErrCode : codes . InvalidArgument ,
2021-09-27 16:47:51 -07:00
},
{
2021-11-16 17:29:17 -08:00
name : "error reading attributes" ,
2021-09-27 16:47:51 -07:00
req : & pb . OnCreateCatalogRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Secrets : new ( structpb . Struct ),
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : new ( structpb . Struct ),
},
2021-09-27 16:47:51 -07:00
},
},
2022-03-10 11:00:57 -08:00
expectedErrContains : "missing required value \"region\"" ,
expectedErrCode : codes . InvalidArgument ,
2021-09-27 16:47:51 -07:00
},
{
2024-09-12 16:46:55 +01:00
name : "failed to build credentials config" ,
2021-09-27 16:47:51 -07:00
req : & pb . OnCreateCatalogRequest {
Catalog : & hostcatalogs . HostCatalog {
2024-09-12 16:46:55 +01:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
},
},
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2024-09-12 16:46:55 +01:00
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
credential . ConstRoleArn : structpb . NewStringValue ( "arn:0123:test:rolearn" ),
2023-03-20 11:02:17 -07:00
},
},
2022-08-19 00:42:19 -04:00
},
2021-09-27 16:47:51 -07:00
},
},
2024-09-12 16:46:55 +01:00
expectedErrContains : "Error in the secrets provided: [attributes.role_arn: conflicts with access_key_id and secret_access_key values, secrets.access_key_id: conflicts with role_arn value, secrets.secret_access_key: conflicts with role_arn value]" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-09-27 16:47:51 -07:00
},
{
2024-09-12 16:46:55 +01:00
name : "credential persisted state setup error" ,
2021-09-27 16:47:51 -07:00
req : & pb . OnCreateCatalogRequest {
Catalog : & hostcatalogs . HostCatalog {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
2026-05-27 13:12:34 -07:00
},
},
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-09-27 16:47:51 -07:00
},
},
2024-09-12 16:46:55 +01:00
credOpts : [] credential . AwsCredentialPersistedStateOption {
func ( s * credential . AwsCredentialPersistedState ) error {
2021-10-06 12:47:05 -07:00
return errors . New ( testOptionErr )
},
},
2022-03-10 11:00:57 -08:00
expectedErrContains : fmt . Sprintf ( "error setting up persisted state: %s" , testOptionErr ),
expectedErrCode : codes . InvalidArgument ,
2021-10-06 12:47:05 -07:00
},
{
name : "rotation error" ,
req : & pb . OnCreateCatalogRequest {
Catalog : & hostcatalogs . HostCatalog {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
2026-05-27 13:12:34 -07:00
},
},
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 12:47:05 -07:00
},
},
2023-03-20 11:02:17 -07:00
credOpts : [] credential . AwsCredentialPersistedStateOption {
credential . WithStateTestOpts ([] awsutil . Option {
2021-10-06 12:47:05 -07:00
awsutil . WithIAMAPIFunc (
awsutil . NewMockIAM (
awsutil . WithGetUserError ( errors . New ( testGetUserErr )),
),
),
}),
},
2024-10-21 09:55:51 -07:00
expectedErrContains : "rotating credentials" ,
2024-09-12 16:46:55 +01:00
expectedErrCode : codes . Unknown ,
},
{
name : "catalog persisted state error" ,
req : & pb . OnCreateCatalogRequest {
Catalog : & hostcatalogs . HostCatalog {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
2026-05-27 13:12:34 -07:00
},
},
2024-09-12 16:46:55 +01:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
},
},
},
},
},
catalogOpts : [] awsCatalogPersistedStateOption {
func ( s * awsCatalogPersistedState ) error {
return fmt . Errorf ( "oops there was an error" )
},
},
expectedErrContains : "error setting up persisted state: oops there was an error" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 12:47:05 -07:00
},
{
name : "validation error" ,
req : & pb . OnCreateCatalogRequest {
Catalog : & hostcatalogs . HostCatalog {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
2026-05-27 13:12:34 -07:00
},
},
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 12:47:05 -07:00
},
},
2024-09-12 16:46:55 +01:00
catalogOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesError ( errors . New ( testDescribeInstancesError )),
)),
2021-10-06 12:47:05 -07:00
},
2026-09-18 18:03:48 -07:00
// New: checkHosts returns the DescribeInstances error as-is rather than
2026-09-18 17:56:42 -07:00
// wrapping it in a gRPC FailedPrecondition status like the old
// dryRunValidation did. A plain Go error maps to codes.Unknown.
2026-09-18 18:31:22 -04:00
expectedErrContains : testDescribeInstancesError ,
expectedErrCode : codes . Unknown ,
2021-09-27 16:47:51 -07:00
},
}
for _ , tc := range cases {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
require := require . New ( t )
2023-03-20 11:02:17 -07:00
p := & HostPlugin {
testCredStateOpts : tc . credOpts ,
testCatalogStateOpts : tc . catalogOpts ,
2021-10-06 12:47:05 -07:00
}
2021-09-27 16:47:51 -07:00
_ , err := p . OnCreateCatalog ( context . Background (), tc . req )
2024-07-10 17:59:02 +01:00
require . Error ( err )
2022-03-10 11:00:57 -08:00
require . Contains ( err . Error (), tc . expectedErrContains )
require . Equal ( status . Code ( err ). String (), tc . expectedErrCode . String ())
2021-09-27 16:47:51 -07:00
})
}
}
2021-10-07 12:40:55 -07:00
func TestPluginOnUpdateCatalogErr ( t * testing . T ) {
2021-10-06 12:47:05 -07:00
cases := [] struct {
2022-03-10 11:00:57 -08:00
name string
req * pb . OnUpdateCatalogRequest
2023-03-20 11:02:17 -07:00
credOpts [] credential . AwsCredentialPersistedStateOption
catalogOpts [] awsCatalogPersistedStateOption
2022-03-10 11:00:57 -08:00
expectedErrContains string
expectedErrCode codes . Code
2021-10-06 12:47:05 -07:00
}{
{
2024-09-12 16:46:55 +01:00
name : "nil current catalog" ,
req : & pb . OnUpdateCatalogRequest {
CurrentCatalog : nil ,
NewCatalog : & hostcatalogs . HostCatalog {},
Persisted : & pb . HostCatalogPersisted {},
},
expectedErrContains : "current catalog is required" ,
expectedErrCode : codes . InvalidArgument ,
},
{
name : "nil new catalog" ,
req : & pb . OnUpdateCatalogRequest {
CurrentCatalog : & hostcatalogs . HostCatalog {},
NewCatalog : nil ,
Persisted : & pb . HostCatalogPersisted {},
},
expectedErrContains : "new catalog is required" ,
expectedErrCode : codes . InvalidArgument ,
},
{
name : "nil current attributes" ,
req : & pb . OnUpdateCatalogRequest {
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : nil ,
},
NewCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {},
},
},
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {},
},
},
expectedErrContains : "current catalog attributes are required" ,
expectedErrCode : codes . InvalidArgument ,
},
{
name : "error reading current catalog attributes" ,
req : & pb . OnUpdateCatalogRequest {
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {},
},
},
NewCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {},
},
},
},
expectedErrContains : "missing required value \"region\"" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 12:47:05 -07:00
},
{
name : "nil new attributes" ,
req : & pb . OnUpdateCatalogRequest {
2024-09-12 16:46:55 +01:00
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
},
},
},
},
2021-10-06 12:47:05 -07:00
NewCatalog : & hostcatalogs . HostCatalog {
2024-09-12 16:46:55 +01:00
Attrs : nil ,
},
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {},
2021-10-06 12:47:05 -07:00
},
},
2024-09-12 16:46:55 +01:00
expectedErrContains : "new catalog attributes are required" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 12:47:05 -07:00
},
2021-11-16 17:29:17 -08:00
{
2024-09-12 16:46:55 +01:00
name : "error reading new catalog attributes" ,
2021-11-16 17:29:17 -08:00
req : & pb . OnUpdateCatalogRequest {
2024-09-12 16:46:55 +01:00
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
},
},
},
},
2021-11-16 17:29:17 -08:00
NewCatalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Secrets : new ( structpb . Struct ),
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : new ( structpb . Struct ),
},
2021-11-16 17:29:17 -08:00
},
},
2022-03-10 11:00:57 -08:00
expectedErrContains : "missing required value \"region\"" ,
expectedErrCode : codes . InvalidArgument ,
2021-11-16 17:29:17 -08:00
},
2021-10-06 12:47:05 -07:00
{
2024-09-12 16:46:55 +01:00
name : "credential persisted state setup error" ,
req : & pb . OnUpdateCatalogRequest {
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
},
},
},
},
NewCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
},
},
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
},
},
credOpts : [] credential . AwsCredentialPersistedStateOption {
func ( s * credential . AwsCredentialPersistedState ) error {
return errors . New ( testOptionErr )
},
},
expectedErrContains : fmt . Sprintf ( "error loading persisted state: %s" , testOptionErr ),
expectedErrCode : codes . InvalidArgument ,
},
{
name : "get new credentials config error" ,
req : & pb . OnUpdateCatalogRequest {
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
},
},
},
},
NewCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
credential . ConstRoleArn : structpb . NewStringValue ( "arn:0123:test:rolearn" ),
},
},
},
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIAfoo" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bar" ),
},
},
},
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
},
},
expectedErrContains : "Error in the secrets provided: [attributes.role_arn: conflicts with access_key_id and secret_access_key values, secrets.access_key_id: conflicts with role_arn value, secrets.secret_access_key: conflicts with role_arn value]" ,
expectedErrCode : codes . InvalidArgument ,
},
{
name : "new incoming static credential update dry run error" ,
req : & pb . OnUpdateCatalogRequest {
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
},
},
},
},
NewCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
},
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIAfoo" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bar" ),
},
},
},
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
},
},
catalogOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesError ( errors . New ( testDescribeInstancesError )),
)),
},
2026-09-18 18:03:48 -07:00
// New: checkHosts returns the DesribeInstances errror as-is rather than wrapping it
2026-09-18 17:56:42 -07:00
// in a gRpc FailedPrecodndition status like the old dryRunValidiation did.
// A plain Go error maps to codeds.Unknown.
2026-09-18 18:31:22 -04:00
expectedErrContains : testDescribeInstancesError ,
expectedErrCode : codes . Unknown ,
2024-09-12 16:46:55 +01:00
},
{
name : "new incoming dynamic credential update dry run error" ,
2021-10-06 12:47:05 -07:00
req : & pb . OnUpdateCatalogRequest {
2024-09-12 16:46:55 +01:00
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
},
},
},
},
2021-10-06 12:47:05 -07:00
NewCatalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2024-09-12 16:46:55 +01:00
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
credential . ConstRoleArn : structpb . NewStringValue ( "arn:0123:test:rolearn" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
2023-03-20 11:02:17 -07:00
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 12:47:05 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 12:47:05 -07:00
},
},
2023-03-20 11:02:17 -07:00
catalogOpts : [] awsCatalogPersistedStateOption {
2024-09-12 16:46:55 +01:00
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesError ( errors . New ( testDescribeInstancesError )),
)),
2021-10-06 12:47:05 -07:00
},
2026-09-18 18:03:48 -07:00
// New: checkHosts returns the DescribeInstances error as-is rather than wrapping it
2026-09-18 17:56:42 -07:00
// in a gRPC FailedPrecondition status like the old dryRunValidiation did.
// A plain Go error maps to codes.Unknown.
2026-09-18 18:31:22 -04:00
expectedErrContains : testDescribeInstancesError ,
expectedErrCode : codes . Unknown ,
2021-10-06 12:47:05 -07:00
},
{
2024-09-12 16:46:55 +01:00
name : "replace creds error" ,
2021-10-06 12:47:05 -07:00
req : & pb . OnUpdateCatalogRequest {
2024-09-12 16:46:55 +01:00
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-east-1" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
},
},
},
},
2021-10-06 12:47:05 -07:00
NewCatalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
},
},
2022-08-19 00:42:19 -04:00
},
2024-09-12 16:46:55 +01:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIAfoo" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bar" ),
},
},
2021-10-06 12:47:05 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 12:47:05 -07:00
},
},
2024-09-12 16:46:55 +01:00
catalogOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesOutput ( & ec2 . DescribeInstancesOutput {}),
)),
},
credOpts : [] credential . AwsCredentialPersistedStateOption {
credential . WithStateTestOpts ([] awsutil . Option {
awsutil . WithIAMAPIFunc (
awsutil . NewMockIAM (
awsutil . WithDeleteAccessKeyError ( fmt . Errorf ( "oops delete fail" )),
),
),
}),
},
2024-10-21 09:55:51 -07:00
expectedErrContains : "deleting credentials" ,
2024-09-12 16:46:55 +01:00
expectedErrCode : codes . Unknown ,
2021-10-06 12:47:05 -07:00
},
{
2024-09-12 16:46:55 +01:00
name : "cannot disable rotation for already rotated credentials" ,
2021-10-06 12:47:05 -07:00
req : & pb . OnUpdateCatalogRequest {
2024-09-12 16:46:55 +01:00
CurrentCatalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 12:47:05 -07:00
},
2024-09-12 16:46:55 +01:00
NewCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
},
},
},
},
2021-10-06 12:47:05 -07:00
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 12:47:05 -07:00
},
},
2024-09-12 16:46:55 +01:00
expectedErrContains : "cannot disable rotation for already-rotated credentials" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 12:47:05 -07:00
},
{
2024-09-12 16:46:55 +01:00
name : "rotation error" ,
2021-10-06 12:47:05 -07:00
req : & pb . OnUpdateCatalogRequest {
2024-09-12 16:46:55 +01:00
CurrentCatalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
credential . ConstDisableCredentialRotation : structpb . NewBoolValue ( true ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 12:47:05 -07:00
},
2024-09-12 16:46:55 +01:00
NewCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
},
},
2021-10-06 12:47:05 -07:00
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2024-09-12 16:46:55 +01:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
2023-03-20 11:02:17 -07:00
},
},
2021-10-06 12:47:05 -07:00
},
},
2023-03-20 11:02:17 -07:00
credOpts : [] credential . AwsCredentialPersistedStateOption {
credential . WithStateTestOpts ([] awsutil . Option {
2021-10-06 12:47:05 -07:00
awsutil . WithIAMAPIFunc (
awsutil . NewMockIAM (
2024-09-12 16:46:55 +01:00
awsutil . WithGetUserError ( errors . New ( testGetUserErr )),
2021-10-06 12:47:05 -07:00
),
),
}),
},
2024-10-21 09:55:51 -07:00
expectedErrContains : "rotating credentials" ,
2024-09-12 16:46:55 +01:00
expectedErrCode : codes . Unknown ,
2021-10-06 12:47:05 -07:00
},
{
2024-09-12 16:46:55 +01:00
name : "final dry run fail" ,
2021-10-06 12:47:05 -07:00
req : & pb . OnUpdateCatalogRequest {
2024-09-12 16:46:55 +01:00
CurrentCatalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
},
},
2021-10-06 12:47:05 -07:00
NewCatalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 12:47:05 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2024-09-12 16:46:55 +01:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
2023-03-20 11:02:17 -07:00
},
},
2021-10-06 12:47:05 -07:00
},
},
2024-09-12 16:46:55 +01:00
catalogOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesError ( fmt . Errorf ( "oops there was an error" )),
)),
2021-10-06 12:47:05 -07:00
},
2026-09-18 18:03:48 -07:00
// New: checkHosts returns the DescribeInstances error as-is rather than wrapping it
2026-09-18 17:56:42 -07:00
// in a gRPC FailedPrecondition status like the old dryRunValidiation did.
// A plain Go error maps to codes.Unknown.
2026-09-18 18:31:22 -04:00
expectedErrContains : "oops there was an error" ,
expectedErrCode : codes . Unknown ,
2021-10-06 12:47:05 -07:00
},
2021-09-27 16:47:51 -07:00
}
2021-10-06 12:47:05 -07:00
for _ , tc := range cases {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
require := require . New ( t )
2023-03-20 11:02:17 -07:00
p := & HostPlugin {
testCredStateOpts : tc . credOpts ,
testCatalogStateOpts : tc . catalogOpts ,
2021-10-06 12:47:05 -07:00
}
_ , err := p . OnUpdateCatalog ( context . Background (), tc . req )
2024-07-10 17:59:02 +01:00
require . Error ( err )
2022-03-10 11:00:57 -08:00
require . Contains ( err . Error (), tc . expectedErrContains )
require . Equal ( status . Code ( err ). String (), tc . expectedErrCode . String ())
2021-10-06 12:47:05 -07:00
})
}
2021-09-27 16:47:51 -07:00
}
2021-10-06 16:53:11 -07:00
2021-10-07 12:40:55 -07:00
func TestPluginOnDeleteCatalogErr ( t * testing . T ) {
2021-10-06 16:53:11 -07:00
cases := [] struct {
2022-03-10 11:00:57 -08:00
name string
req * pb . OnDeleteCatalogRequest
2023-03-20 11:02:17 -07:00
credOpts [] credential . AwsCredentialPersistedStateOption
catalogOpts [] awsCatalogPersistedStateOption
2022-03-10 11:00:57 -08:00
expectedErrContains string
expectedErrCode codes . Code
2021-10-06 16:53:11 -07:00
}{
{
name : "persisted state setup error" ,
req : & pb . OnDeleteCatalogRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
},
2023-03-20 11:02:17 -07:00
catalogOpts : [] awsCatalogPersistedStateOption {
2021-10-06 16:53:11 -07:00
func ( s * awsCatalogPersistedState ) error {
return errors . New ( testOptionErr )
},
},
2022-03-10 11:00:57 -08:00
expectedErrContains : fmt . Sprintf ( "error loading persisted state: %s" , testOptionErr ),
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
name : "delete error" ,
req : & pb . OnDeleteCatalogRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
},
2023-03-20 11:02:17 -07:00
credOpts : [] credential . AwsCredentialPersistedStateOption {
credential . WithStateTestOpts ([] awsutil . Option {
2021-10-06 16:53:11 -07:00
awsutil . WithIAMAPIFunc (
awsutil . NewMockIAM (
awsutil . WithDeleteAccessKeyError ( errors . New ( testDeleteAccessKeyErr )),
),
),
}),
},
2024-10-21 09:55:51 -07:00
expectedErrContains : "aws service unknown: unknown error: deleting credentials:" ,
2024-09-12 16:46:55 +01:00
expectedErrCode : codes . Unknown ,
2021-10-06 16:53:11 -07:00
},
}
for _ , tc := range cases {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
require := require . New ( t )
2023-03-20 11:02:17 -07:00
p := & HostPlugin {
testCredStateOpts : tc . credOpts ,
testCatalogStateOpts : tc . catalogOpts ,
2021-10-06 16:53:11 -07:00
}
_ , err := p . OnDeleteCatalog ( context . Background (), tc . req )
2024-07-10 17:59:02 +01:00
require . Error ( err )
2022-03-10 11:00:57 -08:00
require . Contains ( err . Error (), tc . expectedErrContains )
require . Equal ( status . Code ( err ). String (), tc . expectedErrCode . String ())
2021-10-06 16:53:11 -07:00
})
}
}
2021-10-07 12:40:55 -07:00
func TestPluginOnCreateSetErr ( t * testing . T ) {
2021-10-06 16:53:11 -07:00
cases := [] struct {
2022-03-10 11:00:57 -08:00
name string
req * pb . OnCreateSetRequest
2023-03-20 11:02:17 -07:00
credOpts [] credential . AwsCredentialPersistedStateOption
catalogOpts [] awsCatalogPersistedStateOption
2022-03-10 11:00:57 -08:00
expectedErrContains string
expectedErrCode codes . Code
2021-10-06 16:53:11 -07:00
}{
{
2022-03-10 11:00:57 -08:00
name : "nil catalog" ,
req : & pb . OnCreateSetRequest {},
2024-09-12 16:46:55 +01:00
expectedErrContains : "catalog is required" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
name : "nil catalog attributes" ,
req : & pb . OnCreateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
Secrets : new ( structpb . Struct ),
},
},
2024-09-12 16:46:55 +01:00
expectedErrContains : "catalog attributes are required" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
2021-11-16 17:29:17 -08:00
{
name : "error reading catalog attributes" ,
req : & pb . OnCreateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Secrets : new ( structpb . Struct ),
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : new ( structpb . Struct ),
},
2021-11-16 17:29:17 -08:00
},
},
2022-03-10 11:00:57 -08:00
expectedErrContains : "missing required value \"region\"" ,
expectedErrCode : codes . InvalidArgument ,
2021-11-16 17:29:17 -08:00
},
2021-10-06 16:53:11 -07:00
{
name : "persisted state setup error" ,
req : & pb . OnCreateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
},
2023-03-20 11:02:17 -07:00
catalogOpts : [] awsCatalogPersistedStateOption {
2021-10-06 16:53:11 -07:00
func ( s * awsCatalogPersistedState ) error {
return errors . New ( testOptionErr )
},
},
2022-03-10 11:00:57 -08:00
expectedErrContains : fmt . Sprintf ( "error loading persisted state: %s" , testOptionErr ),
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
2021-11-16 17:29:17 -08:00
name : "nil set" ,
2021-10-06 16:53:11 -07:00
req : & pb . OnCreateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
},
2024-09-12 16:46:55 +01:00
expectedErrContains : "set is required" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
2021-11-16 17:29:17 -08:00
name : "nil attributes in set" ,
2021-10-06 16:53:11 -07:00
req : & pb . OnCreateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
2021-11-16 17:29:17 -08:00
Set : & hostsets . HostSet {},
2021-10-06 16:53:11 -07:00
},
2024-09-12 16:46:55 +01:00
expectedErrContains : "set attributes are required" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
2021-11-16 17:29:17 -08:00
name : "set attribute load error" ,
2021-10-06 16:53:11 -07:00
req : & pb . OnCreateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
2021-11-16 17:29:17 -08:00
Set : & hostsets . HostSet {
2022-08-19 00:42:19 -04:00
Attrs : & hostsets . HostSet_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
"foo" : structpb . NewBoolValue ( true ),
"bar" : structpb . NewBoolValue ( true ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-11-16 17:29:17 -08:00
},
2021-10-06 16:53:11 -07:00
},
2022-08-19 00:42:19 -04:00
expectedErrContains : "attributes.bar: unrecognized field, attributes.foo: unrecognized field" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
name : "client load error" ,
req : & pb . OnCreateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
Set : & hostsets . HostSet {
2022-08-19 00:42:19 -04:00
Attrs : & hostsets . HostSet_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
ConstDescribeInstancesFilters : structpb . NewListValue (
& structpb . ListValue {
Values : [] * structpb . Value {
structpb . NewStringValue ( "tag-key=foo" ),
},
},
),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
},
2023-03-20 11:02:17 -07:00
credOpts : [] credential . AwsCredentialPersistedStateOption {
credential . WithStateTestOpts ([] awsutil . Option {
2021-10-06 16:53:11 -07:00
awsutil . MockOptionErr ( errors . New ( testOptionErr )),
}),
},
2023-07-18 16:33:24 -07:00
expectedErrContains : fmt . Sprintf ( "error loading persisted state: error reading options in NewCredentialsConfig: %s" , testOptionErr ),
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
2021-11-12 19:47:46 -05:00
name : "DescribeInstances error" ,
2021-10-06 16:53:11 -07:00
req : & pb . OnCreateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
Set : & hostsets . HostSet {
2022-08-19 00:42:19 -04:00
Attrs : & hostsets . HostSet_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
ConstDescribeInstancesFilters : structpb . NewListValue (
& structpb . ListValue {
Values : [] * structpb . Value {
structpb . NewStringValue ( "tag-key=foo" ),
},
},
),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
},
2023-03-20 11:02:17 -07:00
catalogOpts : [] awsCatalogPersistedStateOption {
2021-11-12 19:47:46 -05:00
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesError ( errors . New ( testDescribeInstancesError )),
)),
},
2026-09-18 18:31:22 -04:00
expectedErrContains : testDescribeInstancesError ,
expectedErrCode : codes . Unknown ,
2021-10-06 16:53:11 -07:00
},
}
for _ , tc := range cases {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
require := require . New ( t )
2023-03-20 11:02:17 -07:00
p := & HostPlugin {
testCredStateOpts : tc . credOpts ,
testCatalogStateOpts : tc . catalogOpts ,
2021-10-06 16:53:11 -07:00
}
_ , err := p . OnCreateSet ( context . Background (), tc . req )
2024-07-10 17:59:02 +01:00
require . Error ( err )
2022-03-10 11:00:57 -08:00
require . Contains ( err . Error (), tc . expectedErrContains )
require . Equal ( status . Code ( err ). String (), tc . expectedErrCode . String ())
2021-10-06 16:53:11 -07:00
})
}
}
2021-10-07 12:40:55 -07:00
func TestPluginOnUpdateSetErr ( t * testing . T ) {
2021-10-06 16:53:11 -07:00
cases := [] struct {
2022-03-10 11:00:57 -08:00
name string
req * pb . OnUpdateSetRequest
2023-03-20 11:02:17 -07:00
credOpts [] credential . AwsCredentialPersistedStateOption
catalogOpts [] awsCatalogPersistedStateOption
2022-03-10 11:00:57 -08:00
expectedErrContains string
expectedErrCode codes . Code
2021-10-06 16:53:11 -07:00
}{
{
2022-03-10 11:00:57 -08:00
name : "nil catalog" ,
req : & pb . OnUpdateSetRequest {},
2024-09-12 16:46:55 +01:00
expectedErrContains : "catalog is required" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
name : "nil catalog attributes" ,
req : & pb . OnUpdateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
Secrets : new ( structpb . Struct ),
},
},
2024-09-12 16:46:55 +01:00
expectedErrContains : "catalog attributes are required" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
2021-11-16 17:29:17 -08:00
{
name : "catalog attribute load error" ,
req : & pb . OnUpdateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Secrets : new ( structpb . Struct ),
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : new ( structpb . Struct ),
},
2021-11-16 17:29:17 -08:00
},
},
2022-03-10 11:00:57 -08:00
expectedErrContains : "missing required value \"region\"" ,
expectedErrCode : codes . InvalidArgument ,
2021-11-16 17:29:17 -08:00
},
2021-10-06 16:53:11 -07:00
{
name : "persisted state setup error" ,
req : & pb . OnUpdateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
},
2023-03-20 11:02:17 -07:00
catalogOpts : [] awsCatalogPersistedStateOption {
2021-10-06 16:53:11 -07:00
func ( s * awsCatalogPersistedState ) error {
return errors . New ( testOptionErr )
},
},
2022-03-10 11:00:57 -08:00
expectedErrContains : fmt . Sprintf ( "error loading persisted state: %s" , testOptionErr ),
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
2021-11-16 17:29:17 -08:00
name : "nil set" ,
2021-10-06 16:53:11 -07:00
req : & pb . OnUpdateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
},
2024-09-12 16:46:55 +01:00
expectedErrContains : "new set is required" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
2021-11-16 17:29:17 -08:00
name : "nil attributes in set" ,
2021-10-06 16:53:11 -07:00
req : & pb . OnUpdateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
2021-11-16 17:29:17 -08:00
NewSet : & hostsets . HostSet {},
2021-10-06 16:53:11 -07:00
},
2024-09-12 16:46:55 +01:00
expectedErrContains : "new set attributes are required" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
2021-11-16 17:29:17 -08:00
name : "set attribute load error" ,
2021-10-06 16:53:11 -07:00
req : & pb . OnUpdateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
2021-11-16 17:29:17 -08:00
NewSet : & hostsets . HostSet {
2022-08-19 00:42:19 -04:00
Attrs : & hostsets . HostSet_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
"foo" : structpb . NewBoolValue ( true ),
"bar" : structpb . NewBoolValue ( true ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-11-16 17:29:17 -08:00
},
2021-10-06 16:53:11 -07:00
},
2022-08-19 00:42:19 -04:00
expectedErrContains : "attributes.bar: unrecognized field, attributes.foo: unrecognized field" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
name : "client load error" ,
req : & pb . OnUpdateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
NewSet : & hostsets . HostSet {
2022-08-19 00:42:19 -04:00
Attrs : & hostsets . HostSet_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
ConstDescribeInstancesFilters : structpb . NewListValue (
& structpb . ListValue {
Values : [] * structpb . Value {
structpb . NewStringValue ( "tag-key=foo" ),
},
},
),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
},
2023-03-20 11:02:17 -07:00
credOpts : [] credential . AwsCredentialPersistedStateOption {
credential . WithStateTestOpts ([] awsutil . Option {
2021-10-06 16:53:11 -07:00
awsutil . MockOptionErr ( errors . New ( testOptionErr )),
}),
},
2023-07-18 16:33:24 -07:00
expectedErrContains : fmt . Sprintf ( "error loading persisted state: error reading options in NewCredentialsConfig: %s" , testOptionErr ),
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
name : "DescribeInstances error" ,
req : & pb . OnUpdateSetRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
NewSet : & hostsets . HostSet {
2022-08-19 00:42:19 -04:00
Attrs : & hostsets . HostSet_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
ConstDescribeInstancesFilters : structpb . NewListValue (
& structpb . ListValue {
Values : [] * structpb . Value {
structpb . NewStringValue ( "tag-key=foo" ),
},
},
),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
},
2023-03-20 11:02:17 -07:00
catalogOpts : [] awsCatalogPersistedStateOption {
2021-10-06 16:53:11 -07:00
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesError ( errors . New ( testDescribeInstancesError )),
)),
},
2026-09-18 18:31:22 -04:00
expectedErrContains : testDescribeInstancesError ,
expectedErrCode : codes . Unknown ,
2021-10-06 16:53:11 -07:00
},
}
for _ , tc := range cases {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
require := require . New ( t )
2023-03-20 11:02:17 -07:00
p := & HostPlugin {
testCredStateOpts : tc . credOpts ,
testCatalogStateOpts : tc . catalogOpts ,
2021-10-06 16:53:11 -07:00
}
_ , err := p . OnUpdateSet ( context . Background (), tc . req )
2024-07-10 17:59:02 +01:00
require . Error ( err )
2022-03-10 11:00:57 -08:00
require . Contains ( err . Error (), tc . expectedErrContains )
require . Equal ( status . Code ( err ). String (), tc . expectedErrCode . String ())
2021-10-06 16:53:11 -07:00
})
}
}
2026-09-18 14:33:31 -04:00
func testListHostsSet ( id , filter string ) * hostsets . HostSet {
return & hostsets . HostSet {
Id : id ,
Attrs : & hostsets . HostSet_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
ConstDescribeInstancesFilters : structpb . NewListValue (
& structpb . ListValue {
Values : [] * structpb . Value {
structpb . NewStringValue ( filter ),
},
},
),
},
},
},
}
}
func testListHostsPersisted () * pb . HostCatalogPersisted {
return & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
},
},
}
}
func testExpectedDescribeInput ( filterKey , filterValue string ) * ec2 . DescribeInstancesInput {
return & ec2 . DescribeInstancesInput {
DryRun : aws . Bool ( false ),
Filters : [] types . Filter {
{
Name : aws . String ( filterKey ),
Values : [] string { filterValue },
},
{
Name : aws . String ( "instance-state-name" ),
Values : [] string { string ( types . InstanceStateNameRunning )},
},
},
}
}
func TestPluginListHosts ( t * testing . T ) {
describeOutput := & ec2 . DescribeInstancesOutput {
Reservations : [] types . Reservation {
{
Instances : [] types . Instance {
{
InstanceId : aws . String ( "i-shared" ),
PrivateIpAddress : aws . String ( "10.0.0.1" ),
},
},
},
},
}
cases := [] struct {
name string
catalogFields map [ string ] * structpb . Value
sets [] * hostsets . HostSet
expectedRegion string
expectedHosts [] * pb . ListHostsResponseHost
expectedInputs [] * ec2 . DescribeInstancesInput
expectedDescribe int
}{
{
name : "principal account lists hosts" ,
catalogFields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
sets : [] * hostsets . HostSet {
testListHostsSet ( "set-1" , "tag-key=foo" ),
},
expectedRegion : "us-west-2" ,
expectedHosts : [] * pb . ListHostsResponseHost {
{
ExternalId : "i-shared" ,
SetIds : [] string { "set-1" },
},
},
expectedInputs : [] * ec2 . DescribeInstancesInput {
testExpectedDescribeInput ( "tag-key" , "foo" ),
},
expectedDescribe : 1 ,
},
{
name : "target account lists hosts without querying principal" ,
catalogFields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
credential . ConstTargetRoleArn : structpb . NewStringValue ( "arn:aws:iam::222222222222:role/Target" ),
credential . ConstTargetRegion : structpb . NewStringValue ( "eu-west-1" ),
},
sets : [] * hostsets . HostSet {
testListHostsSet ( "set-1" , "tag-key=foo" ),
testListHostsSet ( "set-2" , "tag-key=bar" ),
},
expectedRegion : "eu-west-1" ,
expectedHosts : [] * pb . ListHostsResponseHost {
{
ExternalId : "i-shared" ,
SetIds : [] string { "set-1" , "set-2" },
},
},
expectedInputs : [] * ec2 . DescribeInstancesInput {
testExpectedDescribeInput ( "tag-key" , "foo" ),
testExpectedDescribeInput ( "tag-key" , "bar" ),
},
expectedDescribe : 2 ,
},
}
for _ , tc := range cases {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
require := require . New ( t )
state := & testMockEC2State {}
p := & HostPlugin {
testCatalogStateOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( newTestMockEC2 (
state ,
testMockEC2WithDescribeInstancesOutput ( describeOutput ),
)),
},
}
resp , err := p . ListHosts ( context . Background (), & pb . ListHostsRequest {
Catalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct { Fields : tc . catalogFields },
},
},
Persisted : testListHostsPersisted (),
Sets : tc . sets ,
})
require . NoError ( err )
require . NotNil ( resp )
require . Len ( resp . GetHosts (), len ( tc . expectedHosts ))
for i , expected := range tc . expectedHosts {
require . Equal ( expected . ExternalId , resp . Hosts [ i ]. ExternalId )
require . Equal ( expected . SetIds , resp . Hosts [ i ]. SetIds )
}
require . Equal ( tc . expectedDescribe , state . DescribeInstancesCallCount )
require . Len ( state . ClientRegions , tc . expectedDescribe )
for _ , region := range state . ClientRegions {
require . Equal ( tc . expectedRegion , region )
}
require . Equal ( tc . expectedInputs , state . DescribeInstancesInputs )
})
}
}
2021-10-07 12:40:55 -07:00
func TestPluginListHostsErr ( t * testing . T ) {
2021-10-06 16:53:11 -07:00
cases := [] struct {
2022-03-10 11:00:57 -08:00
name string
req * pb . ListHostsRequest
2023-03-20 11:02:17 -07:00
credOpts [] credential . AwsCredentialPersistedStateOption
catalogOpts [] awsCatalogPersistedStateOption
2022-03-10 11:00:57 -08:00
expectedErrContains string
expectedErrCode codes . Code
2021-10-06 16:53:11 -07:00
}{
{
2022-03-10 11:00:57 -08:00
name : "nil catalog" ,
req : & pb . ListHostsRequest {},
2024-09-12 16:46:55 +01:00
expectedErrContains : "catalog is required" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
name : "nil catalog attributes" ,
req : & pb . ListHostsRequest {
Catalog : & hostcatalogs . HostCatalog {
Secrets : new ( structpb . Struct ),
},
},
2024-09-12 16:46:55 +01:00
expectedErrContains : "catalog attributes are required" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
2021-11-16 17:29:17 -08:00
{
name : "error reading catalog attributes" ,
req : & pb . ListHostsRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Secrets : new ( structpb . Struct ),
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : new ( structpb . Struct ),
},
2021-11-16 17:29:17 -08:00
},
},
2022-03-10 11:00:57 -08:00
expectedErrContains : "attributes.region: missing required value \"region\"" ,
expectedErrCode : codes . InvalidArgument ,
2021-11-16 17:29:17 -08:00
},
2021-10-06 16:53:11 -07:00
{
name : "persisted state setup error" ,
req : & pb . ListHostsRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
},
2023-03-20 11:02:17 -07:00
catalogOpts : [] awsCatalogPersistedStateOption {
2021-10-06 16:53:11 -07:00
func ( s * awsCatalogPersistedState ) error {
return errors . New ( testOptionErr )
},
},
2022-03-10 11:00:57 -08:00
expectedErrContains : fmt . Sprintf ( "error loading persisted state: %s" , testOptionErr ),
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
2021-11-16 17:29:17 -08:00
name : "nil sets" ,
2021-10-06 16:53:11 -07:00
req : & pb . ListHostsRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
},
2024-09-12 16:46:55 +01:00
expectedErrContains : "sets are required" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
2021-11-16 17:29:17 -08:00
name : "set missing id" ,
2021-10-06 16:53:11 -07:00
req : & pb . ListHostsRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
2022-08-19 00:42:19 -04:00
Sets : [] * hostsets . HostSet {{}},
2021-10-06 16:53:11 -07:00
},
2024-09-12 16:46:55 +01:00
expectedErrContains : "set id is required" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
2021-11-16 17:29:17 -08:00
name : "set missing attributes" ,
2021-10-06 16:53:11 -07:00
req : & pb . ListHostsRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
2021-11-16 17:29:17 -08:00
Sets : [] * hostsets . HostSet {
2022-08-19 00:42:19 -04:00
{
2021-11-16 17:29:17 -08:00
Id : "foobar" ,
},
},
2021-10-06 16:53:11 -07:00
},
2024-09-12 16:46:55 +01:00
expectedErrContains : "set foobar attributes are required" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
2021-11-16 17:29:17 -08:00
name : "set attribute load error" ,
2021-10-06 16:53:11 -07:00
req : & pb . ListHostsRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
Sets : [] * hostsets . HostSet {
2022-08-19 00:42:19 -04:00
{
2021-10-06 16:53:11 -07:00
Id : "foobar" ,
2022-08-19 00:42:19 -04:00
Attrs : & hostsets . HostSet_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
"foo" : structpb . NewBoolValue ( true ),
"bar" : structpb . NewBoolValue ( true ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
},
},
2022-08-19 00:42:19 -04:00
expectedErrContains : "attributes.bar: unrecognized field, attributes.foo: unrecognized field" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
name : "client load error" ,
req : & pb . ListHostsRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
Sets : [] * hostsets . HostSet {
2021-11-12 19:47:46 -05:00
{
2021-10-06 16:53:11 -07:00
Id : "foobar" ,
2022-08-19 00:42:19 -04:00
Attrs : & hostsets . HostSet_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
ConstDescribeInstancesFilters : structpb . NewListValue (
& structpb . ListValue {
Values : [] * structpb . Value {
structpb . NewStringValue ( "tag-key=foo" ),
},
},
),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
},
},
2023-03-20 11:02:17 -07:00
credOpts : [] credential . AwsCredentialPersistedStateOption {
credential . WithStateTestOpts ([] awsutil . Option {
2021-10-06 16:53:11 -07:00
awsutil . MockOptionErr ( errors . New ( testOptionErr )),
}),
},
2023-07-18 16:33:24 -07:00
expectedErrContains : fmt . Sprintf ( "error loading persisted state: error reading options in NewCredentialsConfig: %s" , testOptionErr ),
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
name : "DescribeInstances error" ,
req : & pb . ListHostsRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
Sets : [] * hostsets . HostSet {
2021-11-12 19:47:46 -05:00
{
2021-10-06 16:53:11 -07:00
Id : "foobar" ,
2022-08-19 00:42:19 -04:00
Attrs : & hostsets . HostSet_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
ConstDescribeInstancesFilters : structpb . NewListValue (
& structpb . ListValue {
Values : [] * structpb . Value {
structpb . NewStringValue ( "tag-key=foo" ),
},
},
),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
},
},
2023-03-20 11:02:17 -07:00
catalogOpts : [] awsCatalogPersistedStateOption {
2021-10-06 16:53:11 -07:00
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesError ( errors . New ( testDescribeInstancesError )),
)),
},
2026-09-18 14:33:31 -04:00
expectedErrContains : fmt . Sprintf ( "error retrieving host results for host set id \"foobar\": %s" , testDescribeInstancesError ),
expectedErrCode : codes . InvalidArgument ,
},
{
name : "target attributes missing target_role_arn" ,
req : & pb . ListHostsRequest {
Catalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
credential . ConstTargetRegion : structpb . NewStringValue ( "eu-west-1" ),
},
},
},
},
},
expectedErrContains : "attributes.target_role_arn: missing required value \"target_role_arn\"" ,
expectedErrCode : codes . InvalidArgument ,
},
{
name : "target EC2 client error" ,
req : & pb . ListHostsRequest {
Catalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
credential . ConstTargetRoleArn : structpb . NewStringValue ( "arn:aws:iam::222222222222:role/Target" ),
},
},
},
},
Persisted : & pb . HostCatalogPersisted {
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
},
Sets : [] * hostsets . HostSet {
{
Id : "foobar" ,
Attrs : & hostsets . HostSet_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
ConstDescribeInstancesFilters : structpb . NewListValue (
& structpb . ListValue {
Values : [] * structpb . Value {
structpb . NewStringValue ( "tag-key=foo" ),
},
},
),
},
},
},
},
},
},
catalogOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( func ( ... aws . Config ) ( EC2API , error ) {
return nil , errors . New ( "test EC2 client error" )
}),
},
expectedErrContains : "unable to list hosts for host set \"foobar\": EC2 client setup failed for target account: test EC2 client error" ,
expectedErrCode : codes . InvalidArgument ,
},
{
name : "target DescribeInstances error" ,
req : & pb . ListHostsRequest {
Catalog : & hostcatalogs . HostCatalog {
Attrs : & hostcatalogs . HostCatalog_Attributes {
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
credential . ConstTargetRoleArn : structpb . NewStringValue ( "arn:aws:iam::222222222222:role/Target" ),
credential . ConstTargetRegion : structpb . NewStringValue ( "eu-west-1" ),
},
},
},
},
Persisted : testListHostsPersisted (),
Sets : [] * hostsets . HostSet {
testListHostsSet ( "foobar" , "tag-key=foo" ),
},
},
catalogOpts : [] awsCatalogPersistedStateOption {
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesError ( errors . New ( testDescribeInstancesError )),
)),
},
expectedErrContains : "unable to list hosts for host set \"foobar\": EC2 DescribeInstances failed for target account: DescribeInstances error" ,
2022-03-10 11:00:57 -08:00
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
{
name : "awsInstanceToHost error" ,
req : & pb . ListHostsRequest {
Catalog : & hostcatalogs . HostCatalog {
2022-08-19 00:42:19 -04:00
Attrs : & hostcatalogs . HostCatalog_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
credential . ConstRegion : structpb . NewStringValue ( "us-west-2" ),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
Persisted : & pb . HostCatalogPersisted {
2023-03-20 11:02:17 -07:00
Secrets : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
2023-07-18 16:33:24 -07:00
credential . ConstAccessKeyId : structpb . NewStringValue ( "AKIA_foobar" ),
2023-03-20 11:02:17 -07:00
credential . ConstSecretAccessKey : structpb . NewStringValue ( "bazqux" ),
credential . ConstCredsLastRotatedTime : structpb . NewStringValue ( "2006-01-02T15:04:05+07:00" ),
},
},
2021-10-06 16:53:11 -07:00
},
Sets : [] * hostsets . HostSet {
2021-11-12 19:47:46 -05:00
{
2021-10-06 16:53:11 -07:00
Id : "foobar" ,
2022-08-19 00:42:19 -04:00
Attrs : & hostsets . HostSet_Attributes {
2023-03-20 11:02:17 -07:00
Attributes : & structpb . Struct {
Fields : map [ string ] * structpb . Value {
ConstDescribeInstancesFilters : structpb . NewListValue (
& structpb . ListValue {
Values : [] * structpb . Value {
structpb . NewStringValue ( "tag-key=foo" ),
},
},
),
},
},
2022-08-19 00:42:19 -04:00
},
2021-10-06 16:53:11 -07:00
},
},
},
2023-03-20 11:02:17 -07:00
catalogOpts : [] awsCatalogPersistedStateOption {
2021-10-06 16:53:11 -07:00
withTestEC2APIFunc ( newTestMockEC2 (
nil ,
testMockEC2WithDescribeInstancesOutput (
& ec2 . DescribeInstancesOutput {
2023-07-04 11:03:24 -07:00
Reservations : [] types . Reservation {
2022-08-19 00:42:19 -04:00
{
2023-07-04 11:03:24 -07:00
Instances : [] types . Instance {
2022-08-19 00:42:19 -04:00
{
2021-10-06 16:53:11 -07:00
// Blank so we error out
},
},
},
},
},
),
)),
},
2022-03-10 11:00:57 -08:00
expectedErrContains : "error processing host results for host set id \"foobar\": response integrity error: missing instance id" ,
expectedErrCode : codes . InvalidArgument ,
2021-10-06 16:53:11 -07:00
},
}
for _ , tc := range cases {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
require := require . New ( t )
2023-03-20 11:02:17 -07:00
p := & HostPlugin {
testCredStateOpts : tc . credOpts ,
testCatalogStateOpts : tc . catalogOpts ,
2021-10-06 16:53:11 -07:00
}
_ , err := p . ListHosts ( context . Background (), tc . req )
2024-07-10 17:59:02 +01:00
require . Error ( err )
2022-03-10 11:00:57 -08:00
require . Contains ( err . Error (), tc . expectedErrContains )
require . Equal ( status . Code ( err ). String (), tc . expectedErrCode . String ())
2021-10-06 16:53:11 -07:00
})
}
}
2021-10-07 12:40:55 -07:00
func TestBuildFilters ( t * testing . T ) {
cases := [] struct {
2021-11-03 21:21:27 -04:00
name string
2023-03-20 11:02:17 -07:00
in map [ string ] any
2023-07-04 11:03:24 -07:00
expected [] types . Filter
2021-11-03 21:21:27 -04:00
expectedErrContains string
2021-10-07 12:40:55 -07:00
}{
{
2021-11-03 21:21:27 -04:00
name : "good without instance-state-name" ,
2023-03-20 11:02:17 -07:00
in : map [ string ] any {
ConstDescribeInstancesFilters : [] any { "foo=bar" },
},
2023-07-04 11:03:24 -07:00
expected : [] types . Filter {
2021-11-12 19:47:46 -05:00
{
2021-11-03 21:21:27 -04:00
Name : aws . String ( "foo" ),
2023-07-04 11:03:24 -07:00
Values : [] string { "bar" },
2021-10-07 12:40:55 -07:00
},
2021-11-12 19:47:46 -05:00
{
2021-11-03 21:21:27 -04:00
Name : aws . String ( "instance-state-name" ),
2023-07-04 11:03:24 -07:00
Values : [] string { string ( types . InstanceStateNameRunning )},
2021-11-03 21:21:27 -04:00
},
},
2021-10-07 12:40:55 -07:00
},
{
2021-11-03 21:21:27 -04:00
name : "good with instance-state-name" ,
2023-03-20 11:02:17 -07:00
in : map [ string ] any {
ConstDescribeInstancesFilters : [] any {
2021-11-03 21:21:27 -04:00
"foo=bar" ,
"instance-state-name=static" ,
2021-10-07 12:40:55 -07:00
},
2023-03-20 11:02:17 -07:00
},
2023-07-04 11:03:24 -07:00
expected : [] types . Filter {
2021-11-12 19:47:46 -05:00
{
2021-10-07 12:40:55 -07:00
Name : aws . String ( "foo" ),
2023-07-04 11:03:24 -07:00
Values : [] string { "bar" },
2021-10-07 12:40:55 -07:00
},
2021-11-12 19:47:46 -05:00
{
2021-10-07 12:40:55 -07:00
Name : aws . String ( "instance-state-name" ),
2023-07-04 11:03:24 -07:00
Values : [] string { "static" },
2021-10-07 12:40:55 -07:00
},
},
},
{
2021-11-03 21:21:27 -04:00
name : "good with multiple values" ,
2023-03-20 11:02:17 -07:00
in : map [ string ] any {
ConstDescribeInstancesFilters : [] any {
2021-11-03 21:21:27 -04:00
"foo=bar,baz" ,
2021-10-07 12:40:55 -07:00
},
2023-03-20 11:02:17 -07:00
},
2023-07-04 11:03:24 -07:00
expected : [] types . Filter {
2021-11-12 19:47:46 -05:00
{
2021-10-07 12:40:55 -07:00
Name : aws . String ( "foo" ),
2023-07-04 11:03:24 -07:00
Values : [] string { "bar" , "baz" },
2021-10-07 12:40:55 -07:00
},
2021-11-12 19:47:46 -05:00
{
2021-10-07 12:40:55 -07:00
Name : aws . String ( "instance-state-name" ),
2023-07-04 11:03:24 -07:00
Values : [] string { string ( types . InstanceStateNameRunning )},
2021-10-07 12:40:55 -07:00
},
},
},
{
name : "empty filter set" ,
2023-03-20 11:02:17 -07:00
in : map [ string ] any {
ConstDescribeInstancesFilters : [] any {},
},
2023-07-04 11:03:24 -07:00
expected : [] types . Filter {
2021-11-12 19:47:46 -05:00
{
2021-10-07 12:40:55 -07:00
Name : aws . String ( "instance-state-name" ),
2023-07-04 11:03:24 -07:00
Values : [] string { string ( types . InstanceStateNameRunning )},
2021-10-07 12:40:55 -07:00
},
},
},
}
for _ , tc := range cases {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
require := require . New ( t )
2023-03-20 11:02:17 -07:00
input , err := structpb . NewStruct ( tc . in )
require . NoError ( err )
attrs , err := getSetAttributes ( input )
2021-11-03 21:21:27 -04:00
require . NoError ( err )
actual , err := buildFilters ( attrs )
if tc . expectedErrContains != "" {
require . Contains ( err . Error (), tc . expectedErrContains )
2021-10-07 12:40:55 -07:00
return
}
require . NoError ( err )
sort . Sort ( ec2FilterSorter ( actual ))
require . Equal ( tc . expected , actual )
})
}
}
func TestBuildDescribeInstancesInput ( t * testing . T ) {
cases := [] struct {
name string
2023-03-20 11:02:17 -07:00
in map [ string ] any
2021-10-07 12:40:55 -07:00
dryRun bool
expected * ec2 . DescribeInstancesInput
expectedErr string
}{
{
name : "good, dry run" ,
2023-03-20 11:02:17 -07:00
in : map [ string ] any {
ConstDescribeInstancesFilters : [] any {
2021-11-03 21:21:27 -04:00
"foo=bar" ,
2021-10-07 12:40:55 -07:00
},
2023-03-20 11:02:17 -07:00
},
2021-10-07 12:40:55 -07:00
dryRun : true ,
expected : & ec2 . DescribeInstancesInput {
DryRun : aws . Bool ( true ),
2023-07-04 11:03:24 -07:00
Filters : [] types . Filter {
2021-11-12 19:47:46 -05:00
{
2021-10-07 12:40:55 -07:00
Name : aws . String ( "foo" ),
2023-07-04 11:03:24 -07:00
Values : [] string { "bar" },
2021-10-07 12:40:55 -07:00
},
2021-11-12 19:47:46 -05:00
{
2021-10-07 12:40:55 -07:00
Name : aws . String ( "instance-state-name" ),
2023-07-04 11:03:24 -07:00
Values : [] string { string ( types . InstanceStateNameRunning )},
2021-10-07 12:40:55 -07:00
},
},
},
},
{
name : "good, real run" ,
2023-03-20 11:02:17 -07:00
in : map [ string ] any {
ConstDescribeInstancesFilters : [] any {
2021-11-03 21:21:27 -04:00
"foo=bar" ,
2021-10-07 12:40:55 -07:00
},
2023-03-20 11:02:17 -07:00
},
2021-10-07 12:40:55 -07:00
dryRun : false ,
expected : & ec2 . DescribeInstancesInput {
DryRun : aws . Bool ( false ),
2023-07-04 11:03:24 -07:00
Filters : [] types . Filter {
2021-11-12 19:47:46 -05:00
{
2021-10-07 12:40:55 -07:00
Name : aws . String ( "foo" ),
2023-07-04 11:03:24 -07:00
Values : [] string { "bar" },
2021-10-07 12:40:55 -07:00
},
2021-11-12 19:47:46 -05:00
{
2021-10-07 12:40:55 -07:00
Name : aws . String ( "instance-state-name" ),
2023-07-04 11:03:24 -07:00
Values : [] string { string ( types . InstanceStateNameRunning )},
2021-10-07 12:40:55 -07:00
},
},
},
},
}
for _ , tc := range cases {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
require := require . New ( t )
2023-03-20 11:02:17 -07:00
input , err := structpb . NewStruct ( tc . in )
require . NoError ( err )
attrs , err := getSetAttributes ( input )
2021-11-03 21:21:27 -04:00
require . NoError ( err )
actual , err := buildDescribeInstancesInput ( attrs , tc . dryRun )
2021-10-07 12:40:55 -07:00
if tc . expectedErr != "" {
require . EqualError ( err , tc . expectedErr )
return
}
require . NoError ( err )
require . Equal ( tc . expected , actual )
})
}
}
func TestAwsInstanceToHost ( t * testing . T ) {
cases := [] struct {
2026-05-27 13:12:34 -07:00
name string
instance types . Instance
catalogAttrs * CatalogAttributes
expected * pb . ListHostsResponseHost
expectedErr string
2021-10-07 12:40:55 -07:00
}{
{
2026-05-27 13:12:34 -07:00
name : "missing instance id" ,
instance : types . Instance {},
catalogAttrs : & CatalogAttributes {},
expectedErr : "response integrity error: missing instance id" ,
},
{
name : "missing catalog attributes" ,
instance : types . Instance {
InstanceId : aws . String ( "foobar" ),
},
catalogAttrs : nil ,
expectedErr : "response integrity error: missing catalog attributes" ,
2021-10-07 12:40:55 -07:00
},
{
name : "good, single IP w/public addr" ,
2023-07-04 11:03:24 -07:00
instance : types . Instance {
2021-10-07 12:40:55 -07:00
InstanceId : aws . String ( "foobar" ),
PrivateIpAddress : aws . String ( "10.0.0.1" ),
2021-12-03 13:24:48 -08:00
PrivateDnsName : aws . String ( "test.example.internal" ),
2021-10-07 12:40:55 -07:00
PublicIpAddress : aws . String ( "1.1.1.1" ),
2021-12-03 13:24:48 -08:00
PublicDnsName : aws . String ( "test.example.com" ),
2023-07-04 11:03:24 -07:00
NetworkInterfaces : [] types . InstanceNetworkInterface {
2022-08-19 00:42:19 -04:00
{
2021-12-06 11:40:44 -08:00
PrivateIpAddress : aws . String ( "10.0.0.1" ),
PrivateDnsName : aws . String ( "test.example.internal" ),
2023-07-04 11:03:24 -07:00
PrivateIpAddresses : [] types . InstancePrivateIpAddress {
2022-08-19 00:42:19 -04:00
{
2023-07-04 11:03:24 -07:00
Association : & types . InstanceNetworkInterfaceAssociation {
2021-12-03 13:24:48 -08:00
PublicIp : aws . String ( "1.1.1.1" ),
PublicDnsName : aws . String ( "test.example.com" ),
2021-10-07 12:40:55 -07:00
},
PrivateIpAddress : aws . String ( "10.0.0.1" ),
2021-12-03 13:24:48 -08:00
PrivateDnsName : aws . String ( "test.example.internal" ),
2021-10-07 12:40:55 -07:00
},
},
},
},
},
2026-05-27 13:12:34 -07:00
catalogAttrs : & CatalogAttributes {},
2021-10-07 12:40:55 -07:00
expected : & pb . ListHostsResponseHost {
ExternalId : "foobar" ,
IpAddresses : [] string { "10.0.0.1" , "1.1.1.1" },
2021-12-03 13:24:48 -08:00
DnsNames : [] string { "test.example.internal" , "test.example.com" },
2021-10-07 12:40:55 -07:00
},
},
{
name : "good, private" ,
2023-07-04 11:03:24 -07:00
instance : types . Instance {
2021-10-07 12:40:55 -07:00
InstanceId : aws . String ( "foobar" ),
PrivateIpAddress : aws . String ( "10.0.0.1" ),
2021-12-03 13:24:48 -08:00
PrivateDnsName : aws . String ( "test.example.internal" ),
2023-07-04 11:03:24 -07:00
NetworkInterfaces : [] types . InstanceNetworkInterface {
2022-08-19 00:42:19 -04:00
{
2021-12-06 11:40:44 -08:00
PrivateIpAddress : aws . String ( "10.0.0.1" ),
PrivateDnsName : aws . String ( "test.example.internal" ),
2023-07-04 11:03:24 -07:00
PrivateIpAddresses : [] types . InstancePrivateIpAddress {
2022-08-19 00:42:19 -04:00
{
2021-10-07 12:40:55 -07:00
PrivateIpAddress : aws . String ( "10.0.0.1" ),
2021-12-03 13:24:48 -08:00
PrivateDnsName : aws . String ( "test.example.internal" ),
2021-10-07 12:40:55 -07:00
},
},
},
},
},
2026-05-27 13:12:34 -07:00
catalogAttrs : & CatalogAttributes {},
2021-10-07 12:40:55 -07:00
expected : & pb . ListHostsResponseHost {
ExternalId : "foobar" ,
IpAddresses : [] string { "10.0.0.1" },
2021-12-03 13:24:48 -08:00
DnsNames : [] string { "test.example.internal" },
2021-10-07 12:40:55 -07:00
},
},
{
name : "good, multiple interfaces" ,
2023-07-04 11:03:24 -07:00
instance : types . Instance {
2021-10-07 12:40:55 -07:00
InstanceId : aws . String ( "foobar" ),
PrivateIpAddress : aws . String ( "10.0.0.1" ),
2021-12-03 13:24:48 -08:00
PrivateDnsName : aws . String ( "test.example.internal" ),
2021-10-07 12:40:55 -07:00
PublicIpAddress : aws . String ( "1.1.1.1" ),
2021-12-03 13:24:48 -08:00
PublicDnsName : aws . String ( "test.example.com" ),
2023-07-04 11:03:24 -07:00
NetworkInterfaces : [] types . InstanceNetworkInterface {
2022-08-19 00:42:19 -04:00
{
2021-12-06 11:40:44 -08:00
PrivateIpAddress : aws . String ( "10.0.0.2" ),
PrivateDnsName : aws . String ( "test2.example.internal" ),
2023-07-04 11:03:24 -07:00
PrivateIpAddresses : [] types . InstancePrivateIpAddress {
2022-08-19 00:42:19 -04:00
{
2021-10-07 12:40:55 -07:00
PrivateIpAddress : aws . String ( "10.0.0.2" ),
2021-12-03 13:24:48 -08:00
PrivateDnsName : aws . String ( "test2.example.internal" ),
2021-10-07 12:40:55 -07:00
},
},
},
2022-08-19 00:42:19 -04:00
{
2021-12-06 11:40:44 -08:00
PrivateIpAddress : aws . String ( "10.0.0.1" ),
PrivateDnsName : aws . String ( "test.example.internal" ),
2023-07-04 11:03:24 -07:00
PrivateIpAddresses : [] types . InstancePrivateIpAddress {
2022-08-19 00:42:19 -04:00
{
2023-07-04 11:03:24 -07:00
Association : & types . InstanceNetworkInterfaceAssociation {
2021-12-03 13:24:48 -08:00
PublicIp : aws . String ( "1.1.1.1" ),
PublicDnsName : aws . String ( "test.example.com" ),
2021-10-07 12:40:55 -07:00
},
PrivateIpAddress : aws . String ( "10.0.0.1" ),
2021-12-03 13:24:48 -08:00
PrivateDnsName : aws . String ( "test.example.internal" ),
2021-10-07 12:40:55 -07:00
},
},
},
},
},
2026-05-27 13:12:34 -07:00
catalogAttrs : & CatalogAttributes {},
2021-10-07 12:40:55 -07:00
expected : & pb . ListHostsResponseHost {
ExternalId : "foobar" ,
IpAddresses : [] string { "10.0.0.1" , "1.1.1.1" , "10.0.0.2" },
2021-12-03 13:24:48 -08:00
DnsNames : [] string { "test.example.internal" , "test.example.com" , "test2.example.internal" },
2021-10-07 12:40:55 -07:00
},
},
{
name : "good, multiple public interfaces" ,
2023-07-04 11:03:24 -07:00
instance : types . Instance {
2021-10-07 12:40:55 -07:00
InstanceId : aws . String ( "foobar" ),
PrivateIpAddress : aws . String ( "10.0.0.1" ),
2021-12-03 13:24:48 -08:00
PrivateDnsName : aws . String ( "test.example.internal" ),
2021-10-07 12:40:55 -07:00
PublicIpAddress : aws . String ( "1.1.1.1" ),
2021-12-03 13:24:48 -08:00
PublicDnsName : aws . String ( "test.example.com" ),
2023-07-04 11:03:24 -07:00
NetworkInterfaces : [] types . InstanceNetworkInterface {
2022-08-19 00:42:19 -04:00
{
2021-12-06 11:40:44 -08:00
PrivateIpAddress : aws . String ( "10.0.0.2" ),
PrivateDnsName : aws . String ( "test2.example.internal" ),
2023-07-04 11:03:24 -07:00
PrivateIpAddresses : [] types . InstancePrivateIpAddress {
2022-08-19 00:42:19 -04:00
{
2023-07-04 11:03:24 -07:00
Association : & types . InstanceNetworkInterfaceAssociation {
2021-12-03 13:24:48 -08:00
PublicIp : aws . String ( "1.1.1.2" ),
PublicDnsName : aws . String ( "test2.example.com" ),
2021-10-07 12:40:55 -07:00
},
PrivateIpAddress : aws . String ( "10.0.0.2" ),
2021-12-03 13:24:48 -08:00
PrivateDnsName : aws . String ( "test2.example.internal" ),
2021-10-07 12:40:55 -07:00
},
},
},
2022-08-19 00:42:19 -04:00
{
2021-12-06 11:40:44 -08:00
PrivateIpAddress : aws . String ( "10.0.0.1" ),
PrivateDnsName : aws . String ( "test.example.internal" ),
2023-07-04 11:03:24 -07:00
PrivateIpAddresses : [] types . InstancePrivateIpAddress {
2022-08-19 00:42:19 -04:00
{
2023-07-04 11:03:24 -07:00
Association : & types . InstanceNetworkInterfaceAssociation {
2021-12-03 13:24:48 -08:00
PublicIp : aws . String ( "1.1.1.1" ),
PublicDnsName : aws . String ( "test.example.com" ),
2021-10-07 12:40:55 -07:00
},
PrivateIpAddress : aws . String ( "10.0.0.1" ),
2021-12-03 13:24:48 -08:00
PrivateDnsName : aws . String ( "test.example.internal" ),
2021-10-07 12:40:55 -07:00
},
},
},
},
},
2026-05-27 13:12:34 -07:00
catalogAttrs : & CatalogAttributes {},
2021-10-07 12:40:55 -07:00
expected : & pb . ListHostsResponseHost {
ExternalId : "foobar" ,
IpAddresses : [] string { "10.0.0.1" , "1.1.1.1" , "10.0.0.2" , "1.1.1.2" },
2021-12-03 13:24:48 -08:00
DnsNames : [] string { "test.example.internal" , "test.example.com" , "test2.example.internal" , "test2.example.com" },
2021-10-07 12:40:55 -07:00
},
},
{
name : "good, multiple addresses on single interface" ,
2023-07-04 11:03:24 -07:00
instance : types . Instance {
2021-10-07 12:40:55 -07:00
InstanceId : aws . String ( "foobar" ),
PrivateIpAddress : aws . String ( "10.0.0.1" ),
2021-12-03 13:24:48 -08:00
PrivateDnsName : aws . String ( "test.example.internal" ),
2021-10-07 12:40:55 -07:00
PublicIpAddress : aws . String ( "1.1.1.1" ),
2021-12-03 13:24:48 -08:00
PublicDnsName : aws . String ( "test.example.com" ),
2023-07-04 11:03:24 -07:00
NetworkInterfaces : [] types . InstanceNetworkInterface {
2022-08-19 00:42:19 -04:00
{
2021-12-06 11:40:44 -08:00
PrivateIpAddress : aws . String ( "10.0.0.1" ),
PrivateDnsName : aws . String ( "test.example.internal" ),
2023-07-04 11:03:24 -07:00
PrivateIpAddresses : [] types . InstancePrivateIpAddress {
2022-08-19 00:42:19 -04:00
{
2023-07-04 11:03:24 -07:00
Association : & types . InstanceNetworkInterfaceAssociation {
2021-12-03 13:24:48 -08:00
PublicIp : aws . String ( "1.1.1.1" ),
PublicDnsName : aws . String ( "test.example.com" ),
2021-10-07 12:40:55 -07:00
},
PrivateIpAddress : aws . String ( "10.0.0.1" ),
2021-12-03 13:24:48 -08:00
PrivateDnsName : aws . String ( "test.example.internal" ),
2021-10-07 12:40:55 -07:00
},
2022-08-19 00:42:19 -04:00
{
2021-10-07 12:40:55 -07:00
PrivateIpAddress : aws . String ( "10.0.0.2" ),
2021-12-03 13:24:48 -08:00
PrivateDnsName : aws . String ( "test2.example.internal" ),
2021-10-07 12:40:55 -07:00
},
},
},
},
},
2026-05-27 13:12:34 -07:00
catalogAttrs : & CatalogAttributes {},
2021-10-07 12:40:55 -07:00
expected : & pb . ListHostsResponseHost {
ExternalId : "foobar" ,
IpAddresses : [] string { "10.0.0.1" , "1.1.1.1" , "10.0.0.2" },
2021-12-03 13:24:48 -08:00
DnsNames : [] string { "test.example.internal" , "test.example.com" , "test2.example.internal" },
2021-10-07 12:40:55 -07:00
},
},
{
name : "good, single IP w/public addr and IPv6" ,
2023-07-04 11:03:24 -07:00
instance : types . Instance {
2021-10-07 12:40:55 -07:00
InstanceId : aws . String ( "foobar" ),
PrivateIpAddress : aws . String ( "10.0.0.1" ),
2021-12-03 13:24:48 -08:00
PrivateDnsName : aws . String ( "test.example.internal" ),
2021-10-07 12:40:55 -07:00
PublicIpAddress : aws . String ( "1.1.1.1" ),
2021-12-03 13:24:48 -08:00
PublicDnsName : aws . String ( "test.example.com" ),
2026-05-27 13:12:34 -07:00
Ipv6Address : aws . String ( "instance::ipv6::address" ),
2023-07-04 11:03:24 -07:00
NetworkInterfaces : [] types . InstanceNetworkInterface {
2022-08-19 00:42:19 -04:00
{
2021-12-06 11:40:44 -08:00
PrivateIpAddress : aws . String ( "10.0.0.1" ),
2023-07-04 11:03:24 -07:00
PrivateIpAddresses : [] types . InstancePrivateIpAddress {
2022-08-19 00:42:19 -04:00
{
2023-07-04 11:03:24 -07:00
Association : & types . InstanceNetworkInterfaceAssociation {
2021-10-07 12:40:55 -07:00
PublicIp : aws . String ( "1.1.1.1" ),
},
PrivateIpAddress : aws . String ( "10.0.0.1" ),
},
},
2023-07-04 11:03:24 -07:00
Ipv6Addresses : [] types . InstanceIpv6Address {
{ Ipv6Address : nil }, // Just coverage for nil assertion which is skipped
2022-08-19 00:42:19 -04:00
{ Ipv6Address : aws . String ( "some::fake::address" )},
2021-10-07 12:40:55 -07:00
},
},
},
},
2026-05-27 13:12:34 -07:00
catalogAttrs : & CatalogAttributes {},
expected : & pb . ListHostsResponseHost {
ExternalId : "foobar" ,
IpAddresses : [] string { "10.0.0.1" , "1.1.1.1" , "instance::ipv6::address" , "some::fake::address" },
DnsNames : [] string { "test.example.internal" , "test.example.com" },
},
},
{
name : "good, instance addresses only excludes interface secondary addresses" ,
instance : types . Instance {
InstanceId : aws . String ( "foobar" ),
PrivateIpAddress : aws . String ( "10.0.0.1" ),
PrivateDnsName : aws . String ( "test.example.internal" ),
PublicIpAddress : aws . String ( "1.1.1.1" ),
PublicDnsName : aws . String ( "test.example.com" ),
Ipv6Address : aws . String ( "instance::ipv6::address" ),
NetworkInterfaces : [] types . InstanceNetworkInterface {
{
PrivateIpAddress : aws . String ( "10.0.0.1" ),
PrivateDnsName : aws . String ( "test.example.internal" ),
PrivateIpAddresses : [] types . InstancePrivateIpAddress {
{
Association : & types . InstanceNetworkInterfaceAssociation {
PublicIp : aws . String ( "1.1.1.1" ),
PublicDnsName : aws . String ( "test.example.com" ),
},
PrivateIpAddress : aws . String ( "10.0.0.1" ),
PrivateDnsName : aws . String ( "test.example.internal" ),
},
{
PrivateIpAddress : aws . String ( "10.0.0.2" ),
PrivateDnsName : aws . String ( "test2.example.internal" ),
},
},
Ipv6Addresses : [] types . InstanceIpv6Address {{ Ipv6Address : aws . String ( "iface::ipv6::address" )}},
},
{
PrivateIpAddress : aws . String ( "10.0.1.1" ),
PrivateDnsName : aws . String ( "secondary.example.internal" ),
PrivateIpAddresses : [] types . InstancePrivateIpAddress {
{
Association : & types . InstanceNetworkInterfaceAssociation {
PublicIp : aws . String ( "2.2.2.2" ),
PublicDnsName : aws . String ( "secondary.example.com" ),
},
PrivateIpAddress : aws . String ( "10.0.1.1" ),
PrivateDnsName : aws . String ( "secondary.example.internal" ),
},
},
Ipv6Addresses : [] types . InstanceIpv6Address {{ Ipv6Address : aws . String ( "secondary::ipv6::address" )}},
},
},
},
catalogAttrs : & CatalogAttributes { InstanceAddressesOnly : true },
2021-10-07 12:40:55 -07:00
expected : & pb . ListHostsResponseHost {
ExternalId : "foobar" ,
2026-05-27 13:12:34 -07:00
IpAddresses : [] string { "10.0.0.1" , "1.1.1.1" , "instance::ipv6::address" },
2021-12-03 13:24:48 -08:00
DnsNames : [] string { "test.example.internal" , "test.example.com" },
2021-10-07 12:40:55 -07:00
},
},
2023-03-20 11:02:17 -07:00
{
name : "good, single IP w/public addr and external name" ,
2023-07-04 11:03:24 -07:00
instance : types . Instance {
2023-03-20 11:02:17 -07:00
InstanceId : aws . String ( "foobar" ),
PrivateIpAddress : aws . String ( "10.0.0.1" ),
PrivateDnsName : aws . String ( "test.example.internal" ),
PublicIpAddress : aws . String ( "1.1.1.1" ),
PublicDnsName : aws . String ( "test.example.com" ),
2023-07-04 11:03:24 -07:00
Tags : [] types . Tag {
2023-03-20 11:02:17 -07:00
{ Key : aws . String ( "Name" ), Value : aws . String ( "test-instance-actual-name" )}, // The tag name is "Name", not "name".
{ Key : aws . String ( "name" ), Value : aws . String ( "test-instance-fake-name" )},
{ Key : aws . String ( "contains-Name" ), Value : aws . String ( "test-instance-contains-name" )},
},
2023-07-04 11:03:24 -07:00
NetworkInterfaces : [] types . InstanceNetworkInterface {
2023-03-20 11:02:17 -07:00
{
PrivateIpAddress : aws . String ( "10.0.0.1" ),
PrivateDnsName : aws . String ( "test.example.internal" ),
2023-07-04 11:03:24 -07:00
PrivateIpAddresses : [] types . InstancePrivateIpAddress {
2023-03-20 11:02:17 -07:00
{
2023-07-04 11:03:24 -07:00
Association : & types . InstanceNetworkInterfaceAssociation {
2023-03-20 11:02:17 -07:00
PublicIp : aws . String ( "1.1.1.1" ),
PublicDnsName : aws . String ( "test.example.com" ),
},
PrivateIpAddress : aws . String ( "10.0.0.1" ),
PrivateDnsName : aws . String ( "test.example.internal" ),
},
},
},
},
},
2026-05-27 13:12:34 -07:00
catalogAttrs : & CatalogAttributes {},
2023-03-20 11:02:17 -07:00
expected : & pb . ListHostsResponseHost {
ExternalId : "foobar" ,
ExternalName : "test-instance-actual-name" ,
IpAddresses : [] string { "10.0.0.1" , "1.1.1.1" },
DnsNames : [] string { "test.example.internal" , "test.example.com" },
},
},
2023-04-17 11:00:12 +01:00
{
name : "good, single IP w/public addr and external name" ,
2023-07-04 11:03:24 -07:00
instance : types . Instance {
2023-04-17 11:00:12 +01:00
InstanceId : aws . String ( "foobar" ),
PrivateIpAddress : aws . String ( "10.0.0.1" ),
PrivateDnsName : aws . String ( "test.example.internal" ),
PublicIpAddress : aws . String ( "1.1.1.1" ),
PublicDnsName : aws . String ( "test.example.com" ),
2023-07-04 11:03:24 -07:00
Tags : [] types . Tag {
2023-04-17 11:00:12 +01:00
{ Key : aws . String ( "Name" ), Value : aws . String ( "test-instance-actual-name" )}, // The tag name is "Name", not "name".
{ Key : aws . String ( "name" ), Value : aws . String ( "test-instance-fake-name" )},
{ Key : aws . String ( "contains-Name" ), Value : aws . String ( "test-instance-contains-name" )},
},
2023-07-04 11:03:24 -07:00
NetworkInterfaces : [] types . InstanceNetworkInterface {
2023-04-17 11:00:12 +01:00
{
PrivateIpAddress : aws . String ( "10.0.0.1" ),
PrivateDnsName : aws . String ( "test.example.internal" ),
2023-07-04 11:03:24 -07:00
PrivateIpAddresses : [] types . InstancePrivateIpAddress {
2023-04-17 11:00:12 +01:00
{
2023-07-04 11:03:24 -07:00
Association : & types . InstanceNetworkInterfaceAssociation {
2023-04-17 11:00:12 +01:00
PublicIp : aws . String ( "1.1.1.1" ),
PublicDnsName : aws . String ( "test.example.com" ),
},
PrivateIpAddress : aws . String ( "10.0.0.1" ),
PrivateDnsName : aws . String ( "test.example.internal" ),
},
},
},
},
},
2026-05-27 13:12:34 -07:00
catalogAttrs : & CatalogAttributes {},
2023-04-17 11:00:12 +01:00
expected : & pb . ListHostsResponseHost {
ExternalId : "foobar" ,
ExternalName : "test-instance-actual-name" ,
IpAddresses : [] string { "10.0.0.1" , "1.1.1.1" },
DnsNames : [] string { "test.example.internal" , "test.example.com" },
},
},
2021-10-07 12:40:55 -07:00
}
for _ , tc := range cases {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
require := require . New ( t )
2026-05-27 13:12:34 -07:00
actual , err := awsInstanceToHost ( tc . instance , tc . catalogAttrs )
2021-10-07 12:40:55 -07:00
if tc . expectedErr != "" {
require . EqualError ( err , tc . expectedErr )
return
}
require . NoError ( err )
require . Equal ( tc . expected , actual )
})
}
}
2023-07-04 11:03:24 -07:00
func TestAppendDistinct ( t * testing . T ) {
cases := [] struct {
name string
slice [] string
elems [] * string
expected [] string
}{
{
name : "empty slice, empty elems" ,
slice : [] string {},
elems : [] * string {},
expected : [] string {},
},
{
name : "empty elems" ,
slice : [] string { "bar" },
elems : [] * string {},
expected : [] string { "bar" },
},
{
name : "skip nil elem" ,
slice : [] string { "bar" },
elems : [] * string { nil },
expected : [] string { "bar" },
},
{
name : "skip empty elem" ,
slice : [] string { "bar" },
elems : [] * string { aws . String ( "" )},
expected : [] string { "bar" },
},
{
name : "skip duplicate elem" ,
slice : [] string { "bar" },
elems : [] * string {
aws . String ( "bar" ),
aws . String ( "foo" ),
aws . String ( "foo" ),
},
expected : [] string { "bar" , "foo" },
},
}
for _ , tc := range cases {
tc := tc
t . Run ( tc . name , func ( t * testing . T ) {
require := require . New ( t )
actual := appendDistinct ( tc . slice , tc . elems ... )
require . ElementsMatch ( actual , tc . expected )
})
}
}
2024-09-12 16:46:55 +01:00
func TestDryRunValidation ( t * testing . T ) {
2024-10-28 11:50:18 -07:00
t . Run ( "nil credential state" , func ( t * testing . T ) {
st := dryRunValidation ( context . Background (), nil , nil )
2024-09-12 16:46:55 +01:00
require . NotNil ( t , st )
require . Equal ( t , codes . InvalidArgument . String (), st . Code (). String ())
require . Equal ( t , "persisted state is required" , st . Message ())
})
t . Run ( "ec2ClientErr" , func ( t * testing . T ) {
st := dryRunValidation ( context . Background (), & awsCatalogPersistedState {
AwsCredentialPersistedState : & credential . AwsCredentialPersistedState {
CredentialsConfig : & awsutil . CredentialsConfig {},
},
testEC2APIFunc : func ( ... aws . Config ) ( EC2API , error ) {
return nil , fmt . Errorf ( "oops ec2 client err" )
},
2024-10-28 11:50:18 -07:00
}, [] ec2Option {})
2024-09-12 16:46:55 +01:00
require . NotNil ( t , st )
require . Equal ( t , codes . InvalidArgument . String (), st . Code (). String ())
require . Equal ( t , "error getting EC2 client: oops ec2 client err" , st . Message ())
})
t . Run ( "describeInstancesErr" , func ( t * testing . T ) {
st := dryRunValidation ( context . Background (), & awsCatalogPersistedState {
AwsCredentialPersistedState : & credential . AwsCredentialPersistedState {
CredentialsConfig : & awsutil . CredentialsConfig {
AccessKey : "AKIAfoo" ,
SecretKey : "baz" ,
},
},
testEC2APIFunc : newTestMockEC2 ( nil , testMockEC2WithDescribeInstancesError ( fmt . Errorf ( "oops describe instances error" ))),
2024-10-28 11:50:18 -07:00
}, [] ec2Option {})
2024-09-12 16:46:55 +01:00
require . NotNil ( t , st )
require . Equal ( t , codes . FailedPrecondition . String (), st . Code (). String ())
require . Equal ( t , "aws describe instances failed: oops describe instances error" , st . Message ())
})
t . Run ( "success" , func ( t * testing . T ) {
st := dryRunValidation ( context . Background (), & awsCatalogPersistedState {
AwsCredentialPersistedState : & credential . AwsCredentialPersistedState {
CredentialsConfig : & awsutil . CredentialsConfig {
AccessKey : "AKIAfoo" ,
SecretKey : "baz" ,
},
},
testEC2APIFunc : newTestMockEC2 ( nil , testMockEC2WithDescribeInstancesOutput ( & ec2 . DescribeInstancesOutput {})),
2024-10-28 11:50:18 -07:00
}, [] ec2Option {})
2024-09-12 16:46:55 +01:00
require . Nil ( t , st )
})
}