mirror of https://github.com/kubernetes/kops.git
Merge pull request #1877 from justinsb/cloudformation
Initial support for cloudformation output
This commit is contained in:
commit
540947cbc4
|
|
@ -38,6 +38,8 @@ type MockEC2 struct {
|
|||
volumeNumber int
|
||||
Volumes []*ec2.Volume
|
||||
|
||||
KeyPairs []*ec2.KeyPairInfo
|
||||
|
||||
Tags []*ec2.TagDescription
|
||||
|
||||
vpcNumber int
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License 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 mockec2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
func (m *MockEC2) DescribeKeyPairsRequest(*ec2.DescribeKeyPairsInput) (*request.Request, *ec2.DescribeKeyPairsOutput) {
|
||||
panic("MockEC2 DescribeKeyPairsRequest not implemented")
|
||||
}
|
||||
func (m *MockEC2) ImportKeyPairRequest(*ec2.ImportKeyPairInput) (*request.Request, *ec2.ImportKeyPairOutput) {
|
||||
panic("MockEC2 ImportKeyPairRequest not implemented")
|
||||
}
|
||||
func (m *MockEC2) ImportKeyPair(request *ec2.ImportKeyPairInput) (*ec2.ImportKeyPairOutput, error) {
|
||||
glog.Infof("ImportKeyPair: %v", request)
|
||||
|
||||
fp := "12345" // TODO: calculate fingerprint
|
||||
|
||||
kp := &ec2.KeyPairInfo{
|
||||
KeyFingerprint: aws.String(fp),
|
||||
KeyName: request.KeyName,
|
||||
}
|
||||
m.KeyPairs = append(m.KeyPairs, kp)
|
||||
response := &ec2.ImportKeyPairOutput{
|
||||
KeyFingerprint: kp.KeyFingerprint,
|
||||
KeyName: kp.KeyName,
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
func (m *MockEC2) CreateKeyPairRequest(*ec2.CreateKeyPairInput) (*request.Request, *ec2.CreateKeyPairOutput) {
|
||||
panic("MockEC2 CreateKeyPairRequest not implemented")
|
||||
}
|
||||
func (m *MockEC2) CreateKeyPair(*ec2.CreateKeyPairInput) (*ec2.CreateKeyPairOutput, error) {
|
||||
panic("MockEC2 CreateKeyPair not implemented")
|
||||
}
|
||||
|
||||
func (m *MockEC2) DescribeKeyPairs(request *ec2.DescribeKeyPairsInput) (*ec2.DescribeKeyPairsOutput, error) {
|
||||
glog.Infof("DescribeKeyPairs: %v", request)
|
||||
|
||||
var keypairs []*ec2.KeyPairInfo
|
||||
|
||||
for _, keypair := range m.KeyPairs {
|
||||
allFiltersMatch := true
|
||||
|
||||
if len(request.KeyNames) != 0 {
|
||||
match := false
|
||||
for _, keyname := range request.KeyNames {
|
||||
if aws.StringValue(keyname) == aws.StringValue(keypair.KeyName) {
|
||||
match = true
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
allFiltersMatch = false
|
||||
}
|
||||
}
|
||||
for _, filter := range request.Filters {
|
||||
match := false
|
||||
switch *filter.Name {
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown filter name: %q", *filter.Name)
|
||||
}
|
||||
|
||||
if !match {
|
||||
allFiltersMatch = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !allFiltersMatch {
|
||||
continue
|
||||
}
|
||||
|
||||
copy := *keypair
|
||||
keypairs = append(keypairs, ©)
|
||||
}
|
||||
|
||||
response := &ec2.DescribeKeyPairsOutput{
|
||||
KeyPairs: keypairs,
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
|
@ -234,14 +234,6 @@ func (m *MockEC2) CreateInternetGateway(*ec2.CreateInternetGatewayInput) (*ec2.C
|
|||
panic("Not implemented")
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockEC2) CreateKeyPairRequest(*ec2.CreateKeyPairInput) (*request.Request, *ec2.CreateKeyPairOutput) {
|
||||
panic("Not implemented")
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockEC2) CreateKeyPair(*ec2.CreateKeyPairInput) (*ec2.CreateKeyPairOutput, error) {
|
||||
panic("Not implemented")
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockEC2) CreateNatGatewayRequest(*ec2.CreateNatGatewayInput) (*request.Request, *ec2.CreateNatGatewayOutput) {
|
||||
panic("Not implemented")
|
||||
return nil, nil
|
||||
|
|
@ -706,14 +698,6 @@ func (m *MockEC2) DescribeInternetGateways(*ec2.DescribeInternetGatewaysInput) (
|
|||
panic("Not implemented")
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockEC2) DescribeKeyPairsRequest(*ec2.DescribeKeyPairsInput) (*request.Request, *ec2.DescribeKeyPairsOutput) {
|
||||
panic("Not implemented")
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockEC2) DescribeKeyPairs(*ec2.DescribeKeyPairsInput) (*ec2.DescribeKeyPairsOutput, error) {
|
||||
panic("Not implemented")
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockEC2) DescribeMovingAddressesRequest(*ec2.DescribeMovingAddressesInput) (*request.Request, *ec2.DescribeMovingAddressesOutput) {
|
||||
panic("Not implemented")
|
||||
return nil, nil
|
||||
|
|
@ -1118,14 +1102,6 @@ func (m *MockEC2) ImportInstance(*ec2.ImportInstanceInput) (*ec2.ImportInstanceO
|
|||
panic("Not implemented")
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockEC2) ImportKeyPairRequest(*ec2.ImportKeyPairInput) (*request.Request, *ec2.ImportKeyPairOutput) {
|
||||
panic("Not implemented")
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockEC2) ImportKeyPair(*ec2.ImportKeyPairInput) (*ec2.ImportKeyPairOutput, error) {
|
||||
panic("Not implemented")
|
||||
return nil, nil
|
||||
}
|
||||
func (m *MockEC2) ImportSnapshotRequest(*ec2.ImportSnapshotInput) (*request.Request, *ec2.ImportSnapshotOutput) {
|
||||
panic("Not implemented")
|
||||
return nil, nil
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ func NewCmdCreateCluster(f *util.Factory, out io.Writer) *cobra.Command {
|
|||
}
|
||||
|
||||
cmd.Flags().BoolVar(&options.Yes, "yes", options.Yes, "Specify --yes to immediately create the cluster")
|
||||
cmd.Flags().StringVar(&options.Target, "target", options.Target, "Target - direct, terraform")
|
||||
cmd.Flags().StringVar(&options.Target, "target", options.Target, "Target - direct, terraform, cloudformation")
|
||||
cmd.Flags().StringVar(&options.Models, "model", options.Models, "Models to apply (separate multiple models with commas)")
|
||||
|
||||
cmd.Flags().StringVar(&options.Cloud, "cloud", options.Cloud, "Cloud provider to use - gce, aws")
|
||||
|
|
@ -201,6 +201,8 @@ func RunCreateCluster(f *util.Factory, out io.Writer, c *CreateClusterOptions) e
|
|||
if c.OutDir == "" {
|
||||
if c.Target == cloudup.TargetTerraform {
|
||||
c.OutDir = "out/terraform"
|
||||
} else if c.Target == cloudup.TargetCloudformation {
|
||||
c.OutDir = "out/cloudformation"
|
||||
} else {
|
||||
c.OutDir = "out"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,13 @@ func TestMinimal(t *testing.T) {
|
|||
runTest(t, "minimal.example.com", "../../tests/integration/minimal", "v1alpha2", false)
|
||||
}
|
||||
|
||||
// TestMinimalCloudformation runs the test on a minimum configuration, similar to kops create cluster minimal.example.com --zones us-west-1a
|
||||
func TestMinimalCloudformation(t *testing.T) {
|
||||
//runTestCloudformation(t, "minimal.example.com", "../../tests/integration/minimal", "v1alpha0", false)
|
||||
//runTestCloudformation(t, "minimal.example.com", "../../tests/integration/minimal", "v1alpha1", false)
|
||||
runTestCloudformation(t, "minimal.example.com", "../../tests/integration/minimal", "v1alpha2", false)
|
||||
}
|
||||
|
||||
// TestMinimal_141 runs the test on a configuration from 1.4.1 release
|
||||
func TestMinimal_141(t *testing.T) {
|
||||
runTest(t, "minimal-141.example.com", "../../tests/integration/minimal-141", "v1alpha0", false)
|
||||
|
|
@ -209,6 +216,97 @@ func runTest(t *testing.T, clusterName string, srcDir string, version string, pr
|
|||
}
|
||||
}
|
||||
|
||||
func runTestCloudformation(t *testing.T, clusterName string, srcDir string, version string, private bool) {
|
||||
var stdout bytes.Buffer
|
||||
|
||||
inputYAML := "in-" + version + ".yaml"
|
||||
expectedCfPath := "cloudformation.json"
|
||||
|
||||
factoryOptions := &util.FactoryOptions{}
|
||||
factoryOptions.RegistryPath = "memfs://tests"
|
||||
|
||||
h := NewIntegrationTestHarness(t)
|
||||
defer h.Close()
|
||||
|
||||
h.SetupMockAWS()
|
||||
|
||||
factory := util.NewFactory(factoryOptions)
|
||||
|
||||
{
|
||||
options := &CreateOptions{}
|
||||
options.Filenames = []string{path.Join(srcDir, inputYAML)}
|
||||
|
||||
err := RunCreate(factory, &stdout, options)
|
||||
if err != nil {
|
||||
t.Fatalf("error running %q create: %v", inputYAML, err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
options := &CreateSecretPublickeyOptions{}
|
||||
options.ClusterName = clusterName
|
||||
options.Name = "admin"
|
||||
options.PublicKeyPath = path.Join(srcDir, "id_rsa.pub")
|
||||
|
||||
err := RunCreateSecretPublicKey(factory, &stdout, options)
|
||||
if err != nil {
|
||||
t.Fatalf("error running %q create: %v", inputYAML, err)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
options := &UpdateClusterOptions{}
|
||||
options.InitDefaults()
|
||||
options.Target = "cloudformation"
|
||||
options.OutDir = path.Join(h.TempDir, "out")
|
||||
options.MaxTaskDuration = 30 * time.Second
|
||||
|
||||
// We don't test it here, and it adds a dependency on kubectl
|
||||
options.CreateKubecfg = false
|
||||
|
||||
err := RunUpdateCluster(factory, clusterName, &stdout, options)
|
||||
if err != nil {
|
||||
t.Fatalf("error running update cluster %q: %v", clusterName, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Compare main files
|
||||
{
|
||||
files, err := ioutil.ReadDir(path.Join(h.TempDir, "out"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read dir: %v", err)
|
||||
}
|
||||
|
||||
var fileNames []string
|
||||
for _, f := range files {
|
||||
fileNames = append(fileNames, f.Name())
|
||||
}
|
||||
sort.Strings(fileNames)
|
||||
|
||||
actualFilenames := strings.Join(fileNames, ",")
|
||||
expectedFilenames := "kubernetes.json"
|
||||
if actualFilenames != expectedFilenames {
|
||||
t.Fatalf("unexpected files. actual=%q, expected=%q", actualFilenames, expectedFilenames)
|
||||
}
|
||||
|
||||
actualCF, err := ioutil.ReadFile(path.Join(h.TempDir, "out", "kubernetes.json"))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error reading actual cloudformation output: %v", err)
|
||||
}
|
||||
expectedCF, err := ioutil.ReadFile(path.Join(srcDir, expectedCfPath))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error reading expected cloudformation output: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(actualCF, expectedCF) {
|
||||
diffString := diff.FormatDiff(string(expectedCF), string(actualCF))
|
||||
t.Logf("diff:\n%s\n", diffString)
|
||||
|
||||
t.Fatalf("cloudformation output differed from expected")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type IntegrationTestHarness struct {
|
||||
TempDir string
|
||||
T *testing.T
|
||||
|
|
@ -256,6 +354,8 @@ func (h *IntegrationTestHarness) SetupMockAWS() {
|
|||
ImageId: aws.String("ami-12345678"),
|
||||
Name: aws.String("k8s-1.4-debian-jessie-amd64-hvm-ebs-2016-10-21"),
|
||||
OwnerId: aws.String(awsup.WellKnownAccountKopeio),
|
||||
|
||||
RootDeviceName: aws.String("/dev/xvda"),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -107,6 +108,8 @@ func RunUpdateCluster(f *util.Factory, clusterName string, out io.Writer, c *Upd
|
|||
if c.OutDir == "" {
|
||||
if c.Target == cloudup.TargetTerraform {
|
||||
c.OutDir = "out/terraform"
|
||||
} else if c.Target == cloudup.TargetCloudformation {
|
||||
c.OutDir = "out/cloudformation"
|
||||
} else {
|
||||
c.OutDir = "out"
|
||||
}
|
||||
|
|
@ -233,6 +236,17 @@ func RunUpdateCluster(f *util.Factory, clusterName string, out io.Writer, c *Upd
|
|||
fmt.Fprintf(sb, " terraform apply\n")
|
||||
fmt.Fprintf(sb, "\n")
|
||||
}
|
||||
} else if c.Target == cloudup.TargetCloudformation {
|
||||
fmt.Fprintf(sb, "\n")
|
||||
fmt.Fprintf(sb, "Cloudformation output has been placed into %s\n", c.OutDir)
|
||||
|
||||
if firstRun {
|
||||
cfName := "kubernetes-" + strings.Replace(clusterName, ".", "-", -1)
|
||||
cfPath := filepath.Join(c.OutDir, "kubernetes.json")
|
||||
fmt.Fprintf(sb, "Run this command to apply the configuration:\n")
|
||||
fmt.Fprintf(sb, " aws cloudformation create-stack --capabilities CAPABILITY_NAMED_IAM --stack-name %s --template-body file://%s\n", cfName, cfPath)
|
||||
fmt.Fprintf(sb, "\n")
|
||||
}
|
||||
} else if firstRun {
|
||||
fmt.Fprintf(sb, "\n")
|
||||
fmt.Fprintf(sb, "Cluster is starting. It should be ready in a few minutes.\n")
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ k8s.io/kops/upup/pkg/fi
|
|||
k8s.io/kops/upup/pkg/fi/cloudup
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/awstasks
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/awsup
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/cloudformation
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/gce
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/gcetasks
|
||||
k8s.io/kops/upup/pkg/fi/cloudup/terraform
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -34,6 +34,7 @@ import (
|
|||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awstasks"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/gce"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/gcetasks"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
|
|
@ -514,6 +515,7 @@ func (c *ApplyClusterCmd) Run() error {
|
|||
|
||||
var target fi.Target
|
||||
dryRun := false
|
||||
shouldPrecreateDNS := true
|
||||
|
||||
switch c.TargetName {
|
||||
case TargetDirect:
|
||||
|
|
@ -531,9 +533,24 @@ func (c *ApplyClusterCmd) Run() error {
|
|||
outDir := c.OutDir
|
||||
target = terraform.NewTerraformTarget(cloud, region, project, outDir)
|
||||
|
||||
// Can cause conflicts with terraform management
|
||||
shouldPrecreateDNS = false
|
||||
|
||||
case TargetCloudformation:
|
||||
checkExisting = false
|
||||
outDir := c.OutDir
|
||||
target = cloudformation.NewCloudformationTarget(cloud, region, project, outDir)
|
||||
|
||||
// Can cause conflicts with cloudformation management
|
||||
shouldPrecreateDNS = false
|
||||
|
||||
case TargetDryRun:
|
||||
target = fi.NewDryRunTarget(os.Stdout)
|
||||
dryRun = true
|
||||
|
||||
// Avoid making changes on a dry-run
|
||||
shouldPrecreateDNS = false
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported target type %q", c.TargetName)
|
||||
}
|
||||
|
|
@ -564,7 +581,7 @@ func (c *ApplyClusterCmd) Run() error {
|
|||
return fmt.Errorf("error running tasks: %v", err)
|
||||
}
|
||||
|
||||
if !dryRun {
|
||||
if shouldPrecreateDNS {
|
||||
if err := precreateDNS(cluster, cloud); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
|
|
@ -295,3 +296,54 @@ func (_ *AutoscalingGroup) RenderTerraform(t *terraform.TerraformTarget, a, e, c
|
|||
func (e *AutoscalingGroup) TerraformLink() *terraform.Literal {
|
||||
return terraform.LiteralProperty("aws_autoscaling_group", *e.Name, "id")
|
||||
}
|
||||
|
||||
type cloudformationASGTag struct {
|
||||
Key *string `json:"Key"`
|
||||
Value *string `json:"Value"`
|
||||
PropagateAtLaunch *bool `json:"PropagateAtLaunch"`
|
||||
}
|
||||
type cloudformationAutoscalingGroup struct {
|
||||
//Name *string `json:"name,omitempty"`
|
||||
LaunchConfigurationName *cloudformation.Literal `json:"LaunchConfigurationName,omitempty"`
|
||||
MaxSize *int64 `json:"MaxSize,omitempty"`
|
||||
MinSize *int64 `json:"MinSize,omitempty"`
|
||||
VPCZoneIdentifier []*cloudformation.Literal `json:"VPCZoneIdentifier,omitempty"`
|
||||
Tags []*cloudformationASGTag `json:"Tags,omitempty"`
|
||||
|
||||
LoadBalancerNames []*cloudformation.Literal `json:"LoadBalancerNames,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *AutoscalingGroup) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *AutoscalingGroup) error {
|
||||
tf := &cloudformationAutoscalingGroup{
|
||||
//Name: e.Name,
|
||||
MinSize: e.MinSize,
|
||||
MaxSize: e.MaxSize,
|
||||
LaunchConfigurationName: e.LaunchConfiguration.CloudformationLink(),
|
||||
}
|
||||
|
||||
for _, s := range e.Subnets {
|
||||
tf.VPCZoneIdentifier = append(tf.VPCZoneIdentifier, s.CloudformationLink())
|
||||
}
|
||||
|
||||
tags := e.buildTags(t.Cloud)
|
||||
// Make sure we output in a stable order
|
||||
var tagKeys []string
|
||||
for k := range tags {
|
||||
tagKeys = append(tagKeys, k)
|
||||
}
|
||||
sort.Strings(tagKeys)
|
||||
for _, k := range tagKeys {
|
||||
v := tags[k]
|
||||
tf.Tags = append(tf.Tags, &cloudformationASGTag{
|
||||
Key: fi.String(k),
|
||||
Value: fi.String(v),
|
||||
PropagateAtLaunch: fi.Bool(true),
|
||||
})
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::AutoScaling::AutoScalingGroup", *e.Name, tf)
|
||||
}
|
||||
|
||||
func (e *AutoscalingGroup) CloudformationLink() *cloudformation.Literal {
|
||||
return cloudformation.Ref("AWS::AutoScaling::AutoScalingGroup", *e.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License 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 awstasks
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type cloudformationTag struct {
|
||||
Key *string `json:"Key"`
|
||||
Value *string `json:"Value"`
|
||||
}
|
||||
|
||||
type cfTagByKey []cloudformationTag
|
||||
|
||||
func (a cfTagByKey) Len() int { return len(a) }
|
||||
func (a cfTagByKey) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a cfTagByKey) Less(i, j int) bool {
|
||||
return aws.StringValue(a[i].Key) < aws.StringValue(a[j].Key)
|
||||
}
|
||||
|
||||
func buildCloudformationTags(tags map[string]string) []cloudformationTag {
|
||||
var cfTags []cloudformationTag
|
||||
for k, v := range tags {
|
||||
cfTag := cloudformationTag{
|
||||
Key: aws.String(k),
|
||||
Value: aws.String(v),
|
||||
}
|
||||
cfTags = append(cfTags, cfTag)
|
||||
}
|
||||
sort.Sort(cfTagByKey(cfTags))
|
||||
return cfTags
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
"strings"
|
||||
)
|
||||
|
|
@ -178,3 +179,28 @@ func (_ *DHCPOptions) RenderTerraform(t *terraform.TerraformTarget, a, e, change
|
|||
func (e *DHCPOptions) TerraformLink() *terraform.Literal {
|
||||
return terraform.LiteralProperty("aws_vpc_dhcp_options", *e.Name, "id")
|
||||
}
|
||||
|
||||
type cloudformationDHCPOptions struct {
|
||||
DomainName *string `json:"DomainName,omitempty"`
|
||||
DomainNameServers []string `json:"DomainNameServers,omitempty"`
|
||||
Tags []cloudformationTag `json:"Tags,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *DHCPOptions) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *DHCPOptions) error {
|
||||
cloud := t.Cloud.(awsup.AWSCloud)
|
||||
|
||||
cf := &cloudformationDHCPOptions{
|
||||
DomainName: e.DomainName,
|
||||
Tags: buildCloudformationTags(cloud.BuildTags(e.Name)),
|
||||
}
|
||||
|
||||
if e.DomainNameServers != nil {
|
||||
cf.DomainNameServers = strings.Split(*e.DomainNameServers, ",")
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::EC2::DHCPOptions", *e.Name, cf)
|
||||
}
|
||||
|
||||
func (e *DHCPOptions) CloudformationLink() *cloudformation.Literal {
|
||||
return cloudformation.Ref("AWS::EC2::DHCPOptions", *e.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
|
|
@ -210,3 +211,41 @@ func (_ *DNSName) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *D
|
|||
func (e *DNSName) TerraformLink() *terraform.Literal {
|
||||
return terraform.LiteralSelfLink("aws_route53_record", *e.Name)
|
||||
}
|
||||
|
||||
type cloudformationRoute53Record struct {
|
||||
Name *string `json:"Name"`
|
||||
Type *string `json:"Type"`
|
||||
TTL *string `json:"TTL,omitempty"`
|
||||
ResourceRecords []string `json:"ResourceRecords,omitempty"`
|
||||
|
||||
AliasTarget *cloudformationAlias `json:"AliasTarget,omitempty"`
|
||||
ZoneID *cloudformation.Literal `json:"HostedZoneId"`
|
||||
}
|
||||
|
||||
type cloudformationAlias struct {
|
||||
DNSName *cloudformation.Literal `json:"DNSName,omitempty"`
|
||||
ZoneID *cloudformation.Literal `json:"HostedZoneId,omitempty"`
|
||||
EvaluateTargetHealth *bool `json:"EvaluateTargetHealth,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *DNSName) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *DNSName) error {
|
||||
cf := &cloudformationRoute53Record{
|
||||
Name: e.Name,
|
||||
ZoneID: e.Zone.CloudformationLink(),
|
||||
Type: e.ResourceType,
|
||||
}
|
||||
|
||||
if e.TargetLoadBalancer != nil {
|
||||
cf.AliasTarget = &cloudformationAlias{
|
||||
DNSName: e.TargetLoadBalancer.CloudformationAttrDNSName(),
|
||||
EvaluateTargetHealth: aws.Bool(false),
|
||||
ZoneID: e.TargetLoadBalancer.CloudformationAttrCanonicalHostedZoneNameID(),
|
||||
}
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::Route53::RecordSet", *e.Name, cf)
|
||||
}
|
||||
|
||||
func (e *DNSName) CloudformationLink() *cloudformation.Literal {
|
||||
return cloudformation.Ref("AWS::Route53::RecordSet", *e.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
|
|
@ -280,3 +281,55 @@ func (e *DNSZone) TerraformLink() *terraform.Literal {
|
|||
|
||||
return terraform.LiteralSelfLink("aws_route53_zone", *e.Name)
|
||||
}
|
||||
|
||||
type cloudformationRoute53Zone struct {
|
||||
Name *string `json:"Name"`
|
||||
VPCs []*cloudformation.Literal `json:"VPCs,omitempty"`
|
||||
Tags []cloudformationTag `json:"HostedZoneTags,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *DNSZone) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *DNSZone) error {
|
||||
cloud := t.Cloud.(awsup.AWSCloud)
|
||||
|
||||
dnsName := fi.StringValue(e.DNSName)
|
||||
|
||||
// As a special case, we check for an existing zone
|
||||
// It is really painful to have TF create a new one...
|
||||
// (you have to reconfigure the DNS NS records)
|
||||
glog.Infof("Check for existing route53 zone to re-use with name %q", dnsName)
|
||||
z, err := e.findExisting(cloud)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if z != nil {
|
||||
glog.Infof("Existing zone %q found; will configure cloudformation to reuse", aws.StringValue(z.HostedZone.Name))
|
||||
|
||||
e.ZoneID = z.HostedZone.Id
|
||||
|
||||
// Don't render a task
|
||||
return nil
|
||||
}
|
||||
|
||||
if !fi.BoolValue(e.Private) {
|
||||
return fmt.Errorf("Creation of public Route53 hosted zones is not supported for cloudformation")
|
||||
}
|
||||
|
||||
// We will create private zones (and delete them)
|
||||
tf := &cloudformationRoute53Zone{
|
||||
Name: e.Name,
|
||||
VPCs: []*cloudformation.Literal{e.PrivateVPC.CloudformationLink()},
|
||||
Tags: buildCloudformationTags(cloud.BuildTags(e.Name)),
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::Route53::HostedZone", *e.Name, tf)
|
||||
}
|
||||
|
||||
func (e *DNSZone) CloudformationLink() *cloudformation.Literal {
|
||||
if e.ZoneID != nil {
|
||||
glog.V(4).Infof("reusing existing route53 zone with id %q", *e.ZoneID)
|
||||
return cloudformation.LiteralString(*e.ZoneID)
|
||||
}
|
||||
|
||||
return cloudformation.Ref("AWS::Route53::HostedZone", *e.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
|
|
@ -171,3 +172,29 @@ func (_ *EBSVolume) RenderTerraform(t *terraform.TerraformTarget, a, e, changes
|
|||
func (e *EBSVolume) TerraformLink() *terraform.Literal {
|
||||
return terraform.LiteralSelfLink("aws_ebs_volume", *e.Name)
|
||||
}
|
||||
|
||||
type cloudformationVolume struct {
|
||||
AvailabilityZone *string `json:"AvailabilityZone,omitempty"`
|
||||
Size *int64 `json:"Size,omitempty"`
|
||||
Type *string `json:"VolumeType,omitempty"`
|
||||
KmsKeyId *string `json:"KmsKeyId,omitempty"`
|
||||
Encrypted *bool `json:"Encrypted,omitempty"`
|
||||
Tags []cloudformationTag `json:"Tags,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *EBSVolume) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *EBSVolume) error {
|
||||
cf := &cloudformationVolume{
|
||||
AvailabilityZone: e.AvailabilityZone,
|
||||
Size: e.SizeGB,
|
||||
Type: e.VolumeType,
|
||||
KmsKeyId: e.KmsKeyId,
|
||||
Encrypted: e.Encrypted,
|
||||
Tags: buildCloudformationTags(e.Tags),
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::EC2::Volume", *e.Name, cf)
|
||||
}
|
||||
|
||||
func (e *EBSVolume) CloudformationLink() *cloudformation.Literal {
|
||||
return cloudformation.Ref("AWS::EC2::Volume", *e.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
|
|
@ -261,3 +262,24 @@ func (_ *ElasticIP) RenderTerraform(t *terraform.TerraformTarget, a, e, changes
|
|||
func (e *ElasticIP) TerraformLink() *terraform.Literal {
|
||||
return terraform.LiteralProperty("aws_eip", *e.Name, "id")
|
||||
}
|
||||
|
||||
type cloudformationElasticIP struct {
|
||||
Domain *string `json:"Domain"`
|
||||
}
|
||||
|
||||
func (_ *ElasticIP) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *ElasticIP) error {
|
||||
tf := &cloudformationElasticIP{
|
||||
Domain: aws.String("vpc"),
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::EC2::EIP", *e.Name, tf)
|
||||
}
|
||||
|
||||
// Removed because you normally want CloudformationAllocationID
|
||||
//func (e *ElasticIP) CloudformationLink() *cloudformation.Literal {
|
||||
// return cloudformation.Ref("AWS::EC2::EIP", *e.Name)
|
||||
//}
|
||||
|
||||
func (e *ElasticIP) CloudformationAllocationID() *cloudformation.Literal {
|
||||
return cloudformation.GetAtt("AWS::EC2::EIP", *e.Name, "AllocationId")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
|
|
@ -150,3 +151,12 @@ func (_ *IAMInstanceProfile) RenderTerraform(t *terraform.TerraformTarget, a, e,
|
|||
func (e *IAMInstanceProfile) TerraformLink() *terraform.Literal {
|
||||
return terraform.LiteralProperty("aws_iam_instance_profile", *e.Name, "id")
|
||||
}
|
||||
|
||||
func (_ *IAMInstanceProfile) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *IAMInstanceProfile) error {
|
||||
// Done on IAMInstanceProfileRole
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *IAMInstanceProfile) CloudformationLink() *cloudformation.Literal {
|
||||
return cloudformation.Ref("AWS::IAM::InstanceProfile", *e.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
|
|
@ -120,3 +121,17 @@ func (_ *IAMInstanceProfileRole) RenderTerraform(t *terraform.TerraformTarget, a
|
|||
|
||||
return t.RenderResource("aws_iam_instance_profile", *e.InstanceProfile.Name, tf)
|
||||
}
|
||||
|
||||
type cloudformationIAMInstanceProfile struct {
|
||||
//Path *string `json:"name"`
|
||||
Roles []*cloudformation.Literal `json:"Roles"`
|
||||
}
|
||||
|
||||
func (_ *IAMInstanceProfileRole) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *IAMInstanceProfileRole) error {
|
||||
cf := &cloudformationIAMInstanceProfile{
|
||||
//Path: e.InstanceProfile.Name,
|
||||
Roles: []*cloudformation.Literal{e.Role.CloudformationLink()},
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::IAM::InstanceProfile", *e.InstanceProfile.Name, cf)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
"k8s.io/kubernetes/pkg/util/diff"
|
||||
"net/url"
|
||||
|
|
@ -199,3 +200,32 @@ func (_ *IAMRole) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *I
|
|||
func (e *IAMRole) TerraformLink() *terraform.Literal {
|
||||
return terraform.LiteralProperty("aws_iam_role", *e.Name, "name")
|
||||
}
|
||||
|
||||
type cloudformationIAMRole struct {
|
||||
RoleName *string `json:"RoleName"`
|
||||
AssumeRolePolicyDocument map[string]interface{}
|
||||
}
|
||||
|
||||
func (_ *IAMRole) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *IAMRole) error {
|
||||
jsonString, err := e.RolePolicyDocument.AsBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
err = json.Unmarshal(jsonString, &data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing RolePolicyDocument: %v", err)
|
||||
}
|
||||
|
||||
cf := &cloudformationIAMRole{
|
||||
RoleName: e.Name,
|
||||
AssumeRolePolicyDocument: data,
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::IAM::Role", *e.Name, cf)
|
||||
}
|
||||
|
||||
func (e *IAMRole) CloudformationLink() *cloudformation.Literal {
|
||||
return cloudformation.Ref("AWS::IAM::Role", *e.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,14 @@ package awstasks
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"encoding/json"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
"k8s.io/kubernetes/pkg/util/diff"
|
||||
"net/url"
|
||||
|
|
@ -201,3 +203,48 @@ func (_ *IAMRolePolicy) RenderTerraform(t *terraform.TerraformTarget, a, e, chan
|
|||
func (e *IAMRolePolicy) TerraformLink() *terraform.Literal {
|
||||
return terraform.LiteralSelfLink("aws_iam_role_policy", *e.Name)
|
||||
}
|
||||
|
||||
type cloudformationIAMRolePolicy struct {
|
||||
PolicyName *string `json:"PolicyName"`
|
||||
Roles []*cloudformation.Literal `json:"Roles"`
|
||||
PolicyDocument map[string]interface{} `json:"PolicyDocument"`
|
||||
}
|
||||
|
||||
func (_ *IAMRolePolicy) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *IAMRolePolicy) error {
|
||||
{
|
||||
policyString, err := e.PolicyDocument.AsString()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error rendering PolicyDocument: %v", err)
|
||||
}
|
||||
if policyString == "" {
|
||||
// A deletion; we simply don't render; cloudformation will observe the removal
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
tf := &cloudformationIAMRolePolicy{
|
||||
PolicyName: e.Name,
|
||||
Roles: []*cloudformation.Literal{e.Role.CloudformationLink()},
|
||||
}
|
||||
|
||||
{
|
||||
jsonString, err := e.PolicyDocument.AsBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
err = json.Unmarshal(jsonString, &data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing PolicyDocument: %v", err)
|
||||
}
|
||||
|
||||
tf.PolicyDocument = data
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::IAM::Policy", *e.Name, tf)
|
||||
}
|
||||
|
||||
func (e *IAMRolePolicy) CloudformationLink() *cloudformation.Literal {
|
||||
return cloudformation.Ref("AWS::IAM::Policy", *e.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
|
|
@ -219,3 +220,81 @@ func (e *InternetGateway) TerraformLink() *terraform.Literal {
|
|||
|
||||
return terraform.LiteralProperty("aws_internet_gateway", *e.Name, "id")
|
||||
}
|
||||
|
||||
type cloudformationInternetGateway struct {
|
||||
Tags []cloudformationTag `json:"Tags,omitempty"`
|
||||
}
|
||||
|
||||
type cloudformationVpcGatewayAttachment struct {
|
||||
VpcId *cloudformation.Literal `json:"VpcId,omitempty"`
|
||||
InternetGatewayId *cloudformation.Literal `json:"InternetGatewayId,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *InternetGateway) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *InternetGateway) error {
|
||||
shared := fi.BoolValue(e.Shared)
|
||||
if shared {
|
||||
// Not cloudformation owned / managed
|
||||
|
||||
// But ... attempt to discover the ID so CloudformationLink works
|
||||
if e.ID == nil {
|
||||
request := &ec2.DescribeInternetGatewaysInput{}
|
||||
vpcID := fi.StringValue(e.VPC.ID)
|
||||
if vpcID == "" {
|
||||
return fmt.Errorf("VPC ID is required when InternetGateway is shared")
|
||||
}
|
||||
request.Filters = []*ec2.Filter{awsup.NewEC2Filter("attachment.vpc-id", vpcID)}
|
||||
igw, err := findInternetGateway(t.Cloud.(awsup.AWSCloud), request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if igw == nil {
|
||||
glog.Warningf("Cannot find internet gateway for VPC %q", vpcID)
|
||||
} else {
|
||||
e.ID = igw.InternetGatewayId
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
cloud := t.Cloud.(awsup.AWSCloud)
|
||||
|
||||
{
|
||||
cf := &cloudformationInternetGateway{
|
||||
Tags: buildCloudformationTags(cloud.BuildTags(e.Name)),
|
||||
}
|
||||
|
||||
err := t.RenderResource("AWS::EC2::InternetGateway", *e.Name, cf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
cf := &cloudformationVpcGatewayAttachment{
|
||||
VpcId: e.VPC.CloudformationLink(),
|
||||
InternetGatewayId: e.CloudformationLink(),
|
||||
}
|
||||
|
||||
err := t.RenderResource("AWS::EC2::VPCGatewayAttachment", *e.Name, cf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *InternetGateway) CloudformationLink() *cloudformation.Literal {
|
||||
shared := fi.BoolValue(e.Shared)
|
||||
if shared {
|
||||
if e.ID == nil {
|
||||
glog.Fatalf("ID must be set, if InternetGateway is shared: %s", e)
|
||||
}
|
||||
|
||||
glog.V(4).Infof("reusing existing InternetGateway with id %q", *e.ID)
|
||||
return cloudformation.LiteralString(*e.ID)
|
||||
}
|
||||
|
||||
return cloudformation.Ref("AWS::EC2::InternetGateway", *e.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
"time"
|
||||
)
|
||||
|
|
@ -428,3 +429,127 @@ func (_ *LaunchConfiguration) RenderTerraform(t *terraform.TerraformTarget, a, e
|
|||
func (e *LaunchConfiguration) TerraformLink() *terraform.Literal {
|
||||
return terraform.LiteralProperty("aws_launch_configuration", *e.Name, "id")
|
||||
}
|
||||
|
||||
type cloudformationLaunchConfiguration struct {
|
||||
AssociatePublicIpAddress *bool `json:"AssociatePublicIpAddress,omitempty"`
|
||||
BlockDeviceMappings []*cloudformationBlockDevice `json:"BlockDeviceMappings,omitempty"`
|
||||
IAMInstanceProfile *cloudformation.Literal `json:"IamInstanceProfile,omitempty"`
|
||||
ImageID *string `json:"ImageId,omitempty"`
|
||||
InstanceType *string `json:"InstanceType,omitempty"`
|
||||
KeyName *string `json:"KeyName,omitempty"`
|
||||
SecurityGroups []*cloudformation.Literal `json:"SecurityGroups,omitempty"`
|
||||
SpotPrice *string `json:"SpotPrice,omitempty"`
|
||||
UserData *string `json:"UserData,omitempty"`
|
||||
|
||||
//NamePrefix *string `json:"name_prefix,omitempty"`
|
||||
//Lifecycle *cloudformation.Lifecycle `json:"lifecycle,omitempty"`
|
||||
}
|
||||
|
||||
type cloudformationBlockDevice struct {
|
||||
// For ephemeral devices
|
||||
DeviceName *string `json:"DeviceName,omitempty"`
|
||||
VirtualName *string `json:"VirtualName,omitempty"`
|
||||
|
||||
// For root
|
||||
Ebs *cloudformationBlockDeviceEBS `json:"Ebs,omitempty"`
|
||||
}
|
||||
|
||||
type cloudformationBlockDeviceEBS struct {
|
||||
VolumeType *string `json:"VolumeType,omitempty"`
|
||||
VolumeSize *int64 `json:"VolumeSize,omitempty"`
|
||||
DeleteOnTermination *bool `json:"DeleteOnTermination,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *LaunchConfiguration) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *LaunchConfiguration) error {
|
||||
cloud := t.Cloud.(awsup.AWSCloud)
|
||||
|
||||
if e.ImageID == nil {
|
||||
return fi.RequiredField("ImageID")
|
||||
}
|
||||
image, err := cloud.ResolveImage(*e.ImageID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cf := &cloudformationLaunchConfiguration{
|
||||
//NamePrefix: fi.String(*e.Name + "-"),
|
||||
ImageID: image.ImageId,
|
||||
InstanceType: e.InstanceType,
|
||||
}
|
||||
|
||||
if e.SpotPrice != "" {
|
||||
cf.SpotPrice = aws.String(e.SpotPrice)
|
||||
}
|
||||
|
||||
if e.SSHKey != nil {
|
||||
if e.SSHKey.Name == nil {
|
||||
return fmt.Errorf("SSHKey Name not set")
|
||||
}
|
||||
cf.KeyName = e.SSHKey.Name
|
||||
}
|
||||
|
||||
for _, sg := range e.SecurityGroups {
|
||||
cf.SecurityGroups = append(cf.SecurityGroups, sg.CloudformationLink())
|
||||
}
|
||||
cf.AssociatePublicIpAddress = e.AssociatePublicIP
|
||||
|
||||
{
|
||||
rootDevices, err := e.buildRootDevice(cloud)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ephemeralDevices, err := buildEphemeralDevices(e.InstanceType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(rootDevices) != 0 {
|
||||
if len(rootDevices) != 1 {
|
||||
return fmt.Errorf("unexpectedly found multiple root devices")
|
||||
}
|
||||
|
||||
for deviceName, bdm := range rootDevices {
|
||||
d := &cloudformationBlockDevice{
|
||||
DeviceName: fi.String(deviceName),
|
||||
Ebs: &cloudformationBlockDeviceEBS{
|
||||
VolumeType: bdm.EbsVolumeType,
|
||||
VolumeSize: bdm.EbsVolumeSize,
|
||||
DeleteOnTermination: fi.Bool(true),
|
||||
},
|
||||
}
|
||||
cf.BlockDeviceMappings = append(cf.BlockDeviceMappings, d)
|
||||
}
|
||||
}
|
||||
|
||||
if len(ephemeralDevices) != 0 {
|
||||
for deviceName, bdm := range ephemeralDevices {
|
||||
cf.BlockDeviceMappings = append(cf.BlockDeviceMappings, &cloudformationBlockDevice{
|
||||
VirtualName: bdm.VirtualName,
|
||||
DeviceName: fi.String(deviceName),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if e.UserData != nil {
|
||||
d, err := e.UserData.AsBytes()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error rendering AutoScalingLaunchConfiguration UserData: %v", err)
|
||||
}
|
||||
cf.UserData = aws.String(base64.StdEncoding.EncodeToString(d))
|
||||
}
|
||||
|
||||
if e.IAMInstanceProfile != nil {
|
||||
cf.IAMInstanceProfile = e.IAMInstanceProfile.CloudformationLink()
|
||||
}
|
||||
|
||||
// So that we can update configurations
|
||||
//tf.Lifecycle = &cloudformation.Lifecycle{CreateBeforeDestroy: fi.Bool(true)}
|
||||
|
||||
return t.RenderResource("AWS::AutoScaling::LaunchConfiguration", *e.Name, cf)
|
||||
}
|
||||
|
||||
func (e *LaunchConfiguration) CloudformationLink() *cloudformation.Literal {
|
||||
return cloudformation.Ref("AWS::AutoScaling::LaunchConfiguration", *e.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
"sort"
|
||||
)
|
||||
|
|
@ -586,3 +587,137 @@ func (e *LoadBalancer) TerraformLink(params ...string) *terraform.Literal {
|
|||
}
|
||||
return terraform.LiteralProperty("aws_elb", *e.Name, prop)
|
||||
}
|
||||
|
||||
type cloudformationLoadBalancer struct {
|
||||
Name *string `json:"LoadBalancerName,omitempty"`
|
||||
Listener []*cloudformationLoadBalancerListener `json:"Listeners,omitempty"`
|
||||
SecurityGroups []*cloudformation.Literal `json:"SecurityGroups,omitempty"`
|
||||
Subnets []*cloudformation.Literal `json:"Subnets,omitempty"`
|
||||
Scheme *string `json:"Scheme,omitempty"`
|
||||
|
||||
HealthCheck *cloudformationLoadBalancerHealthCheck `json:"HealthCheck,omitempty"`
|
||||
AccessLog *cloudformationLoadBalancerAccessLog `json:"AccessLoggingPolicy,omitempty"`
|
||||
|
||||
ConnectionDrainingPolicy *cloudformationConnectionDrainingPolicy `json:"ConnectionDrainingPolicy,omitempty"`
|
||||
ConnectionSettings *cloudformationConnectionSettings `json:"ConnectionSettings,omitempty"`
|
||||
|
||||
CrossZoneLoadBalancing *bool `json:"CrossZone,omitempty"`
|
||||
|
||||
Tags []cloudformationTag `json:"Tags,omitempty"`
|
||||
}
|
||||
|
||||
type cloudformationLoadBalancerListener struct {
|
||||
InstancePort int `json:"InstancePort"`
|
||||
InstanceProtocol string `json:"InstanceProtocol"`
|
||||
LoadBalancerPort int64 `json:"LoadBalancerPort"`
|
||||
LoadBalancerProtocol string `json:"Protocol"`
|
||||
}
|
||||
|
||||
type cloudformationLoadBalancerHealthCheck struct {
|
||||
Target *string `json:"Target"`
|
||||
HealthyThreshold *int64 `json:"HealthyThreshold"`
|
||||
UnhealthyThreshold *int64 `json:"UnhealthyThreshold"`
|
||||
Interval *int64 `json:"Interval"`
|
||||
Timeout *int64 `json:"Timeout"`
|
||||
}
|
||||
|
||||
type cloudformationConnectionDrainingPolicy struct {
|
||||
Enabled *bool `json:"Enabled,omitempty"`
|
||||
Timeout *int64 `json:"Timeout,omitempty"`
|
||||
}
|
||||
|
||||
type cloudformationConnectionSettings struct {
|
||||
IdleTimeout *int64 `json:"IdleTimeout,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *LoadBalancer) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *LoadBalancer) error {
|
||||
// TODO: From http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-elb.html:
|
||||
// If this resource has a public IP address and is also in a VPC that is defined in the same template,
|
||||
// you must use the DependsOn attribute to declare a dependency on the VPC-gateway attachment.
|
||||
|
||||
cloud := t.Cloud.(awsup.AWSCloud)
|
||||
|
||||
elbName := e.ID
|
||||
if elbName == nil {
|
||||
elbName = e.Name
|
||||
}
|
||||
|
||||
tf := &cloudformationLoadBalancer{
|
||||
Name: elbName,
|
||||
Scheme: e.Scheme,
|
||||
}
|
||||
|
||||
for _, subnet := range e.Subnets {
|
||||
tf.Subnets = append(tf.Subnets, subnet.CloudformationLink())
|
||||
}
|
||||
|
||||
for _, sg := range e.SecurityGroups {
|
||||
tf.SecurityGroups = append(tf.SecurityGroups, sg.CloudformationLink())
|
||||
}
|
||||
|
||||
for loadBalancerPort, listener := range e.Listeners {
|
||||
loadBalancerPortInt, err := strconv.ParseInt(loadBalancerPort, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing load balancer listener port: %q", loadBalancerPort)
|
||||
}
|
||||
|
||||
tf.Listener = append(tf.Listener, &cloudformationLoadBalancerListener{
|
||||
InstanceProtocol: "TCP",
|
||||
InstancePort: listener.InstancePort,
|
||||
LoadBalancerPort: loadBalancerPortInt,
|
||||
LoadBalancerProtocol: "TCP",
|
||||
})
|
||||
}
|
||||
|
||||
if e.HealthCheck != nil {
|
||||
tf.HealthCheck = &cloudformationLoadBalancerHealthCheck{
|
||||
Target: e.HealthCheck.Target,
|
||||
HealthyThreshold: e.HealthCheck.HealthyThreshold,
|
||||
UnhealthyThreshold: e.HealthCheck.UnhealthyThreshold,
|
||||
Interval: e.HealthCheck.Interval,
|
||||
Timeout: e.HealthCheck.Timeout,
|
||||
}
|
||||
}
|
||||
|
||||
if e.AccessLog != nil {
|
||||
tf.AccessLog = &cloudformationLoadBalancerAccessLog{
|
||||
EmitInterval: e.AccessLog.EmitInterval,
|
||||
Enabled: e.AccessLog.Enabled,
|
||||
S3BucketName: e.AccessLog.S3BucketName,
|
||||
S3BucketPrefix: e.AccessLog.S3BucketPrefix,
|
||||
}
|
||||
}
|
||||
|
||||
if e.ConnectionDraining != nil {
|
||||
tf.ConnectionDrainingPolicy = &cloudformationConnectionDrainingPolicy{
|
||||
Enabled: e.ConnectionDraining.Enabled,
|
||||
Timeout: e.ConnectionDraining.Timeout,
|
||||
}
|
||||
}
|
||||
|
||||
if e.ConnectionSettings != nil {
|
||||
tf.ConnectionSettings = &cloudformationConnectionSettings{
|
||||
IdleTimeout: e.ConnectionSettings.IdleTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
if e.CrossZoneLoadBalancing != nil {
|
||||
tf.CrossZoneLoadBalancing = e.CrossZoneLoadBalancing.Enabled
|
||||
}
|
||||
|
||||
tf.Tags = buildCloudformationTags(cloud.BuildTags(e.Name))
|
||||
|
||||
return t.RenderResource("AWS::ElasticLoadBalancing::LoadBalancer", *e.Name, tf)
|
||||
}
|
||||
|
||||
func (e *LoadBalancer) CloudformationLink() *cloudformation.Literal {
|
||||
return cloudformation.Ref("AWS::ElasticLoadBalancing::LoadBalancer", *e.Name)
|
||||
}
|
||||
|
||||
func (e *LoadBalancer) CloudformationAttrCanonicalHostedZoneNameID() *cloudformation.Literal {
|
||||
return cloudformation.GetAtt("AWS::ElasticLoadBalancing::LoadBalancer", *e.Name, "CanonicalHostedZoneNameID")
|
||||
}
|
||||
|
||||
func (e *LoadBalancer) CloudformationAttrDNSName() *cloudformation.Literal {
|
||||
return cloudformation.GetAtt("AWS::ElasticLoadBalancing::LoadBalancer", *e.Name, "DNSName")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
|
|
@ -160,3 +161,23 @@ func (e *LoadBalancerAttachment) TerraformLink() *terraform.Literal {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *LoadBalancerAttachment) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *LoadBalancerAttachment) error {
|
||||
if e.AutoscalingGroup != nil {
|
||||
cfObj, ok := t.Find(e.AutoscalingGroup.CloudformationLink())
|
||||
if !ok {
|
||||
// topo-sort fail?
|
||||
return fmt.Errorf("AutoScalingGroup not yet rendered")
|
||||
}
|
||||
cf, ok := cfObj.(*cloudformationAutoscalingGroup)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type for CF record: %T", cfObj)
|
||||
}
|
||||
|
||||
cf.LoadBalancerNames = append(cf.LoadBalancerNames, e.LoadBalancer.CloudformationLink())
|
||||
}
|
||||
if e.Instance != nil {
|
||||
return fmt.Errorf("expected Instance to be nil")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,10 +38,17 @@ func (_ *LoadBalancerAccessLog) GetDependencies(tasks map[string]fi.Task) []fi.T
|
|||
}
|
||||
|
||||
type terraformLoadBalancerAccessLog struct {
|
||||
EmitInterval *int64 `json:"internal,omitempty"`
|
||||
EmitInterval *int64 `json:"interval,omitempty"`
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
S3BucketName *string `json:"bucket,omitempty"`
|
||||
S3BucketPrefix *string `json:"bucekt_prefix,omitempty"`
|
||||
S3BucketPrefix *string `json:"bucket_prefix,omitempty"`
|
||||
}
|
||||
|
||||
type cloudformationLoadBalancerAccessLog struct {
|
||||
EmitInterval *int64 `json:"EmitInterval,omitempty"`
|
||||
Enabled *bool `json:"Enabled,omitempty"`
|
||||
S3BucketName *string `json:"S3BucketName,omitempty"`
|
||||
S3BucketPrefix *string `json:"S3BucketPrefix,omitempty"`
|
||||
}
|
||||
|
||||
//type LoadBalancerAdditionalAttribute struct {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
|
|
@ -340,3 +341,21 @@ func (_ *NatGateway) RenderTerraform(t *terraform.TerraformTarget, a, e, changes
|
|||
func (e *NatGateway) TerraformLink() *terraform.Literal {
|
||||
return terraform.LiteralProperty("aws_nat_gateway", *e.Name, "id")
|
||||
}
|
||||
|
||||
type cloudformationNATGateway struct {
|
||||
AllocationID *cloudformation.Literal `json:"AllocationId,omitempty"`
|
||||
SubnetID *cloudformation.Literal `json:"SubnetId,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *NatGateway) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *NatGateway) error {
|
||||
tf := &cloudformationNATGateway{
|
||||
AllocationID: e.ElasticIP.CloudformationAllocationID(),
|
||||
SubnetID: e.Subnet.CloudformationLink(),
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::EC2::NatGateway", *e.Name, tf)
|
||||
}
|
||||
|
||||
func (e *NatGateway) CloudformationLink() *cloudformation.Literal {
|
||||
return cloudformation.Ref("AWS::EC2::NatGateway", *e.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
|
|
@ -221,7 +222,6 @@ type terraformRoute struct {
|
|||
InternetGatewayID *terraform.Literal `json:"gateway_id,omitempty"`
|
||||
NATGatewayID *terraform.Literal `json:"nat_gateway_id,omitempty"`
|
||||
InstanceID *terraform.Literal `json:"instance_id,omitempty"`
|
||||
// TODO Kris - Add terraform support for NAT Gateway routes
|
||||
}
|
||||
|
||||
func (_ *Route) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *Route) error {
|
||||
|
|
@ -244,3 +244,33 @@ func (_ *Route) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *Rou
|
|||
|
||||
return t.RenderResource("aws_route", *e.Name, tf)
|
||||
}
|
||||
|
||||
type cloudformationRoute struct {
|
||||
RouteTableID *cloudformation.Literal `json:"RouteTableId"`
|
||||
CIDR *string `json:"DestinationCidrBlock,omitempty"`
|
||||
InternetGatewayID *cloudformation.Literal `json:"GatewayId,omitempty"`
|
||||
NATGatewayID *cloudformation.Literal `json:"NatGatewayId,omitempty"`
|
||||
InstanceID *cloudformation.Literal `json:"InstanceId,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *Route) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *Route) error {
|
||||
tf := &cloudformationRoute{
|
||||
CIDR: e.CIDR,
|
||||
RouteTableID: e.RouteTable.CloudformationLink(),
|
||||
}
|
||||
|
||||
if e.InternetGateway == nil && e.NatGateway == nil {
|
||||
return fmt.Errorf("missing target for route")
|
||||
} else if e.InternetGateway != nil {
|
||||
tf.InternetGatewayID = e.InternetGateway.CloudformationLink()
|
||||
} else if e.NatGateway != nil {
|
||||
tf.NATGatewayID = e.NatGateway.CloudformationLink()
|
||||
}
|
||||
|
||||
if e.Instance != nil {
|
||||
return fmt.Errorf("instance cloudformation routes not yet implemented")
|
||||
//tf.InstanceID = e.Instance.CloudformationLink()
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::EC2::Route", *e.Name, tf)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
|
|
@ -147,3 +148,23 @@ func (_ *RouteTable) RenderTerraform(t *terraform.TerraformTarget, a, e, changes
|
|||
func (e *RouteTable) TerraformLink() *terraform.Literal {
|
||||
return terraform.LiteralProperty("aws_route_table", *e.Name, "id")
|
||||
}
|
||||
|
||||
type cloudformationRouteTable struct {
|
||||
VPCID *cloudformation.Literal `json:"VpcId,omitempty"`
|
||||
Tags []cloudformationTag `json:"Tags,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *RouteTable) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *RouteTable) error {
|
||||
cloud := t.Cloud.(awsup.AWSCloud)
|
||||
|
||||
cf := &cloudformationRouteTable{
|
||||
VPCID: e.VPC.CloudformationLink(),
|
||||
Tags: buildCloudformationTags(cloud.BuildTags(e.Name)),
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::EC2::RouteTable", *e.Name, cf)
|
||||
}
|
||||
|
||||
func (e *RouteTable) CloudformationLink() *cloudformation.Literal {
|
||||
return cloudformation.Ref("AWS::EC2::RouteTable", *e.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
|
|
@ -196,3 +197,21 @@ func (_ *RouteTableAssociation) RenderTerraform(t *terraform.TerraformTarget, a,
|
|||
func (e *RouteTableAssociation) TerraformLink() *terraform.Literal {
|
||||
return terraform.LiteralSelfLink("aws_route_table_association", *e.Name)
|
||||
}
|
||||
|
||||
type cloudformationRouteTableAssociation struct {
|
||||
SubnetID *cloudformation.Literal `json:"SubnetId,omitempty"`
|
||||
RouteTableID *cloudformation.Literal `json:"RouteTableId,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *RouteTableAssociation) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *RouteTableAssociation) error {
|
||||
cf := &cloudformationRouteTableAssociation{
|
||||
SubnetID: e.Subnet.CloudformationLink(),
|
||||
RouteTableID: e.RouteTable.CloudformationLink(),
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::EC2::SubnetRouteTableAssociation", *e.Name, cf)
|
||||
}
|
||||
|
||||
func (e *RouteTableAssociation) CloudformationLink() *cloudformation.Literal {
|
||||
return cloudformation.Ref("AWS::EC2::SubnetRouteTableAssociation", *e.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -182,6 +183,30 @@ func (e *SecurityGroup) TerraformLink() *terraform.Literal {
|
|||
return terraform.LiteralProperty("aws_security_group", *e.Name, "id")
|
||||
}
|
||||
|
||||
type cloudformationSecurityGroup struct {
|
||||
//Name *string `json:"name"`
|
||||
VpcId *cloudformation.Literal `json:"VpcId"`
|
||||
Description *string `json:"GroupDescription"`
|
||||
Tags []cloudformationTag `json:"Tags,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *SecurityGroup) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *SecurityGroup) error {
|
||||
cloud := t.Cloud.(awsup.AWSCloud)
|
||||
|
||||
tf := &cloudformationSecurityGroup{
|
||||
//Name: e.Name,
|
||||
VpcId: e.VPC.CloudformationLink(),
|
||||
Description: e.Description,
|
||||
Tags: buildCloudformationTags(cloud.BuildTags(e.Name)),
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::EC2::SecurityGroup", *e.Name, tf)
|
||||
}
|
||||
|
||||
func (e *SecurityGroup) CloudformationLink() *cloudformation.Literal {
|
||||
return cloudformation.Ref("AWS::EC2::SecurityGroup", *e.Name)
|
||||
}
|
||||
|
||||
type deleteSecurityGroupRule struct {
|
||||
groupID *string
|
||||
permission *ec2.IpPermission
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
"k8s.io/kubernetes/pkg/util/validation/field"
|
||||
"strings"
|
||||
|
|
@ -331,3 +332,53 @@ func (_ *SecurityGroupRule) RenderTerraform(t *terraform.TerraformTarget, a, e,
|
|||
}
|
||||
return t.RenderResource("aws_security_group_rule", *e.Name, tf)
|
||||
}
|
||||
|
||||
type cloudformationSecurityGroupIngress struct {
|
||||
SecurityGroup *cloudformation.Literal `json:"GroupId,omitempty"`
|
||||
SourceGroup *cloudformation.Literal `json:"SourceSecurityGroupId,omitempty"`
|
||||
|
||||
FromPort *int64 `json:"FromPort,omitempty"`
|
||||
ToPort *int64 `json:"ToPort,omitempty"`
|
||||
|
||||
Protocol *string `json:"IpProtocol,omitempty"`
|
||||
CidrIp *string `json:"CidrIp,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *SecurityGroupRule) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *SecurityGroupRule) error {
|
||||
cfType := "AWS::EC2::SecurityGroupIngress"
|
||||
if fi.BoolValue(e.Egress) {
|
||||
cfType = "AWS::EC2::SecurityGroupEgress"
|
||||
}
|
||||
|
||||
tf := &cloudformationSecurityGroupIngress{
|
||||
SecurityGroup: e.SecurityGroup.CloudformationLink(),
|
||||
FromPort: e.FromPort,
|
||||
ToPort: e.ToPort,
|
||||
Protocol: e.Protocol,
|
||||
}
|
||||
|
||||
if e.Protocol == nil {
|
||||
tf.Protocol = fi.String("-1")
|
||||
tf.FromPort = fi.Int64(0)
|
||||
tf.ToPort = fi.Int64(0)
|
||||
}
|
||||
|
||||
if tf.FromPort == nil {
|
||||
// FromPort is required by tf
|
||||
tf.FromPort = fi.Int64(0)
|
||||
}
|
||||
if tf.ToPort == nil {
|
||||
// ToPort is required by tf
|
||||
tf.ToPort = fi.Int64(65535)
|
||||
}
|
||||
|
||||
if e.SourceGroup != nil {
|
||||
tf.SourceGroup = e.SourceGroup.CloudformationLink()
|
||||
}
|
||||
|
||||
if e.CIDR != nil {
|
||||
tf.CidrIp = e.CIDR
|
||||
}
|
||||
|
||||
return t.RenderResource(cfType, *e.Name, tf)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import (
|
|||
"golang.org/x/crypto/ssh"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
"k8s.io/kops/upup/pkg/fi/utils"
|
||||
)
|
||||
|
|
@ -55,6 +56,10 @@ func (e *SSHKey) CompareWithID() *string {
|
|||
func (e *SSHKey) Find(c *fi.Context) (*SSHKey, error) {
|
||||
cloud := c.Cloud.(awsup.AWSCloud)
|
||||
|
||||
return e.find(cloud)
|
||||
}
|
||||
|
||||
func (e *SSHKey) find(cloud awsup.AWSCloud) (*SSHKey, error) {
|
||||
request := &ec2.DescribeKeyPairsInput{
|
||||
KeyNames: []*string{e.Name},
|
||||
}
|
||||
|
|
@ -212,28 +217,34 @@ func (s *SSHKey) CheckChanges(a, e, changes *SSHKey) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (e *SSHKey) createKeypair(cloud awsup.AWSCloud) error {
|
||||
glog.V(2).Infof("Creating SSHKey with Name:%q", *e.Name)
|
||||
|
||||
request := &ec2.ImportKeyPairInput{
|
||||
KeyName: e.Name,
|
||||
}
|
||||
|
||||
if e.PublicKey != nil {
|
||||
d, err := e.PublicKey.AsBytes()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error rendering SSHKey PublicKey: %v", err)
|
||||
}
|
||||
request.PublicKeyMaterial = d
|
||||
}
|
||||
|
||||
response, err := cloud.EC2().ImportKeyPair(request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating SSHKey: %v", err)
|
||||
}
|
||||
|
||||
e.KeyFingerprint = response.KeyFingerprint
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *SSHKey) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *SSHKey) error {
|
||||
if a == nil {
|
||||
glog.V(2).Infof("Creating SSHKey with Name:%q", *e.Name)
|
||||
|
||||
request := &ec2.ImportKeyPairInput{
|
||||
KeyName: e.Name,
|
||||
}
|
||||
|
||||
if e.PublicKey != nil {
|
||||
d, err := e.PublicKey.AsBytes()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error rendering SSHKey PublicKey: %v", err)
|
||||
}
|
||||
request.PublicKeyMaterial = d
|
||||
}
|
||||
|
||||
response, err := t.Cloud.EC2().ImportKeyPair(request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating SSHKey: %v", err)
|
||||
}
|
||||
|
||||
e.KeyFingerprint = response.KeyFingerprint
|
||||
return e.createKeypair(t.Cloud)
|
||||
}
|
||||
|
||||
// No tags on SSH public key
|
||||
|
|
@ -264,3 +275,23 @@ func (e *SSHKey) TerraformLink() *terraform.Literal {
|
|||
tfName := strings.Replace(*e.Name, ":", "", -1)
|
||||
return terraform.LiteralProperty("aws_key_pair", tfName, "id")
|
||||
}
|
||||
|
||||
func (_ *SSHKey) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *SSHKey) error {
|
||||
cloud := t.Cloud.(awsup.AWSCloud)
|
||||
|
||||
glog.Warningf("Cloudformation does not manage SSH keys; pre-creating SSH key")
|
||||
|
||||
a, err := e.find(cloud)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if a == nil {
|
||||
err := e.createKeypair(cloud)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
"k8s.io/kops/upup/pkg/fi/utils"
|
||||
)
|
||||
|
|
@ -222,3 +223,43 @@ func (e *Subnet) TerraformLink() *terraform.Literal {
|
|||
|
||||
return terraform.LiteralProperty("aws_subnet", *e.Name, "id")
|
||||
}
|
||||
|
||||
type cloudformationSubnet struct {
|
||||
VPCID *cloudformation.Literal `json:"VpcId,omitempty"`
|
||||
CIDR *string `json:"CidrBlock,omitempty"`
|
||||
AvailabilityZone *string `json:"AvailabilityZone,omitempty"`
|
||||
Tags []cloudformationTag `json:"Tags,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *Subnet) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *Subnet) error {
|
||||
cloud := t.Cloud.(awsup.AWSCloud)
|
||||
|
||||
shared := fi.BoolValue(e.Shared)
|
||||
if shared {
|
||||
// Not cloudformation owned / managed
|
||||
return nil
|
||||
}
|
||||
|
||||
cf := &cloudformationSubnet{
|
||||
VPCID: e.VPC.CloudformationLink(),
|
||||
CIDR: e.CIDR,
|
||||
AvailabilityZone: e.AvailabilityZone,
|
||||
Tags: buildCloudformationTags(cloud.BuildTags(e.Name)),
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::EC2::Subnet", *e.Name, cf)
|
||||
}
|
||||
|
||||
func (e *Subnet) CloudformationLink() *cloudformation.Literal {
|
||||
shared := fi.BoolValue(e.Shared)
|
||||
if shared {
|
||||
if e.ID == nil {
|
||||
glog.Fatalf("ID must be set, if subnet is shared: %s", e)
|
||||
}
|
||||
|
||||
glog.V(4).Infof("reusing existing subnet with id %q", *e.ID)
|
||||
return cloudformation.LiteralString(*e.ID)
|
||||
}
|
||||
|
||||
return cloudformation.Ref("AWS::EC2::Subnet", *e.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"k8s.io/kops/pkg/featureflag"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
|
|
@ -229,3 +230,43 @@ func (e *VPC) TerraformLink() *terraform.Literal {
|
|||
|
||||
return terraform.LiteralProperty("aws_vpc", *e.Name, "id")
|
||||
}
|
||||
|
||||
type cloudformationVPC struct {
|
||||
CidrBlock *string `json:"CidrBlock,omitempty"`
|
||||
EnableDnsHostnames *bool `json:"EnableDnsHostnames,omitempty"`
|
||||
EnableDnsSupport *bool `json:"EnableDnsSupport,omitempty"`
|
||||
Tags []cloudformationTag `json:"Tags,omitempty"`
|
||||
}
|
||||
|
||||
func (_ *VPC) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *VPC) error {
|
||||
cloud := t.Cloud.(awsup.AWSCloud)
|
||||
|
||||
shared := fi.BoolValue(e.Shared)
|
||||
if shared {
|
||||
// Not cloudformation owned / managed
|
||||
return nil
|
||||
}
|
||||
|
||||
tf := &cloudformationVPC{
|
||||
CidrBlock: e.CIDR,
|
||||
EnableDnsHostnames: e.EnableDNSHostnames,
|
||||
EnableDnsSupport: e.EnableDNSSupport,
|
||||
Tags: buildCloudformationTags(cloud.BuildTags(e.Name)),
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::EC2::VPC", *e.Name, tf)
|
||||
}
|
||||
|
||||
func (e *VPC) CloudformationLink() *cloudformation.Literal {
|
||||
shared := fi.BoolValue(e.Shared)
|
||||
if shared {
|
||||
if e.ID == nil {
|
||||
glog.Fatalf("ID must be set, if VPC is shared: %s", e)
|
||||
}
|
||||
|
||||
glog.V(4).Infof("reusing existing VPC with id %q", *e.ID)
|
||||
return cloudformation.LiteralString(*e.ID)
|
||||
}
|
||||
|
||||
return cloudformation.Ref("AWS::EC2::VPC", *e.Name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/cloudformation"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
|
|
@ -111,3 +112,17 @@ func (_ *VPCDHCPOptionsAssociation) RenderTerraform(t *terraform.TerraformTarget
|
|||
|
||||
return t.RenderResource("aws_vpc_dhcp_options_association", *e.Name, tf)
|
||||
}
|
||||
|
||||
type cloudformationVPCDHCPOptionsAssociation struct {
|
||||
VpcId *cloudformation.Literal `json:"VpcId"`
|
||||
DhcpOptionsId *cloudformation.Literal `json:"DhcpOptionsId"`
|
||||
}
|
||||
|
||||
func (_ *VPCDHCPOptionsAssociation) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *VPCDHCPOptionsAssociation) error {
|
||||
tf := &cloudformationVPCDHCPOptionsAssociation{
|
||||
VpcId: e.VPC.CloudformationLink(),
|
||||
DhcpOptionsId: e.DHCPOptions.CloudformationLink(),
|
||||
}
|
||||
|
||||
return t.RenderResource("AWS::EC2::VPCDHCPOptionsAssociation", *e.Name, tf)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License 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 cloudformation
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type Literal struct {
|
||||
json interface{}
|
||||
}
|
||||
|
||||
var _ json.Marshaler = &Literal{}
|
||||
|
||||
func (l *Literal) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&l.json)
|
||||
}
|
||||
|
||||
func (l *Literal) extractRef() string {
|
||||
m, ok := l.json.(map[string]interface{})
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
ref := m["Ref"]
|
||||
if ref == nil {
|
||||
return ""
|
||||
}
|
||||
s, ok := ref.(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func literalRef(s string) *Literal {
|
||||
j := make(map[string]interface{})
|
||||
j["Ref"] = s
|
||||
return &Literal{json: j}
|
||||
}
|
||||
|
||||
func Ref(resourceType, resourceName string) *Literal {
|
||||
return literalRef(sanitizeCloudformationResourceName(resourceType + "::" + resourceName))
|
||||
}
|
||||
|
||||
func GetAtt(resourceType, resourceName string, attribute string) *Literal {
|
||||
path := []string{
|
||||
sanitizeCloudformationResourceName(resourceType + "::" + resourceName),
|
||||
attribute,
|
||||
}
|
||||
j := make(map[string]interface{})
|
||||
j["Fn::GetAtt"] = path
|
||||
return &Literal{json: j}
|
||||
}
|
||||
|
||||
func LiteralString(v string) *Literal {
|
||||
j := &v
|
||||
return &Literal{json: j}
|
||||
}
|
||||
|
||||
//
|
||||
//func LiteralSelfLink(resourceType, resourceName string) *Literal {
|
||||
// return LiteralProperty(resourceType, resourceName, "self_link")
|
||||
//}
|
||||
//
|
||||
//
|
||||
//func DefaultProperty(resourceType, resourceName string) *Literal {
|
||||
// return LiteralProperty(resourceType, resourceName, "")
|
||||
//}
|
||||
//
|
||||
//func LiteralProperty(resourceType, resourceName, prop string) *Literal {
|
||||
// tfName := sanitizeCloudformationResourceName( resourceType + "::" + resourceName)
|
||||
//
|
||||
// expr := "${" + resourceType + "." + tfName + "." + prop + "}"
|
||||
// return LiteralExpression(expr)
|
||||
//}
|
||||
//
|
||||
//func LiteralFromStringValue(s string) *Literal {
|
||||
// return &Literal{value: s}
|
||||
//}
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License 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 cloudformation
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
"io/ioutil"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type CloudformationTarget struct {
|
||||
Cloud fi.Cloud
|
||||
Region string
|
||||
Project string
|
||||
|
||||
outDir string
|
||||
|
||||
// mutex protects the following items (resources & files)
|
||||
mutex sync.Mutex
|
||||
resources map[string]*cloudformationResource
|
||||
}
|
||||
|
||||
func NewCloudformationTarget(cloud fi.Cloud, region, project string, outDir string) *CloudformationTarget {
|
||||
return &CloudformationTarget{
|
||||
Cloud: cloud,
|
||||
Region: region,
|
||||
Project: project,
|
||||
outDir: outDir,
|
||||
resources: make(map[string]*cloudformationResource),
|
||||
}
|
||||
}
|
||||
|
||||
var _ fi.Target = &CloudformationTarget{}
|
||||
|
||||
type cloudformationResource struct {
|
||||
Type string
|
||||
Properties interface{}
|
||||
}
|
||||
|
||||
// A cloudformation resource name must be alphanumeric
|
||||
func sanitizeCloudformationResourceName(name string) string {
|
||||
name = strings.Replace(name, ".", "", -1)
|
||||
name = strings.Replace(name, "-", "", -1)
|
||||
name = strings.Replace(name, ":", "", -1)
|
||||
name = strings.Replace(name, "/", "", -1)
|
||||
return name
|
||||
}
|
||||
|
||||
func (t *CloudformationTarget) ProcessDeletions() bool {
|
||||
// Terraform tracks & performs deletions itself
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *CloudformationTarget) RenderResource(resourceType string, resourceName string, e interface{}) error {
|
||||
res := &cloudformationResource{
|
||||
Type: resourceType,
|
||||
Properties: e,
|
||||
}
|
||||
|
||||
name := resourceType + "::" + resourceName
|
||||
name = sanitizeCloudformationResourceName(name)
|
||||
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
|
||||
if t.resources[name] != nil {
|
||||
return fmt.Errorf("resource %q already exists in cloudformation", name)
|
||||
}
|
||||
t.resources[name] = res
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *CloudformationTarget) Find(ref *Literal) (interface{}, bool) {
|
||||
key := ref.extractRef()
|
||||
if key == "" {
|
||||
glog.Warningf("Unable to extract ref from %v", ref)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
|
||||
r := t.resources[key]
|
||||
if r == nil {
|
||||
return nil, false
|
||||
}
|
||||
return r.Properties, true
|
||||
}
|
||||
|
||||
func (t *CloudformationTarget) Finish(taskMap map[string]fi.Task) error {
|
||||
//resourcesByType := make(map[string]map[string]interface{})
|
||||
//
|
||||
//for _, res := range t.resources {
|
||||
// resources := resourcesByType[res.ResourceType]
|
||||
// if resources == nil {
|
||||
// resources = make(map[string]interface{})
|
||||
// resourcesByType[res.ResourceType] = resources
|
||||
// }
|
||||
//
|
||||
// tfName := tfSanitize(res.ResourceName)
|
||||
//
|
||||
// if resources[tfName] != nil {
|
||||
// return fmt.Errorf("duplicate resource found: %s.%s", res.ResourceType, tfName)
|
||||
// }
|
||||
//
|
||||
// resources[tfName] = res.Item
|
||||
//}
|
||||
|
||||
//providersByName := make(map[string]map[string]interface{})
|
||||
//if t.Cloud.ProviderID() == fi.CloudProviderGCE {
|
||||
// providerGoogle := make(map[string]interface{})
|
||||
// providerGoogle["project"] = t.Project
|
||||
// providerGoogle["region"] = t.Region
|
||||
// providersByName["google"] = providerGoogle
|
||||
//} else if t.Cloud.ProviderID() == fi.CloudProviderAWS {
|
||||
// providerAWS := make(map[string]interface{})
|
||||
// providerAWS["region"] = t.Region
|
||||
// providersByName["aws"] = providerAWS
|
||||
//}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
data["Resources"] = t.resources
|
||||
//if len(providersByName) != 0 {
|
||||
// data["provider"] = providersByName
|
||||
//}
|
||||
|
||||
jsonBytes, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error marshalling cloudformation data to json: %v", err)
|
||||
}
|
||||
|
||||
files := make(map[string][]byte)
|
||||
files["kubernetes.json"] = jsonBytes
|
||||
|
||||
for relativePath, contents := range files {
|
||||
p := path.Join(t.outDir, relativePath)
|
||||
|
||||
err = os.MkdirAll(path.Dir(p), os.FileMode(0755))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating output directory %q: %v", path.Dir(p), err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(p, contents, os.FileMode(0644))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing cloudformation data to output file %q: %v", p, err)
|
||||
}
|
||||
}
|
||||
|
||||
glog.Infof("Cloudformation output is in %s", t.outDir)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -19,3 +19,4 @@ package cloudup
|
|||
const TargetDirect = "direct"
|
||||
const TargetDryRun = "dryrun"
|
||||
const TargetTerraform = "terraform"
|
||||
const TargetCloudformation = "cloudformation"
|
||||
|
|
|
|||
Loading…
Reference in New Issue