// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package config import ( "encoding/json" "github.com/aws/amazon-ec2-metadata-mock/pkg/mock/static/types" "github.com/spf13/pflag" ) const ( // metadata keys mac = "mac" publicIpv4 = "public-ipv4" iamSecurityCredentialsRole = "iam-security-credentials-role" iamSecurityCredentials = "iam-security-credentials" elasticInferenceAssociations = "elastic-inference-associations" elasticInferenceAccelerator = "elastic-inference-accelerator" // prefixed with "mac" because query requires mac address and to avoid collisions with existing metadata keys // ex: network/interfaces/macs//device-number macDeviceNumber = "mac-device-number" macNetworkInterfaceId = "mac-network-interface-id" macNetworkInterfaceCardIdx = "mac-network-interface-card-index" macIpv4Associations = "mac-ipv4-associations" macIpv6Associations = "mac-ipv6-associations" macLocalHostname = "mac-local-hostname" macLocalIpv4s = "mac-local-ipv4s" macMac = "mac-mac" macOwnerId = "mac-owner-id" macPublicHostname = "mac-public-hostname" macPublicIpv4s = "mac-public-ipv4s" macSecurityGroups = "mac-security-groups" macSecurityGroupIds = "mac-security-group-ids" macSubnetId = "mac-subnet-id" macSubnetIpv4CidrBlock = "mac-subnet-ipv4-cidr-block" macSubnetIpv6CidrBlocks = "mac-subnet-ipv6-cidr-blocks" macVpcId = "mac-vpc-id" macVpcIpv4CidrBlock = "mac-vpc-ipv4-cidr-block" macVpcIpv4CidrBlocks = "mac-vpc-ipv4-cidr-blocks" macVpcIpv6CidrBlocks = "mac-vpc-ipv6-cidr-blocks" ) var ( mdCfgPrefix = "metadata." mdPathsCfgPrefix = mdCfgPrefix + "paths." mdValuesCfgPrefix = mdCfgPrefix + "values." // mapping of mdValue KEYS to mdPath KEYS requiring path substitutions on override mdValueToPlaceholderPathsKeyMap = map[string][]string{ // ex: "metadata.values.mac-address" : {"metadata.paths.network-interface-id", "metadata.paths.device-index", ...} mdValuesCfgPrefix + mac: { mdPathsCfgPrefix + macDeviceNumber, mdPathsCfgPrefix + macNetworkInterfaceId, mdPathsCfgPrefix + macNetworkInterfaceCardIdx, mdPathsCfgPrefix + macIpv4Associations, mdPathsCfgPrefix + macLocalHostname, mdPathsCfgPrefix + macLocalIpv4s, mdPathsCfgPrefix + macMac, mdPathsCfgPrefix + macOwnerId, mdPathsCfgPrefix + macPublicHostname, mdPathsCfgPrefix + macPublicIpv4s, mdPathsCfgPrefix + macSecurityGroups, mdPathsCfgPrefix + macSecurityGroupIds, mdPathsCfgPrefix + macSubnetId, mdPathsCfgPrefix + macSubnetIpv4CidrBlock, mdPathsCfgPrefix + macVpcId, mdPathsCfgPrefix + macVpcIpv4CidrBlock, mdPathsCfgPrefix + macVpcIpv4CidrBlocks, mdPathsCfgPrefix + macIpv6Associations, mdPathsCfgPrefix + macSubnetIpv6CidrBlocks, mdPathsCfgPrefix + macVpcIpv6CidrBlocks}, mdValuesCfgPrefix + publicIpv4: { mdPathsCfgPrefix + macIpv4Associations}, mdValuesCfgPrefix + iamSecurityCredentialsRole: { mdPathsCfgPrefix + iamSecurityCredentials}, mdValuesCfgPrefix + elasticInferenceAssociations: { mdPathsCfgPrefix + elasticInferenceAccelerator}, } // supported URL paths to run a mock mdPathsDefaults = map[string]interface{}{} // values in mock responses mdValuesDefaults = map[string]interface{}{} // mapping of metadata value keys to its nested struct mdNestedValues = map[string]interface{}{ "metadata.values.elastic-inference-accelerator": types.ElasticInferenceAccelerator{}, } ) // GetCfgMdValPrefix returns the prefix to use to access metadata values in config func GetCfgMdValPrefix() string { return mdCfgPrefix + "." + mdValuesCfgPrefix + "." } // GetCfgMdPathsPrefix returns the prefix to use to access metadata values in config func GetCfgMdPathsPrefix() string { return mdCfgPrefix + "." + mdPathsCfgPrefix + "." } // BindMetadataCfg binds a flag that represents a metadata value to configuration func BindMetadataCfg(flag *pflag.Flag) { bindFlagWithKeyPrefix(flag, mdValuesCfgPrefix) } // SetMetadataDefaults sets config defaults for metadata paths and values func SetMetadataDefaults(jsonWithDefaults []byte) { // Unmarshal to map to preserve keys for Paths and Values var defaultsMap map[string]interface{} json.Unmarshal(jsonWithDefaults, &defaultsMap) mdPaths := defaultsMap["metadata"].(map[string]interface{})["paths"].(map[string]interface{}) mdValues := defaultsMap["metadata"].(map[string]interface{})["values"].(map[string]interface{}) for k, v := range mdPaths { newKey := mdPathsCfgPrefix + k // ex: "metadata.paths.ami-id": "/latest/meta-data/ami-id" mdPathsDefaults[newKey] = v } for k, v := range mdValues { newKey := mdValuesCfgPrefix + k // ex: "metadata.values.ami-id": "ami-0a887e401f7654935" mdValuesDefaults[newKey] = v // if mdvalue is a nested struct, then re-unmarshal json data to correct type if nestedStruct, ok := mdNestedValues[newKey]; ok { updatedVal, err := unmarshalToNestedStruct(v, nestedStruct) if err == nil { mdValuesDefaults[newKey] = updatedVal } } } LoadConfigFromDefaults(mdPathsDefaults) LoadConfigFromDefaults(mdValuesDefaults) } // GetMetadataDefaults returns config defaults for metadata paths and values func GetMetadataDefaults() (map[string]interface{}, map[string]interface{}) { return mdPathsDefaults, mdValuesDefaults } // GetMetadataValueToPlaceholderPathsKeyMap returns collection of metadata values that are substituted into paths func GetMetadataValueToPlaceholderPathsKeyMap() map[string][]string { return mdValueToPlaceholderPathsKeyMap } // unmarshalToNestedStruct returns a struct with its nested values populated correctly func unmarshalToNestedStruct(originalValue interface{}, unmarshalToStruct interface{}) (interface{}, error) { valAsJson, err := json.Marshal(originalValue) if err != nil { return nil, err } // all supported nested structs switch unmarshalToStruct.(type) { case types.ElasticInferenceAccelerator: nestedStruct, ok := unmarshalToStruct.(types.ElasticInferenceAccelerator) if ok { if err = json.Unmarshal(valAsJson, &nestedStruct); err != nil { return nil, err } return nestedStruct, nil } } return nil, nil }