Skip to content
This repository was archived by the owner on Sep 30, 2020. It is now read-only.

Commit ed09dd9

Browse files
authored
Merge pull request #106 from mumoshu/node-pools
Initial implementation of Node Pools
2 parents 026be76 + 648395a commit ed09dd9

File tree

15 files changed

+1302
-0
lines changed

15 files changed

+1302
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
/e2e/assets
44
*~
55
/config/templates.go
6+
/nodepool/config/templates.go
67
.idea/
78
.envrc
89
coverage.txt

build

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ fi
1919
echo Building kube-aws ${VERSION}
2020

2121
go generate ./config
22+
go generate ./nodepool/config
2223

2324
if [[ ! "${BUILD_GOOS:-}" == "" ]];then
2425
export GOOS=$BUILD_GOOS

cmd/nodepool.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package cmd
2+
3+
import "github.com/coreos/kube-aws/cmd/nodepool"
4+
5+
func init() {
6+
RootCmd.AddCommand(nodepool.NodePoolCmd)
7+
}

cmd/nodepool/init.go

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package nodepool
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
8+
cfg "github.com/coreos/kube-aws/config"
9+
"github.com/coreos/kube-aws/filegen"
10+
"github.com/coreos/kube-aws/nodepool/config"
11+
"github.com/spf13/cobra"
12+
)
13+
14+
var (
15+
cmdInit = &cobra.Command{
16+
Use: "init",
17+
Short: "Initialize default node pool configuration",
18+
Long: ``,
19+
RunE: runCmdInit,
20+
SilenceUsage: true,
21+
}
22+
23+
initOpts = config.ComputedConfig{}
24+
)
25+
26+
func init() {
27+
NodePoolCmd.AddCommand(cmdInit)
28+
cmdInit.Flags().StringVar(&initOpts.AvailabilityZone, "availability-zone", "", "The AWS availability-zone to deploy to")
29+
cmdInit.Flags().StringVar(&initOpts.KeyName, "key-name", "", "The AWS key-pair for ssh access to nodes")
30+
cmdInit.Flags().StringVar(&initOpts.KMSKeyARN, "kms-key-arn", "", "The ARN of the AWS KMS key for encrypting TLS assets")
31+
cmdInit.Flags().StringVar(&initOpts.AmiId, "ami-id", "", "The AMI ID of CoreOS")
32+
}
33+
34+
func runCmdInit(cmd *cobra.Command, args []string) error {
35+
initOpts.NodePoolName = nodePoolOpts.PoolName
36+
37+
// Validate flags.
38+
required := []struct {
39+
name, val string
40+
}{
41+
{"--node-pool-name", initOpts.NodePoolName},
42+
{"--availability-zone", initOpts.AvailabilityZone},
43+
}
44+
var missing []string
45+
for _, req := range required {
46+
if req.val == "" {
47+
missing = append(missing, strconv.Quote(req.name))
48+
}
49+
}
50+
if len(missing) != 0 {
51+
return fmt.Errorf("Missing required flag(s): %s", strings.Join(missing, ", "))
52+
}
53+
54+
// Read the config from file.
55+
mainConfig, err := cfg.ClusterFromFile(clusterConfigFilePath())
56+
if err != nil {
57+
return fmt.Errorf("Failed to read cluster config: %v", err)
58+
}
59+
60+
main, err := mainConfig.Config()
61+
if err != nil {
62+
return fmt.Errorf("Failed to create config: %v", err)
63+
}
64+
65+
// Required and inheritable settings for the node pool.
66+
//
67+
// These can be set via command-line options to kube-aws nodepool init.
68+
// If omitted, these can be inherited from the main cluster's cluster.yaml.
69+
70+
if initOpts.Region == "" {
71+
initOpts.Region = main.Region
72+
}
73+
74+
if initOpts.KeyName == "" {
75+
initOpts.KeyName = main.KeyName
76+
}
77+
78+
if initOpts.KMSKeyARN == "" {
79+
initOpts.KMSKeyARN = main.KMSKeyARN
80+
}
81+
82+
if initOpts.AmiId == "" {
83+
initOpts.AmiId = main.AmiId
84+
}
85+
86+
if initOpts.ReleaseChannel == "" {
87+
initOpts.ReleaseChannel = main.ReleaseChannel
88+
}
89+
90+
if initOpts.VPCCIDR == "" {
91+
initOpts.VPCCIDR = main.VPCCIDR
92+
}
93+
94+
// Required, inheritable and importable settings for the node pool.
95+
//
96+
// These can be customized in the node pool's cluster.yaml
97+
// If omitted from it, these can also can be exported from the main cluster
98+
// and then imported to the node pool in the cloudformation layer.
99+
100+
if initOpts.VPCID == "" {
101+
initOpts.VPCID = main.VPCID
102+
}
103+
104+
if initOpts.RouteTableID == "" {
105+
initOpts.RouteTableID = main.RouteTableID
106+
}
107+
108+
if initOpts.EtcdEndpoints == "" {
109+
initOpts.EtcdEndpoints = main.EtcdEndpoints
110+
}
111+
112+
initOpts.ClusterName = main.ClusterName
113+
114+
// Required and shared settings for the node pool.
115+
//
116+
// These can not be customized in the node pool's cluster yaml.
117+
// Customizing these values in each node pool doesn't make sense
118+
// because inconsistency between the main cluster and node pools result in
119+
// unusable worker nodes that can't communicate with k8s apiserver, kube-dns, calico, etc.
120+
121+
initOpts.KubeClusterSettings = main.KubeClusterSettings
122+
123+
if err := filegen.CreateFileFromTemplate(nodePoolClusterConfigFilePath(), initOpts, config.DefaultClusterConfig); err != nil {
124+
return fmt.Errorf("Error exec-ing default config template: %v", err)
125+
}
126+
127+
successMsg :=
128+
`Success! Created %s
129+
130+
Next steps:
131+
1. (Optional) Edit %s to parameterize the cluster.
132+
2. Use the "kube-aws nodepool render" command to render the stack template.
133+
`
134+
135+
fmt.Printf(successMsg, nodePoolClusterConfigFilePath, nodePoolClusterConfigFilePath)
136+
return nil
137+
}

cmd/nodepool/render.go

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package nodepool
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"os"
7+
8+
"path"
9+
10+
cfg "github.com/coreos/kube-aws/config"
11+
"github.com/coreos/kube-aws/nodepool/config"
12+
"github.com/spf13/cobra"
13+
"strconv"
14+
"strings"
15+
)
16+
17+
var (
18+
cmdRender = &cobra.Command{
19+
Use: "render",
20+
Short: "Render deployment artifacts",
21+
Long: ``,
22+
RunE: runCmdRender,
23+
SilenceUsage: true,
24+
}
25+
26+
cmdRenderStack = &cobra.Command{
27+
Use: "stack",
28+
Short: "Render CloudFormation stack",
29+
Long: ``,
30+
RunE: runCmdRenderStack,
31+
SilenceUsage: true,
32+
}
33+
)
34+
35+
func init() {
36+
NodePoolCmd.AddCommand(cmdRender)
37+
38+
cmdRender.AddCommand(cmdRenderStack)
39+
}
40+
41+
func runCmdRender(cmd *cobra.Command, args []string) error {
42+
fmt.Printf("WARNING: 'kube-aws render' is deprecated. See 'kube-aws render --help' for usage\n")
43+
44+
if len(args) != 0 {
45+
return fmt.Errorf("render takes no arguments\n")
46+
}
47+
48+
if err := runCmdRenderStack(cmd, args); err != nil {
49+
return err
50+
}
51+
52+
return nil
53+
}
54+
func runCmdRenderStack(cmd *cobra.Command, args []string) error {
55+
if len(args) != 0 {
56+
return fmt.Errorf("render stack takes no arguments\n")
57+
}
58+
59+
// Validate flags.
60+
required := []struct {
61+
name, val string
62+
}{
63+
{"--node-pool-name", nodePoolOpts.PoolName},
64+
}
65+
var missing []string
66+
for _, req := range required {
67+
if req.val == "" {
68+
missing = append(missing, strconv.Quote(req.name))
69+
}
70+
}
71+
if len(missing) != 0 {
72+
return fmt.Errorf("Missing required flag(s): %s", strings.Join(missing, ", "))
73+
}
74+
75+
// Write all assets to disk.
76+
files := []struct {
77+
name string
78+
data []byte
79+
mode os.FileMode
80+
}{
81+
{stackTemplateOptions().WorkerTmplFile, cfg.CloudConfigWorker, 0644},
82+
{stackTemplateOptions().StackTemplateTmplFile, config.StackTemplateTemplate, 0644},
83+
}
84+
for _, file := range files {
85+
if err := os.MkdirAll(path.Dir(file.name), 0755); err != nil {
86+
return err
87+
}
88+
89+
if err := ioutil.WriteFile(file.name, file.data, file.mode); err != nil {
90+
return err
91+
}
92+
}
93+
94+
successMsg :=
95+
`Success! Stack rendered to nodepools/%s/stack-template.json.
96+
97+
Next steps:
98+
1. (Optional) Validate your changes to %s with "kube-aws nodepool validate --pool-name %s"
99+
2. (Optional) Further customize the cluster by modifying stack-template.json or files in ./userdata.
100+
3. Start the cluster with "kube-aws up".
101+
`
102+
103+
fmt.Printf(successMsg, nodePoolOpts.PoolName, nodePoolClusterConfigFilePath(), nodePoolOpts.PoolName)
104+
return nil
105+
}

cmd/nodepool/root.go

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package nodepool
2+
3+
import (
4+
"fmt"
5+
"github.com/coreos/kube-aws/nodepool/config"
6+
"github.com/spf13/cobra"
7+
)
8+
9+
var (
10+
NodePoolCmd = &cobra.Command{
11+
Use: "node-pools",
12+
Short: "Manage node pools",
13+
Long: ``,
14+
}
15+
16+
nodePoolOpts = config.Ref{}
17+
)
18+
19+
func clusterConfigFilePath() string {
20+
return "cluster.yaml"
21+
}
22+
23+
func nodePoolConfigDirPath() string {
24+
return fmt.Sprintf("node-pools/%s", nodePoolOpts.PoolName)
25+
}
26+
27+
func nodePoolClusterConfigFilePath() string {
28+
return fmt.Sprintf("%s/cluster.yaml", nodePoolConfigDirPath())
29+
}
30+
31+
func nodePoolExportedStackTemplatePath() string {
32+
return fmt.Sprintf("%s/%s.stack-template.json", nodePoolConfigDirPath(), nodePoolOpts.PoolName)
33+
}
34+
35+
func stackTemplateOptions() config.StackTemplateOptions {
36+
return config.StackTemplateOptions{
37+
TLSAssetsDir: "credentials",
38+
WorkerTmplFile: fmt.Sprintf("%s/userdata/cloud-config-worker", nodePoolConfigDirPath(), nodePoolOpts.PoolName),
39+
StackTemplateTmplFile: fmt.Sprintf("%s/stack-template.json", nodePoolConfigDirPath()),
40+
}
41+
}
42+
43+
func init() {
44+
NodePoolCmd.PersistentFlags().StringVar(&nodePoolOpts.PoolName, "node-pool-name", "", "The name of this node pool. This will be the name of the cloudformation stack")
45+
}

cmd/nodepool/up.go

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package nodepool
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/coreos/kube-aws/nodepool/cluster"
7+
"github.com/coreos/kube-aws/nodepool/config"
8+
"github.com/spf13/cobra"
9+
"io/ioutil"
10+
)
11+
12+
var (
13+
cmdUp = &cobra.Command{
14+
Use: "up",
15+
Short: "Create a new Kubernetes cluster",
16+
Long: ``,
17+
RunE: runCmdUp,
18+
SilenceUsage: true,
19+
}
20+
21+
upOpts = struct {
22+
awsDebug, export bool
23+
s3URI string
24+
}{}
25+
)
26+
27+
func init() {
28+
NodePoolCmd.AddCommand(cmdUp)
29+
cmdUp.Flags().BoolVar(&upOpts.export, "export", false, "Don't create cluster, instead export cloudformation stack file")
30+
cmdUp.Flags().BoolVar(&upOpts.awsDebug, "aws-debug", false, "Log debug information from aws-sdk-go library")
31+
cmdUp.Flags().StringVar(&upOpts.s3URI, "s3-uri", "", "When your template is bigger than the cloudformation limit of 51200 bytes, upload the template to the specified location in S3")
32+
}
33+
34+
func runCmdUp(cmd *cobra.Command, args []string) error {
35+
conf, err := config.ClusterFromFile(nodePoolClusterConfigFilePath())
36+
if err != nil {
37+
return fmt.Errorf("Failed to read cluster config: %v", err)
38+
}
39+
40+
if err := conf.ValidateUserData(stackTemplateOptions()); err != nil {
41+
return fmt.Errorf("Failed to validate user data: %v", err)
42+
}
43+
44+
data, err := conf.RenderStackTemplate(stackTemplateOptions())
45+
if err != nil {
46+
return fmt.Errorf("Failed to render stack template: %v", err)
47+
}
48+
49+
if upOpts.export {
50+
templatePath := nodePoolExportedStackTemplatePath()
51+
fmt.Printf("Exporting %s\n", templatePath)
52+
if err := ioutil.WriteFile(templatePath, data, 0600); err != nil {
53+
return fmt.Errorf("Error writing %s : %v", templatePath, err)
54+
}
55+
if conf.KMSKeyARN == "" {
56+
fmt.Printf("BEWARE: %s contains your TLS secrets!\n", templatePath)
57+
}
58+
return nil
59+
}
60+
61+
cluster := cluster.New(conf, upOpts.awsDebug)
62+
fmt.Printf("Creating AWS resources. This should take around 5 minutes.\n")
63+
if err := cluster.Create(string(data), upOpts.s3URI); err != nil {
64+
return fmt.Errorf("Error creating cluster: %v", err)
65+
}
66+
67+
info, err := cluster.Info()
68+
if err != nil {
69+
return fmt.Errorf("Failed fetching cluster info: %v", err)
70+
}
71+
72+
successMsg :=
73+
`Success! Your AWS resources have been created:
74+
%s
75+
The containers that power your cluster are now being downloaded.
76+
77+
You should be able to access the Kubernetes API once the containers finish downloading.
78+
`
79+
fmt.Printf(successMsg, info.String())
80+
81+
return nil
82+
}

0 commit comments

Comments
 (0)