Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/gh 118 aws virtual network #624

Open
wants to merge 24 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 179 additions & 2 deletions data/tosca/yorc-aws-types.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,86 @@ tosca_definitions_version: yorc_tosca_simple_yaml_1_0
metadata:
template_name: yorc-aws-types
template_author: yorc
template_version: 1.1.0
template_version: 1.2.0

imports:
- yorc: <yorc-types.yml>

data_types:
yorc.datatypes.aws.SubnetType:
derived_from: tosca.datatypes.Root
# See : https://www.terraform.io/docs/providers/aws/r/subnet.html
properties:
availability_zone:
type: string
required: false
description: The AZ for the subnet
availability_zone_id:
type: string
required: false
description: The AZ ID for the subnet
cidr_block:
type: string
required: true
ipv6_cidr_block :
type: string
required: false
description: The IPv6 network range for the subnet, in CIDR notation. The subnet size must use a /64 prefix length.
map_public_ip_on_launch:
type: boolean
required: false
description: Specify true to indicate that instances launched into the subnet should be assigned a public IP address.
assign_ipv6_address_on_creation:
type: boolean
required: false
description: >
Specify true to indicate that network interfaces created in the specified subnet should be assigned an IPv6 address
tags:
type: map
description: A mapping of tags to assign to the resource.
required: false
entry_schema:
type: string

yorc.datatypes.aws.SecurityRuleType:
derived_from: tosca.datatypes.Root
# See : https://www.terraform.io/docs/providers/aws/r/subnet.html
properties:
from_port:
type: string
required: true
to_port:
type: string
required: true
protocol:
type: string
required: true

yorc.datatypes.aws.SecurityGroupType:
derived_from: tosca.datatypes.Root
# See : https://www.terraform.io/docs/providers/aws/r/subnet.html
properties:
name:
type: string
required: true
ingress:
type: yorc.datatypes.aws.SecurityRuleType
required: true
egress:
type: yorc.datatypes.aws.SecurityRuleType
required: true

relationship_types:
yorc.relationships.aws.Network:
derived_from: tosca.relationships.Network
properties:
subnet_id:
type: string
required: true
security_group:
type: string
required: true

node_types:
yorc.nodes.aws.Compute:
derived_from: yorc.nodes.Compute
Expand All @@ -29,7 +104,12 @@ node_types:
type: string
description: >
Comma-separated list of security groups to add to the Compute
required: true
required: false
subnet_id:
type: string
description: >
The VPC Subnet ID to launch in.
required: false
availability_zone:
type: string
required: false
Expand Down Expand Up @@ -101,4 +181,101 @@ node_types:
required: false
default: false

yorc.nodes.aws.Subnet:
derived_from: tosca.nodes.Network
# See : https://www.terraform.io/docs/providers/aws/r/subnet.html
properties:
availability_zone:
type: string
required: false
description: The AZ for the subnet
availability_zone_id:
type: string
required: false
description: The AZ ID for the subnet
cidr_block:
type: string
required: true
ipv6_cidr_block :
type: string
required: false
description: The IPv6 network range for the subnet, in CIDR notation. The subnet size must use a /64 prefix length.
map_public_ip_on_launch:
type: boolean
required: false
description: Specify true to indicate that instances launched into the subnet should be assigned a public IP address.
assign_ipv6_address_on_creation:
type: boolean
required: false
description: >
Specify true to indicate that network interfaces created in the specified subnet should be assigned an IPv6 address
vpc_id:
type: string
required: true
tags:
type: map
description: A mapping of tags to assign to the resource.
required: false
entry_schema:
type: string

yorc.nodes.aws.VPC:
derived_from: tosca.nodes.Network
# See https://www.terraform.io/docs/providers/aws/r/vpc.html
properties:
cidr_block:
type: string
required: true
instance_tenancy:
type: string
description: >
You can run instances in your VPC on single-tenant, dedicated hardware.
Select Dedicated to ensure that instances launched in this VPC are dedicated tenancy instances, regardless of the tenancy attribute specified at launch.
Select Default to ensure that instances launched in this VPC use the tenancy attribute specified at launch
default: default
constraints :
- valid_values: [default, dedicated]
enable_dns_support:
type: boolean
description: A boolean flag to enable/disable DNS support in the VPC. Defaults true.
default: true
required: false
enable_dns_hostnames:
type: boolean
default: false
description : A boolean flag to enable/disable DNS hostnames in the VPC. Defaults false.
required: false
enable_classiclink:
type: boolean
default: false
description: A boolean flag to enable/disable ClassicLink for the VPC. Only valid in regions and accounts that support EC2 Classic.
required: false
enable_classiclink_dns_support:
type: boolean
required: false
description: A boolean flag to enable/disable ClassicLink DNS Support for the VPC. Only valid in regions and accounts that support EC2 Classic
assign_generated_ipv6_cidr_block:
type: boolean
required: false
description: Requests an Amazon-provided IPv6 CIDR block with a /56 prefix length for the VPC. You cannot specify the range of IP addresses, or the size of the CIDR block.
tags:
type: map
description: A mapping of tags to assign to the resource.
required: false
entry_schema:
type: string
subnets:
type: list
description: Subnetworks in this VPC
required: false
entry_schema:
type: yorc.datatypes.aws.SubnetType
security_groups:
type: list
description: SecurityGroups in this VPC
required: false
entry_schema:
type: yorc.datatypes.aws.SecurityGroupType



82 changes: 75 additions & 7 deletions prov/terraform/aws/aws_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,16 @@ func (g *awsGenerator) generateAWSInstance(ctx context.Context, cfg config.Confi
}
instance.KeyName = keyName.RawString()

// security_groups needs to contain a least one occurrence
secGroups, err := deployments.GetNodePropertyValue(ctx, deploymentID, nodeName, "security_groups")
if err != nil {
return err
}
if secGroups == nil || secGroups.RawString() == "" {
return errors.Errorf("Missing mandatory parameter 'security_groups' node type for %s", nodeName)
}

for _, secGroup := range strings.Split(strings.NewReplacer("\"", "", "'", "").Replace(secGroups.RawString()), ",") {
secGroup = strings.TrimSpace(secGroup)
instance.SecurityGroups = append(instance.SecurityGroups, secGroup)
if secGroups.RawString() != "" {
for _, secGroup := range strings.Split(strings.NewReplacer("\"", "", "'", "").Replace(secGroups.RawString()), ",") {
secGroup = strings.TrimSpace(secGroup)
instance.SecurityGroups = append(instance.SecurityGroups, secGroup)
}
}

// Get connection info (user, private key)
Expand Down Expand Up @@ -141,6 +139,76 @@ func (g *awsGenerator) generateAWSInstance(ctx context.Context, cfg config.Confi
if placementGroup != nil {
instance.PlacementGroup = placementGroup.RawString()
}

// Network
subnetID, err := deployments.GetNodePropertyValue(ctx, deploymentID, nodeName, "subnet_id")
if err != nil {
return err
}
if subnetID != nil {
instance.SubnetID = subnetID.RawString()
}
hasNetwork, _, err := deployments.HasAnyRequirementFromNodeType(ctx, deploymentID, nodeName, "network", "yorc.nodes.aws.VPC")
if err != nil {
return err
}
if hasNetwork {
networkRequirements, err := deployments.GetRequirementsByTypeForNode(ctx, deploymentID, nodeName, "network")
if err != nil {
return err
}

i := 0
for _, networkReq := range networkRequirements {
networkInterface := &NetworkInterface{}
// TODO : Check if subnet and security group are defined in relationship
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO found

// networkInterface.SubnetID, err = deployments.LookupInstanceAttributeValue(ctx, deploymentID, networkReq.RequirementAssignment.Node, instanceName, "subnet_id")
// if err != nil {
// return err
// }

// If not, use default one
defaultSubnetID, err := deployments.LookupInstanceAttributeValue(ctx, deploymentID, networkReq.RequirementAssignment.Node, instanceName, "default_subnet_id")
if err != nil {
return err
}
networkInterface.SubnetID = defaultSubnetID

// With default Security Group
defaultSecurityGroup, err := deployments.LookupInstanceAttributeValue(ctx, deploymentID, networkReq.RequirementAssignment.Node, instanceName, "default_security_group")
if err != nil {
return err
}
networkInterface.SecurityGroups = make([]string, 1)
networkInterface.SecurityGroups = append(networkInterface.SecurityGroups, defaultSecurityGroup)

name := strings.ToLower("network-inteface-" + strconv.Itoa(i))
name = strings.Replace(strings.ToLower(name), "_", "-", -1)

// First interface will be considered as default one
if i == 0 {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Identical blocks of code found in 2 locations. Consider refactoring.

instance.NetworkInterface = map[string]string{
"network_interface_id": "${aws_network_interface." + name + ".id}",
"device_index": strconv.Itoa(i),
}
} else {
// Others interfaces are considered attachment
networkInterface.Attachment = map[string]string{
"instance": "${aws_instance." + instance.Tags.Name + ".id}",
"device_index": strconv.Itoa(i),
}
}

commons.AddResource(infrastructure, "aws_network_interface", name, networkInterface)

i++
}

// No security groups on instance level when defining customs ENI
// (So the instance will have be in the default security groups on creation)
instance.SecurityGroups = nil
}

// Add the AWS instance
commons.AddResource(infrastructure, "aws_instance", instance.Tags.Name, &instance)

Expand Down
31 changes: 31 additions & 0 deletions prov/terraform/aws/aws_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ func testSimpleAWSInstance(t *testing.T, cfg config.Configuration) {
require.Equal(t, "ComputeAWS-0", compute.Tags.Name)
require.Equal(t, "us-east-2c", compute.AvailabilityZone)
require.Equal(t, "myPlacement", compute.PlacementGroup)
require.Equal(t, "subnet-f578918e", compute.SubnetID)
require.Equal(t, true, compute.RootBlockDevice.DeleteOnTermination)
require.Len(t, compute.SecurityGroups, 1)
require.Contains(t, compute.SecurityGroups, "yorc-securityGroup")
Expand Down Expand Up @@ -373,3 +374,33 @@ func testSimpleAWSInstanceWithPersistentDisk(t *testing.T, cfg config.Configurat
assert.Equal(t, "/dev/sdf", attachedDisk.DeviceName)

}

func simpleAWSInstanceWithVPCandSubnet(t *testing.T, cfg config.Configuration, srv *testutil.TestServer) {
t.Parallel()

deploymentID := loadTestYaml(t)
g := awsGenerator{}
infrastructure := commons.Infrastructure{}
ctx := context.Background()
env := make([]string, 0)
outputs := make(map[string]string)

srv.PopulateKV(t, map[string][]byte{
path.Join(consulutil.DeploymentKVPrefix, deploymentID+"/topology/nodes/Network_Subnet/type"): []byte("yorc.nodes.aws.Subnet"),
path.Join(consulutil.DeploymentKVPrefix, deploymentID+"/topology/instances/Network_Subnet/0/attributes/subnet_id"): []byte("subnet_id"),
})

err := g.generateAWSInstance(ctx, cfg, deploymentID, "AWS_Compute", "0", &infrastructure, outputs, &env)
require.Nil(t, err)
require.Len(t, infrastructure.Resource["aws_instance"], 1)

instancesMap := infrastructure.Resource["aws_instance"].(map[string]interface{})
require.Contains(t, instancesMap, "AWS_Compute-0")

compute, ok := instancesMap["AWS_Compute-0"].(*ComputeInstance)
require.True(t, ok, "%s is not a ComputeInstance", "AWS_Compute-0")

assert.Equal(t, "${aws_network_interface.network-inteface-0.id}", compute.NetworkInterface["network_interface_id"], "Subnetwork is not retrieved")
assert.Equal(t, "0", compute.NetworkInterface["device_index"], "Subnetwork is not retrieved")

}
13 changes: 12 additions & 1 deletion prov/terraform/aws/consul_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"testing"

"github.com/stretchr/testify/require"

"github.com/ystia/yorc/v4/locations"
"github.com/ystia/yorc/v4/testutil"
)
Expand Down Expand Up @@ -64,6 +63,9 @@ func TestRunConsulAWSPackageTests(t *testing.T) {
t.Run("simpleAWSInstanceWithMalformedEIP", func(t *testing.T) {
testSimpleAWSInstanceWithMalformedEIP(t, cfg)
})
t.Run("simpleAWSInstanceWithVPCandSubnet", func(t *testing.T) {
simpleAWSInstanceWithVPCandSubnet(t, cfg, srv)
})
t.Run("generateTerraformInfraForAWSNode", func(t *testing.T) {
testGenerateTerraformInfraForAWSNode(t, cfg, locationMgr)
})
Expand All @@ -76,5 +78,14 @@ func TestRunConsulAWSPackageTests(t *testing.T) {
t.Run("simpleAWSInstanceWithPersistentDisk", func(t *testing.T) {
testSimpleAWSInstanceWithPersistentDisk(t, cfg, srv)
})
t.Run("simpleVPC", func(t *testing.T) {
testSimpleVPC(t, cfg)
})
t.Run("simpleSubnet", func(t *testing.T) {
testSimpleSubnet(t, srv, cfg)
})
t.Run("simpleVPCWithSubnet", func(t *testing.T) {
testSimpleVPCWithSubnet(t, srv, cfg)
})
})
}
11 changes: 11 additions & 0 deletions prov/terraform/aws/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,17 @@ func (g *awsGenerator) generateInstances(ctx context.Context, cfg config.Configu
if err != nil {
return err
}
case "yorc.nodes.aws.VPC":
err = g.generateVPC(ctx, *nodeParams, instanceName, outputs)
if err != nil {
return err
}

case "yorc.nodes.aws.Subnet":
err = g.generateSubnet(ctx, *nodeParams, instanceName, outputs)
if err != nil {
return err
}
default:
return errors.Errorf("Unsupported node type '%s' for node '%s' in deployment '%s'", nodeType, nodeParams.nodeName, nodeParams.deploymentID)
}
Expand Down
Loading