imds

package module
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 7, 2023 License: MIT Imports: 12 Imported by: 0

README

testcontainers-imds

Build status License MIT Go Report Card Go Version DeepSource

Testcontainers wrapper for the Instance Metadata Mock (imds-mock) tool. Quickly and easily simulate the Amazon Instance Metadata Service (IMDS) for localised testing.

Quick Start

Import the library into your project:

go get github.com/purpleclay/testcontainers-imds

Then write your first test:

package imds_test

import (
    "context"
    "testing"

    imds "github.com/purpleclay/testcontainers-imds"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

func TestInstanceMetadata(t *testing.T) {
    ctx := context.Background()

    container, err := imds.Start(ctx)
    require.NoError(t, err)
    defer container.Terminate(ctx)

    ipv4, _, _ := container.Get(imds.PathLocalIPv4)

    assert.Equal(t, imds.ValueLocalIPv4, ipv4)
}

If you need more examples, take a look here.

Documentation

Overview

Package imds exposes the Instance Metadata Mock (imds-mock) container through testcontainers-go supporting the simulation and testing of the Amazon Instance Metadata Service (IMDS). Supporting documentation for the Instance Metatadata Mock can be found at: https://docs.purpleclay.dev/imds-mock/

To quickly identify which instance metadata categories are supported, see: https://docs.purpleclay.dev/imds-mock/reference/instance-metadata/

Index

Constants

View Source
const (
	PathAMIID                                 = "ami-id"
	PathAMILaunchIndex                        = "ami-launch-index"
	PathAMIManifestPath                       = "ami-manifest-path"
	PathBlockDeviceMappingAMI                 = "block-device-mapping/ami"
	PathBlockDeviceMappingEBS2                = "block-device-mapping/ebs2"
	PathBlockDeviceMappingRoot                = "block-device-mapping/root"
	PathEventsMaintenanceHistory              = "events/maintenance/history"
	PathEventsMaintenanceScheduled            = "events/maintenance/scheduled"
	PathEventsRecommendationsRebalance        = "events/recommendations/rebalance"
	PathHostname                              = "hostname"
	PathIAMInfo                               = "iam/info"
	PathIAMSecurityCredentials                = "iam/security-credentials/ssm-access"
	PathInstanceAction                        = "instance-action"
	PathInstanceID                            = "instance-id"
	PathInstanceLifecycle                     = "instance-life-cycle"
	PathInstanceType                          = "instance-type"
	PathLocalHostname                         = "local-hostname"
	PathLocalIPv4                             = "local-ipv4"
	PathMAC                                   = "mac"
	PathNetworkInterfaces0DeviceNumber        = "network/interfaces/macs/06:e5:43:29:8f:08/device-number"
	PathNetworkInterfaces0InterfaceID         = "network/interfaces/macs/06:e5:43:29:8f:08/interface-id"
	PathNetworkInterfaces0LocalHostname       = "network/interfaces/macs/06:e5:43:29:8f:08/local-hostname"
	PathNetworkInterfaces0LocalIPv4s          = "network/interfaces/macs/06:e5:43:29:8f:08/local-ipv4s"
	PathNetworkInterfaces0MAC                 = "network/interfaces/macs/06:e5:43:29:8f:08/mac"
	PathNetworkInterfaces0OwnerID             = "network/interfaces/macs/06:e5:43:29:8f:08/owner-id"
	PathNetworkInterfaces0SecurityGroups      = "network/interfaces/macs/06:e5:43:29:8f:08/security-groups"
	PathNetworkInterfaces0SecurityGroupIDs    = "network/interfaces/macs/06:e5:43:29:8f:08/security-group-ids"
	PathNetworkInterfaces0SubnetID            = "network/interfaces/macs/06:e5:43:29:8f:08/subnet-id"
	PathNetworkInterfaces0SubnetIPv4CIDRBlock = "network/interfaces/macs/06:e5:43:29:8f:08/subnet-ipv4-cidr-block"
	PathNetworkInterfaces0VPCID               = "network/interfaces/macs/06:e5:43:29:8f:08/vpc-id"
	PathNetworkInterfaces0VPCIDPv4CIDRBlock   = "network/interfaces/macs/06:e5:43:29:8f:08/vpc-ipv4-cidr-block"
	PathNetworkInterfaces0VPCIDPv4CIDRBlocks  = "network/interfaces/macs/06:e5:43:29:8f:08/vpc-ipv4-cidr-blocks"
	PathNetworkInterfaces0VPCIDPv6CIDRBlocks  = "network/interfaces/macs/06:e5:43:29:8f:08/vpc-ipv6-cidr-blocks"
	PathPlacementAvailabilityZone             = "placement/availability-zone"
	PathPlacementAvailabilityZoneID           = "placement/availability-zone-id"
	PathPlacementRegion                       = "placement/region"
	PathProfile                               = "profile"
	PathPublicKeys0OpenSSHKey                 = "public-keys/0/openssh-key"
	PathReservationID                         = "reservation-id"
	PathSecurityGroups                        = "security-groups"
	PathServicesDomain                        = "services/domain"
	PathServicesPartition                     = "services/partition"
	PathSpotInstanceAction                    = "spot/instance-action"
	PathSpotTerminationTime                   = "spot/termination-time"
	PathTagsInstance                          = "tags/instance"
)

Instance Metadata is divided into categories. To retrieve instance metadata, a category is provided within the request. To find a comprehensive description of each category, view the official AWS documentation at: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html

View Source
const (
	ValueAMIID                                 = "ami-0e34bbddc66def5ac"
	ValueAMILaunchIndex                        = "0"
	ValueAMIManifestPath                       = "(unknown)"
	ValueBlockDeviceMappingAMI                 = "/dev/xvda"
	ValueBlockDeviceMappingEBS2                = "sdb"
	ValueBlockDeviceMappingRoot                = "/dev/xvda"
	ValueHostname                              = "ip-10-0-1-100.us-east-1.compute.internal"
	ValueIAMInfo                               = valueIAMInfo
	ValueIAMSecurityCredentials                = valueIAMSecurityCredentials
	ValueInstanceAction                        = "none"
	ValueInstanceID                            = "i-0decb1524582da041"
	ValueInstanceLifecycle                     = "on-demand"
	ValueInstanceType                          = "m4.xlarge"
	ValueLocalHostname                         = "ip-10-0-1-100.us-east-1.compute.internal"
	ValueLocalIPv4                             = "10.0.1.100"
	ValueMAC                                   = "06:e5:43:29:8f:08"
	ValueNetworkInterfaces0DeviceNumber        = "0"
	ValueNetworkInterfaces0InterfaceID         = "eni-01180ca4a78168553"
	ValueNetworkInterfaces0LocalHostname       = "ip-10-0-1-100.us-east-1.compute.internal"
	ValueNetworkInterfaces0LocalIPv4s          = "10.0.1.100"
	ValueNetworkInterfaces0MAC                 = "06:e5:43:29:8f:08"
	ValueNetworkInterfaces0OwnerID             = "112233445566"
	ValueNetworkInterfaces0SecurityGroups      = "ssm-sg"
	ValueNetworkInterfaces0SecurityGroupIDs    = "sg-083739656b4679c06"
	ValueNetworkInterfaces0SubnetID            = "subnet-0d908159d6c3e2e54"
	ValueNetworkInterfaces0SubnetIPv4CIDRBlock = "10.0.1.0/24"
	ValueNetworkInterfaces0VPCID               = "vpc-016d173db537793d1"
	ValueNetworkInterfaces0VPCIDPv4CIDRBlock   = "10.0.0.0/16"
	ValueNetworkInterfaces0VPCIDPv4CIDRBlocks  = "10.0.0.0/16"
	ValueNetworkInterfaces0VPCIDPv6CIDRBlocks  = "2a05:d01c:f2d:3200::/56"
	ValuePlacementAvailabilityZone             = "us-east-1a"
	ValuePlacementAvailabilityZoneID           = "use1-az4"
	ValuePlacementRegion                       = "us-east-1"
	ValueProfile                               = "default-hvm"
	ValuePublicKeys0OpenSSHKey                 = "" /* 385-byte string literal not displayed */
	ValueReservationID                         = "r-0c4dee716c0dbe3c9"
	ValueSecurityGroups                        = `["ssm-sg"]`
	ValueServicesDomain                        = "amazonaws.com"
	ValueServicesPartition                     = "aws"
	ValueTagsInstance                          = "Name"
)

Instance Metadata values as returned by the Instance Metadata mock for each supported category. Values are not provided for the following categories, as the Instance Metadata mock returns dated values:

  • events/maintenance/history
  • events/maintenance/scheduled
  • events/recommendations/rebalance
  • spot/instance-action
  • spot/termination-time
View Source
const (
	// MinTokenTTLInSeconds defines the minimum duration of a session token in seconds
	MinTokenTTLInSeconds = 1

	// MaxTokenTTLInSeconds defines the maximum duration of a session token in seconds
	MaxTokenTTLInSeconds = 21600

	// AllCategories triggers the retrieval of all instance metadata categories when
	// using either the Get() or GetWithToken() operations
	AllCategories = ""
)

Variables

This section is empty.

Functions

func InstanceTagPath added in v0.7.0

func InstanceTagPath(tag string) string

InstanceTagPath generates a instance tag category path based on the provided tag

Types

type Container

type Container struct {
	testcontainers.Container
	// contains filtered or unexported fields
}

Container represents an instance of an AEMM container

func MustStart

func MustStart(ctx context.Context) *Container

MustStart behaves in the same way as Start but panics if the container cannot be started for any reason. This removes the need to handle any returned errors, simplifying initialisation.

As the caller it is your responsibility to terminate the container by invoking the Terminate() method on the container.

func MustStartWith

func MustStartWith(ctx context.Context, opts Options) *Container

MustStartWith behaves in the same way as StartWith but panics if the container cannot be started for any reason. This removes the need to handle any returned errors, simplifying initialisation.

As the caller it is your responsibility to terminate the container by invoking the Terminate() method on the container.

func Start

func Start(ctx context.Context) (*Container, error)

Start will create and start an instance of the Instance Metadata Mock (imds-mock), simulating the Amazon EC2 Metadata Service (IMDS). Once started, IMDS will be accessible through the expected endpoint. As the caller it is your responsibility to terminate the container by invoking the Terminate() method on the container.

http://localhost:1338/latest/meta-data/

By using the default settings, both IMDSv1 and IMDSv2 are supported. Metadata about the mocked EC2 instance can then be retrieved using any of the documented categories, https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html

For example:

curl http://localhost:1338/latest/meta-data/local-ipv4
curl http://localhost:1338/latest/meta-data/block-device-mapping/root

To ensure your AWS config is configured to call this mock, the required option needs to be set:

package main

import "github.com/aws/aws-sdk-go-v2/config"

func main() {
	config.LoadDefaultConfig(context.TODO(), config.WithEC2IMDSEndpoint("http://localhost:1338/latest/meta-data/"))
}

func StartWith

func StartWith(ctx context.Context, opts Options) (*Container, error)

StartWith will create and start an instance of the Instance Metadata Mock (imds-mock), simulating the Amazon EC2 Metadata Service (IMDS). The launch behaviour of the mock can be configured through the provided LaunchOptions. Once started, IMDS will be accessible through the endpoint. As the caller it is your responsibility to terminate the container by invoking the Terminate() method on the container.

http://localhost:1338/latest/meta-data/

By using the default settings, both IMDSv1 and IMDSv2 are supported. Metadata about the mocked EC2 instance can then be retrieved using any of the documented categories, https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html

For example:

curl http://localhost:1338/latest/meta-data/local-ipv4
curl http://localhost:1338/latest/meta-data/block-device-mapping/root

To ensure your AWS config is configured to call this mock, the required option needs to be set:

package main

import "github.com/aws/aws-sdk-go-v2/config"

func main() {
	config.LoadDefaultConfig(context.TODO(), config.WithEC2IMDSEndpoint("http://localhost:1338/latest/meta-data/"))
}

func (*Container) Get added in v0.7.0

func (c *Container) Get(category string) (string, int, error)

Get will attempt to retrieve an instance category from the running container. The raw value of the category will be returned from the container upon success. If any HTTP failure occurs while trying to retrieve a category, the raw error is returned

Status Codes:

200: category was retrieved
404: category does not exist

func (*Container) GetV2 added in v0.7.0

func (c *Container) GetV2(category, token string) (string, int, error)

GetV2 will attempt to retrieve an instance category from the running container using an authenticated session token based request. If the container was not started in IMDSv2 mode, the token will have no effect. The raw value of the category will be returned from the container upon success. If any HTTP failure occurs while trying to retrieve a category, the raw error is returned

Status Codes:

  • 200: category was retrieved
  • 404: category does not exist
  • 401: session token is either invalid or expired

func (*Container) TokenURL added in v0.8.0

func (c *Container) TokenURL() string

TokenURL returns the URL for accessing the token endpoint of the container

http://localhost:<EXPOSED_PORT>/latest/api/token

func (*Container) TokenWithTTL added in v0.7.0

func (c *Container) TokenWithTTL(ttl int) (string, int, error)

TokenWithTTL will attempt to generate a session token with the provided TTL in seconds. If any HTTP failure occurs while trying to retrieve a category, the raw error is returned

Status Codes:

200: token was created
400: TTL was outside the expected bounds (min: 1, max: 21600)

func (*Container) URL

func (c *Container) URL() string

URL returns the URL for accessing the metadata endpoint of the container

http://localhost:<EXPOSED_PORT>/latest/meta-data/

type Options

type Options struct {
	// ExcludeInstanceTags will ensure any tags associated with the instance
	// are not exposed through the tags/instance category. Enable this to
	// simulate the default behaviour of an EC2
	// 	@Default false
	ExcludeInstanceTags bool

	// ExposedPort defines which port on the host will be mapped to the default port
	// of the container
	//	@Default 1338
	ExposedPort string `default:"1338"`

	// Image is the name of the Instance Metadata Mock image to pull when
	// launching the container
	// 	@Default ghcr.io/purpleclay/imds-mock
	Image string `default:"ghcr.io/purpleclay/imds-mock"`

	// ImageTag is the version of the Instance Metadata Mock image to pull
	// from the source docker registry
	//	@Default latest
	ImageTag string `default:"latest"`

	// InstanceTags defines a list of instance tags that should be exposed through
	// the instance/tags metadata category, overwriting any existing defaults
	//	@Default existing instance tags will not be overwritten
	InstanceTags map[string]string

	// Pretty print any JSON response
	//	@Default false
	Pretty bool

	// Spot is a flag that controls the simulation of a spot instance and interruption
	// notice
	//	@Default false
	Spot bool

	// SpotAction is used in conjunction with the spot flag to control both the type
	// and initial delay of the spot interruption notice.
	//   @Default
	SpotAction imdsmock.SpotActionEvent `default:"{\"Action\":\"terminate\", \"Duration\": \"0s\"}"`

	// IMDSv2 will enforce IMDSv2 and require a session token when making metadata
	// requests. A token is requested by issuing a PUT request to the token endpoint, and
	// supplying a TTL of between 1 and 2600 seconds.
	//
	//	PUT localhost:1338/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"
	//
	// Any subsequent request must provide the token as a header:
	//
	// 	GET localhost:1338/latest/meta-data/local-ipv4 -H "X-aws-ec2-metadata-token: $TOKEN"
	//
	//	@Default false
	IMDSv2 bool
}

Options defines all configurable options when starting the AEMM container

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL