Files
threshold-plugin-aws/plugin/attributes.go
T

221 lines
5.7 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package plugin
import (
"fmt"
"sort"
"strings"
"time"
"github.com/mitchellh/mapstructure"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/structpb"
)
func invalidArgumentError(msg string, f map[string]string) error {
var fieldMsgs []string
for field, val := range f {
fieldMsgs = append(fieldMsgs, fmt.Sprintf("%s: %s", field, val))
}
if len(fieldMsgs) > 0 {
sort.Strings(fieldMsgs)
msg = fmt.Sprintf("%s: [%s]", msg, strings.Join(fieldMsgs, ", "))
}
return status.Error(codes.InvalidArgument, msg)
}
// CatalogAttributes is a Go-native representation of the Attributes
// map.
type CatalogAttributes struct {
Region string
DisableCredentialRotation bool
}
func getCatalogAttributes(in *structpb.Struct) (*CatalogAttributes, error) {
unknownFields := structFields(in)
result := new(CatalogAttributes)
badFields := make(map[string]string)
var err error
result.Region, err = getStringValue(in, constRegion, true)
if err != nil {
badFields[fmt.Sprintf("attributes.%s", constRegion)] = err.Error()
}
delete(unknownFields, constRegion)
result.DisableCredentialRotation, err = getBoolValue(in, constDisableCredentialRotation, false)
if err != nil {
badFields[fmt.Sprintf("attributes.%s", constDisableCredentialRotation)] = err.Error()
}
delete(unknownFields, constDisableCredentialRotation)
for s := range unknownFields {
badFields[fmt.Sprintf("attributes.%s", s)] = "unrecognized field"
}
if len(badFields) > 0 {
return nil, invalidArgumentError("Invalid arguments in catalog attributes", badFields)
}
return result, nil
}
// CatalogSecrets is a Go-native representation of the Secrets map.
type CatalogSecrets struct {
AccessKeyId string
SecretAccessKey string
}
func getCatalogSecrets(in *structpb.Struct) (*CatalogSecrets, error) {
unknownFields := structFields(in)
result := new(CatalogSecrets)
badFields := make(map[string]string)
var err error
result.AccessKeyId, err = getStringValue(in, constAccessKeyId, true)
if err != nil {
badFields[fmt.Sprintf("secrets.%s", constAccessKeyId)] = err.Error()
}
delete(unknownFields, constAccessKeyId)
result.SecretAccessKey, err = getStringValue(in, constSecretAccessKey, true)
if err != nil {
badFields[fmt.Sprintf("secrets.%s", constSecretAccessKey)] = err.Error()
}
delete(unknownFields, constSecretAccessKey)
for s := range unknownFields {
badFields[fmt.Sprintf("secrets.%s", s)] = "unrecognized field"
}
if len(badFields) > 0 {
return nil, invalidArgumentError("Error in the secrets provided", badFields)
}
return result, 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 := 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, 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 getStringValue(in *structpb.Struct, k string, required bool) (string, error) {
mv := in.GetFields()
v, ok := mv[k]
if !ok {
if required {
return "", fmt.Errorf("missing required value %q", k)
}
return "", nil
}
s, ok := v.AsInterface().(string)
if !ok {
return "", fmt.Errorf("unexpected type for value %q: want string, got %T", k, v.AsInterface())
}
if s == "" && required {
return "", fmt.Errorf("value %q cannot be empty", k)
}
return s, nil
}
func getBoolValue(in *structpb.Struct, k string, required bool) (bool, error) {
mv := in.GetFields()
v, ok := mv[k]
if !ok {
if required {
return false, fmt.Errorf("missing required value %q", k)
}
return false, nil
}
b, ok := v.AsInterface().(bool)
if !ok {
return false, fmt.Errorf("unexpected type for value %q: want bool, got %T", k, v.AsInterface())
}
return b, nil
}
func getTimeValue(in *structpb.Struct, k string) (time.Time, error) {
mv := in.GetFields()
v, ok := mv[k]
if !ok {
return time.Time{}, nil
}
tRaw, ok := v.AsInterface().(string)
if !ok {
return time.Time{}, fmt.Errorf("unexpected type for value %q: want string, got %T", k, v.AsInterface())
}
t, err := time.Parse(time.RFC3339Nano, tRaw)
if err != nil {
return time.Time{}, fmt.Errorf("could not parse time in value %q: %w", k, err)
}
return t, nil
}
func structFields(s *structpb.Struct) map[string]struct{} {
m := make(map[string]struct{}, len(s.GetFields()))
for k := range s.GetFields() {
m[k] = struct{}{}
}
return m
}
func keysAsString(m map[string]struct{}) string {
s := make([]string, 0, len(m))
for k := range m {
s = append(s, k)
}
sort.Strings(s)
return strings.Join(s, ", ")
}