Permalink
Browse files

Repair CF rollbacks to work after the S3 userdata improvement

Fixes #352
  • Loading branch information...
mumoshu committed Mar 22, 2017
1 parent 04ea55b commit 9c01a23281e893703601d264c30d3ad4811d65ef
@@ -137,8 +137,8 @@ func (c *Cluster) Assets() (cfnstack.Assets, error) {
}
return cfnstack.NewAssetsBuilder(c.StackName(), c.StackConfig.S3URI, c.StackConfig.Region).
Add("userdata-controller", c.UserDataController).
Add("userdata-etcd", c.UserDataEtcd).
Add(c.UserDataControllerFileName(), c.UserDataController).
Add(c.UserDataEtcdFileName(), c.UserDataEtcd).
Add(STACK_TEMPLATE_FILENAME, stackTemplate).
Build(), nil
}
@@ -502,13 +502,13 @@ stackTags:
t.Errorf("error creating cluster: %v\nfor test case %+v", err, testCase)
}
path, err := cluster.UserDataControllerS3Path()
path, err := cluster.UserDataControllerS3Prefix()
if err != nil {
t.Errorf("failed to get controller user data path in s3: %v", err)
}
if path != "test-bucket/foo/bar/kube-aws/clusters/test-cluster-name/exported/stacks/control-plane/userdata-controller" {
t.Errorf("UserDataControllerS3Path returned an unexpected value: %s", path)
t.Errorf("UserDataControllerS3Prefix returned an unexpected value: %s", path)
}
})
}
@@ -4,6 +4,7 @@ import (
"fmt"
"github.com/coreos/kube-aws/coreos/userdatavalidation"
"github.com/coreos/kube-aws/filereader/jsontemplate"
"github.com/coreos/kube-aws/fingerprint"
"net/url"
)
@@ -20,20 +21,70 @@ type CompressedStackConfig struct {
*StackConfig
}
func (c *StackConfig) UserDataControllerS3Path() (string, error) {
// UserDataControllerS3Prefix is the prefix prepended to all userdata-controller-<fingerprint> files uploaded to S3
// Use this to author the IAM policy to provide controller nodes least required permissions for getting the files from S3
func (c *StackConfig) UserDataControllerS3Prefix() (string, error) {
s3dir, err := c.userDataControllerS3Directory()
if err != nil {
return "", fmt.Errorf("Error in UserDataControllerS3Prefix : %v", err)
}
return fmt.Sprintf("%s/userdata-controller", s3dir), nil
}
func (c *StackConfig) userDataControllerS3Directory() (string, error) {
s3uri, err := url.Parse(c.S3URI)
if err != nil {
return "", fmt.Errorf("Error in UserDataControllerS3Path : %v", err)
return "", fmt.Errorf("Error in userDataControllerS3Directory : %v", err)
}
return fmt.Sprintf("%s%s/%s", s3uri.Host, s3uri.Path, c.StackName()), nil
}
// UserDataControllerS3URI is the URI to an userdata-controller-<fingerprint> file used to provision controller nodes
// Use this to run download the file by running e.g. `aws cp *return value of UserDataControllerS3URI* ./`
func (c *StackConfig) UserDataControllerS3URI() (string, error) {
s3dir, err := c.userDataControllerS3Directory()
if err != nil {
return "", fmt.Errorf("Error in UserDataControllerS3URI : %v", err)
}
return fmt.Sprintf("s3://%s/%s", s3dir, c.UserDataControllerFileName()), nil
}
// UserDataControllerFileName is used to upload and download userdata-controller-<fingerprint> files
func (c *StackConfig) UserDataControllerFileName() string {
return "userdata-controller-" + fingerprint.SHA256(c.UserDataController)
}
// UserDataEtcdS3Prefix is the prefix prepended to all userdata-etcd-<fingerprint> files uploaded to S3
// Use this to author the IAM policy to provide etcd nodes least required permissions for getting the files from S3
func (c *StackConfig) UserDataEtcdS3Prefix() (string, error) {
s3dir, err := c.userDataEtcdS3Directory()
if err != nil {
return "", fmt.Errorf("Error in UserDataEtcdS3Prefix : %v", err)
}
return fmt.Sprintf("%s%s/%s/userdata-controller", s3uri.Host, s3uri.Path, c.StackName()), nil
return fmt.Sprintf("%s/userdata-etcd", s3dir), nil
}
func (c *StackConfig) UserDataEtcdS3Path() (string, error) {
func (c *StackConfig) userDataEtcdS3Directory() (string, error) {
s3uri, err := url.Parse(c.S3URI)
if err != nil {
return "", fmt.Errorf("Error in UserDataEtcdS3Path : %v", err)
return "", fmt.Errorf("Error in userDataEtcdS3Directory : %v", err)
}
return fmt.Sprintf("%s%s/%s", s3uri.Host, s3uri.Path, c.StackName()), nil
}
// UserDataEtcdS3URI is the URI to an userdata-etcd-<fingerprint> file used to provision etcd nodes
// Use this to run download the file by running e.g. `aws cp *return value of UserDataEtcdS3URI* ./`
func (c *StackConfig) UserDataEtcdS3URI() (string, error) {
s3dir, err := c.userDataEtcdS3Directory()
if err != nil {
return "", fmt.Errorf("Error in UserDataEtcdS3URI : %v", err)
}
return fmt.Sprintf("%s%s/%s/userdata-etcd", s3uri.Host, s3uri.Path, c.StackName()), nil
return fmt.Sprintf("s3://%s/%s", s3dir, c.UserDataEtcdFileName()), nil
}
// UserDataEtcdFileName is used to upload and download userdata-etcd-<fingerprint> files
func (c *StackConfig) UserDataEtcdFileName() string {
return "userdata-etcd-" + fingerprint.SHA256(c.UserDataEtcd)
}
func (c *StackConfig) ValidateUserData() error {
@@ -180,7 +180,7 @@
"Action": [
"s3:GetObject"
],
"Resource": "arn:{{.Region.Partition}}:s3:::{{$.UserDataControllerS3Path}}"
"Resource": "arn:{{.Region.Partition}}:s3:::{{$.UserDataControllerS3Prefix}}*"
},
{{if .WaitSignal.Enabled}}
{
@@ -335,7 +335,7 @@
"Action": [
"s3:GetObject"
],
"Resource": "arn:{{.Region.Partition}}:s3:::{{$.UserDataEtcdS3Path}}"
"Resource": "arn:{{.Region.Partition}}:s3:::{{$.UserDataEtcdS3Prefix}}*"
}
],
"Version": "2012-10-17"
@@ -565,20 +565,19 @@
],
"PlacementTenancy": "{{$.EtcdTenancy}}",
"UserData": { "Fn::Base64": { "Fn::Join" : ["\n", [
"#!/bin/bash",
"# userdata hash: {{ $.UserDataEtcd | sha1 }}",
"#!/bin/bash -xe",
{"Fn::Join":["",[ "echo '{{$.StackNameEnvVarName}}=", { "Ref": "AWS::StackName" }, "' >> {{$.EtcdNodeEnvFileName}}" ]]},
"echo '{{$.EtcdIndexEnvVarName}}={{$etcdIndex}}' >> {{$.EtcdNodeEnvFileName}}",
" . /etc/environment",
"export COREOS_PRIVATE_IPV4 COREOS_PRIVATE_IPV6 COREOS_PUBLIC_IPV4 COREOS_PUBLIC_IPV6",
"REGION=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region')",
"USERDATA_FILE=userdata-etcd",
"USERDATA_FILE={{$.UserDataEtcdFileName}}",
"/usr/bin/rkt run \\",
" --net=host \\",
" --volume=dns,kind=host,source=/etc/resolv.conf,readOnly=true --mount volume=dns,target=/etc/resolv.conf \\",
" --volume=awsenv,kind=host,source=/var/run/coreos,readOnly=false --mount volume=awsenv,target=/var/run/coreos \\",
" --trust-keys-from-https \\",
" {{$.AWSCliImage.Options}}{{$.AWSCliImage.RktRepo}} --exec=aws -- s3 --region $REGION cp {{ $.S3URI }}/{{ $.StackName }}/$USERDATA_FILE /var/run/coreos/",
" {{$.AWSCliImage.Options}}{{$.AWSCliImage.RktRepo}} --exec=aws -- s3 --region $REGION cp {{ $.UserDataEtcdS3URI }} /var/run/coreos/$USERDATA_FILE",
"exec /usr/bin/coreos-cloudinit --from-file /var/run/coreos/$USERDATA_FILE"
]]}}
},
@@ -612,18 +611,17 @@
],
"PlacementTenancy": "{{ .ControllerTenancy }}",
"UserData": { "Fn::Base64": { "Fn::Join" : ["\n", [
"#!/bin/bash",
"# userdata hash: {{ .UserDataController | sha1 }}",
"#!/bin/bash -xe",
" . /etc/environment",
"export COREOS_PRIVATE_IPV4 COREOS_PRIVATE_IPV6 COREOS_PUBLIC_IPV4 COREOS_PUBLIC_IPV6",
"REGION=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region')",
"USERDATA_FILE=userdata-controller",
"USERDATA_FILE={{.UserDataControllerFileName}}",
"/usr/bin/rkt run \\",
" --net=host \\",
" --volume=dns,kind=host,source=/etc/resolv.conf,readOnly=true --mount volume=dns,target=/etc/resolv.conf \\",
" --volume=awsenv,kind=host,source=/var/run/coreos,readOnly=false --mount volume=awsenv,target=/var/run/coreos \\",
" --trust-keys-from-https \\",
" {{.AWSCliImage.Options}}{{.AWSCliImage.RktRepo}} --exec=aws -- s3 --region $REGION cp {{ .S3URI }}/{{ .StackName }}/$USERDATA_FILE /var/run/coreos/",
" {{.AWSCliImage.Options}}{{.AWSCliImage.RktRepo}} --exec=aws -- s3 --region $REGION cp {{ .UserDataControllerS3URI }} /var/run/coreos/$USERDATA_FILE",
"exec /usr/bin/coreos-cloudinit --from-file /var/run/coreos/$USERDATA_FILE"
]]}}
},
@@ -91,7 +91,7 @@ func (c *Cluster) Assets() (cfnstack.Assets, error) {
}
return cfnstack.NewAssetsBuilder(c.StackName(), c.StackConfig.S3URI, c.StackConfig.Region).
Add("userdata-worker", c.UserDataWorker).
Add(c.UserDataWorkerFileName(), c.UserDataWorker).
Add(STACK_TEMPLATE_FILENAME, stackTemplate).
Build(), nil
}
@@ -252,13 +252,13 @@ name: pool1
t.Errorf("error creating cluster: %v", err)
}
path, err := cluster.UserDataWorkerS3Path()
path, err := cluster.UserDataWorkerS3Prefix()
if err != nil {
t.Errorf("failed to get worker user data path in s3: %v", err)
}
if path != "test-bucket/foo/bar/kube-aws/clusters/test-cluster-name/exported/stacks/pool1/userdata-worker" {
t.Errorf("UserDataControllerS3Path returned an unexpected value: %s", path)
t.Errorf("UserDataControllerS3Prefix returned an unexpected value: %s", path)
}
})
}
@@ -4,6 +4,7 @@ import (
"fmt"
"github.com/coreos/kube-aws/coreos/userdatavalidation"
"github.com/coreos/kube-aws/filereader/jsontemplate"
"github.com/coreos/kube-aws/fingerprint"
"net/url"
)
@@ -17,12 +18,37 @@ type CompressedStackConfig struct {
*StackConfig
}
func (c *StackConfig) UserDataWorkerS3Path() (string, error) {
// UserDataWorkerS3Prefix is the prefix prepended to all user-data-worker-<fingerprint> files uploaded to S3
// Use this to author the IAM policy to provide worker nodes least required permissions for getting the files from S3
func (c *StackConfig) UserDataWorkerS3Prefix() (string, error) {
s3dir, err := c.userDataWorkerS3Directory()
if err != nil {
return "", fmt.Errorf("Error in UserDataWorkerS3Prefix : %v", err)
}
return fmt.Sprintf("%s/userdata-worker", s3dir), nil
}
func (c *StackConfig) userDataWorkerS3Directory() (string, error) {
s3uri, err := url.Parse(c.S3URI)
if err != nil {
return "", fmt.Errorf("Error in UserDataWorkerS3Path : %v", err)
return "", fmt.Errorf("Error in userDataWorkerS3Directory : %v", err)
}
return fmt.Sprintf("%s%s/%s", s3uri.Host, s3uri.Path, c.StackName()), nil
}
// UserDataWorkerS3URI is the URI to an userdata-worker-<fingerprint> file used to provision worker nodes
// Use this to run download the file by running e.g. `aws cp *return value of UserDataWorkerS3URI* ./`
func (c *StackConfig) UserDataWorkerS3URI() (string, error) {
s3dir, err := c.userDataWorkerS3Directory()
if err != nil {
return "", fmt.Errorf("Error in UserDataWorkerS3URI : %v", err)
}
return fmt.Sprintf("%s%s/%s/userdata-worker", s3uri.Host, s3uri.Path, c.StackName()), nil
return fmt.Sprintf("s3://%s/%s", s3dir, c.UserDataWorkerFileName()), nil
}
// UserDataWorkerFileName is used to upload and download userdata-worker-<fingerprint> files
func (c *StackConfig) UserDataWorkerFileName() string {
return "userdata-worker-" + fingerprint.SHA256(c.UserDataWorker)
}
func (c *StackConfig) ValidateUserData() error {
@@ -1,18 +1,17 @@
{{define "UserData"}}
{ "Fn::Base64": { "Fn::Join" : ["\n", [
"#!/bin/bash",
"# userdata hash: {{ .UserDataWorker | sha1 }}",
"#!/bin/bash -xe",
{"Fn::Join":["",[ "echo '{{.StackNameEnvVarName}}=", { "Ref": "AWS::StackName" }, "' >> {{.StackNameEnvFileName}}" ]]},
" . /etc/environment",
"export COREOS_PRIVATE_IPV4 COREOS_PRIVATE_IPV6 COREOS_PUBLIC_IPV4 COREOS_PUBLIC_IPV6",
"REGION=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region')",
"USERDATA_FILE=userdata-worker",
"USERDATA_FILE={{.UserDataWorkerFileName}}",
"/usr/bin/rkt run \\",
" --net=host \\",
" --volume=dns,kind=host,source=/etc/resolv.conf,readOnly=true --mount volume=dns,target=/etc/resolv.conf \\",
" --volume=awsenv,kind=host,source=/var/run/coreos,readOnly=false --mount volume=awsenv,target=/var/run/coreos \\",
" --trust-keys-from-https \\",
" {{.AWSCliImage.Options}}{{.AWSCliImage.RktRepo}} --exec=aws -- s3 --region $REGION cp {{ .S3URI }}/{{ .StackName }}/$USERDATA_FILE /var/run/coreos/",
" {{.AWSCliImage.Options}}{{.AWSCliImage.RktRepo}} --exec=aws -- s3 --region $REGION cp {{ .UserDataWorkerS3URI }} /var/run/coreos/$USERDATA_FILE",
"exec /usr/bin/coreos-cloudinit --from-file /var/run/coreos/$USERDATA_FILE"
]]}}
{{end}}
@@ -326,7 +325,7 @@
"Action": [
"s3:GetObject"
],
"Resource": "arn:{{.Region.Partition}}:s3:::{{$.UserDataWorkerS3Path}}"
"Resource": "arn:{{.Region.Partition}}:s3:::{{$.UserDataWorkerS3Prefix}}*"
},
{{if .Kube2IamSupport.Enabled }}
{
View
@@ -0,0 +1,13 @@
package fingerprint
import (
"crypto/sha256"
"fmt"
)
// SHA256 calculates and returns a SHA-256 hash intended to be used for a fingerprint of the original data
func SHA256(data string) string {
h := sha256.New()
h.Write([]byte(data))
return fmt.Sprintf("%x", h.Sum(nil))
}
View
@@ -0,0 +1,13 @@
package fingerprint
import (
"testing"
)
func TestSHA256(t *testing.T) {
actual := SHA256("mychangingdata")
expected := "ee867acc5d96cced9b9fe075e293604214519650065c60b42b95f1ccfbac2c97"
if actual != expected {
t.Errorf("unexpected value returned from SHA256: expected=%v actual=%v", expected, actual)
}
}

0 comments on commit 9c01a23

Please sign in to comment.