// Copyright IBM Corp. 2021, 2026 // SPDX-License-Identifier: MPL-2.0 package host import ( "fmt" cred "repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-aws/internal/credential" "repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-aws/internal/errors" "repo.anhonesthost.net/CyberCoveLLC/threshold-plugin-aws/internal/values" "github.com/mitchellh/mapstructure" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/structpb" ) // CatalogAttributes is a Go-native representation of the Attributes // map. type CatalogAttributes struct { *cred.CredentialAttributes // TargetAccount optionally holds the credentials used to AssumeRole into a second, affiliated account when provided. TargetAccount *cred.CredentialAttributes // DualStack is used for configuring how the aws client will resolve requests. DualStack bool // InstanceAddressesOnly specifies whether only the instance addresses (private IPv4/DNS, // public IPv4/DNS, and IPv6) are synced, or all addresses, including those from // secondary ENIs, are synced. InstanceAddressesOnly bool } func getCatalogAttributes(in *structpb.Struct) (*CatalogAttributes, error) { unknownFields := values.StructFields(in) badFields := make(map[string]string) var err error credAttributes, err := cred.GetCredentialAttributes(in) if err != nil { return nil, err } var targetCredAttributes *cred.CredentialAttributes if hasTargetAccountAttributes(in) { targetCredAttributes, err = cred.GetTargetCredentialAttributes(in) if err != nil { return nil, err } } dualStack, err := values.GetBoolValue(in, ConstAwsDualStack, false) if err != nil { badFields[fmt.Sprintf("attributes.%s", ConstAwsDualStack)] = err.Error() } delete(unknownFields, ConstAwsDualStack) instanceAddressesOnly, err := values.GetBoolValue(in, ConstInstanceAddressesOnly, false) if err != nil { badFields[fmt.Sprintf("attributes.%s", ConstInstanceAddressesOnly)] = err.Error() } delete(unknownFields, ConstInstanceAddressesOnly) for s := range unknownFields { switch s { // Ignore knownFields from CredentialAttributes case cred.ConstRegion: continue case cred.ConstDisableCredentialRotation: continue case cred.ConstRoleArn: continue case cred.ConstRoleExternalId: continue case cred.ConstRoleSessionName: continue case cred.ConstRoleTags: continue case cred.ConstTargetRegion: continue case cred.ConstTargetRoleArn: continue case cred.ConstTargetRoleExternalId: continue case cred.ConstTargetRoleSessionName: continue case cred.ConstTargetRoleTags: continue default: badFields[fmt.Sprintf("attributes.%s", s)] = "unrecognized field" } } if len(badFields) > 0 { return nil, errors.InvalidArgumentError("Invalid arguments in catalog attributes", badFields) } return &CatalogAttributes{ CredentialAttributes: credAttributes, TargetAccount: targetCredAttributes, DualStack: dualStack, InstanceAddressesOnly: instanceAddressesOnly, }, nil } // SetAttributes is a Go-native representation of the Attributes map that can be // used for decoding the incoming map via mapstructure. type SetAttributes struct { Filters []string } func getSetAttributes(in *structpb.Struct) (*SetAttributes, error) { var setAttrs SetAttributes // Quick validation to ensure that there's no non-filter attributes // here for now. Make this more complex if we add more attributes // to host sets. unknownFields := values.StructFields(in) badFields := make(map[string]string) delete(unknownFields, ConstDescribeInstancesFilters) for a := range unknownFields { badFields[fmt.Sprintf("attributes.%s", a)] = "unrecognized field" } if len(badFields) > 0 { return nil, errors.InvalidArgumentError("Error in the attributes provided", badFields) } // Mapstructure complains if it expects a slice as output and sees a scalar // value. Rather than use WeakDecode and risk unintended consequences, I'm // manually making this change if necessary. inMap := in.AsMap() if filtersRaw, ok := inMap[ConstDescribeInstancesFilters]; ok { switch filterVal := filtersRaw.(type) { case string: inMap[ConstDescribeInstancesFilters] = []string{filterVal} } } if err := mapstructure.Decode(inMap, &setAttrs); err != nil { return nil, status.Errorf(codes.InvalidArgument, "error decoding set attributes: %s", err) } return &setAttrs, nil } func hasTargetAccountAttributes(in *structpb.Struct) bool { fields := in.GetFields() _, hasTargetRegion := fields[cred.ConstTargetRegion] _, hasTargetRoleArn := fields[cred.ConstTargetRoleArn] _, hasTargetRoleExternalID := fields[cred.ConstTargetRoleExternalId] _, hasTargetRoleSessionName := fields[cred.ConstTargetRoleSessionName] _, hasTargetRoleTags := fields[cred.ConstTargetRoleTags] return hasTargetRegion || hasTargetRoleArn || hasTargetRoleExternalID || hasTargetRoleSessionName || hasTargetRoleTags }