eks_cli 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,118 @@
1
+ require 'active_support/core_ext/hash'
2
+ require 'config'
3
+ require 'cloudformation/stack'
4
+ require 'iam/client'
5
+ require 'k8s/auth'
6
+ require 'log'
7
+
8
+ module EksCli
9
+ class NodeGroup
10
+
11
+ T = {cluster_name: "ClusterName",
12
+ control_plane_sg_id: "ClusterControlPlaneSecurityGroup",
13
+ nodes_sg_id: "ClusterSecurityGroup",
14
+ min: "NodeAutoScalingGroupMinSize",
15
+ max: "NodeAutoScalingGroupMaxSize",
16
+ instance_type: "NodeInstanceType",
17
+ ami: "NodeImageId",
18
+ volume_size: "NodeVolumeSize",
19
+ ssh_key_name: "KeyName",
20
+ vpc_id: "VpcId",
21
+ subnets: "Subnets",
22
+ group_name: "NodeGroupName",
23
+ bootstrap_args: "BootstrapArguments"}
24
+
25
+ CAPABILITIES = ["CAPABILITY_IAM"]
26
+
27
+ def initialize(cluster_name, name)
28
+ @cluster_name = cluster_name
29
+ @group = Config[cluster_name].for_group(name)
30
+ end
31
+
32
+ def create(wait_for_completion: true)
33
+ Log.info "creating stack for nodegroup #{@group["group_name"]}"
34
+ stack = CloudFormation::Stack.create(@cluster_name, cloudformation_config)
35
+ Log.info "stack created - #{@group["group_name"]} - #{stack.id}"
36
+ if wait_for_completion
37
+ await(stack)
38
+ end
39
+ stack
40
+ end
41
+
42
+ def tags
43
+ [{key: "eks-nodegroup", value: @group["group_name"]},
44
+ {key: "eks-cluster", value: @cluster_name}]
45
+ end
46
+
47
+ def detach_iam_policies
48
+ IAM::Client.new(@cluster_name).detach_node_policies(cf_stack.node_instance_role_name)
49
+ end
50
+
51
+ def delete
52
+ detach_iam_policies
53
+ cf_stack.delete
54
+ end
55
+
56
+ private
57
+
58
+ def cf_template_body
59
+ @cf_template_body ||= File.read(File.join($root_dir, '/assets/nodegroup_cf_template.yaml'))
60
+ end
61
+
62
+ def cf_stack
63
+ CloudFormation::Stack.find(@cluster_name, stack_name)
64
+ end
65
+
66
+ def await(stack)
67
+
68
+ while stack.pending? do
69
+ Log.info "waiting for stack #{stack.id} - status is #{stack.status}"
70
+ sleep 10
71
+ end
72
+
73
+ Log.info "stack completed with status #{stack.status}"
74
+
75
+ K8s::Auth.new(@cluster_name).update
76
+ IAM::Client.new(@cluster_name).attach_node_policies(stack.node_instance_role_name)
77
+ end
78
+
79
+ def cloudformation_config
80
+ {stack_name: stack_name,
81
+ template_body: cf_template_body,
82
+ parameters: build_params,
83
+ capabilities: CAPABILITIES,
84
+ tags: tags}
85
+ end
86
+
87
+ def stack_name
88
+ "#{@group["cluster_name"]}-Workers-#{@group["group_name"]}"
89
+ end
90
+
91
+ def build_params
92
+ @group["bootstrap_args"] = bootstrap_args
93
+ @group.except("taints").inject([]) do |params, (k, v)|
94
+ params << build_param(k, v)
95
+ end
96
+ end
97
+
98
+ def bootstrap_args
99
+ flags = "--node-labels=kubernetes.io/role=node,eks/node-group=#{@group["group_name"].downcase}"
100
+ if taints = @group["taints"]
101
+ flags = "#{flags} --register-with-taints=#{taints}"
102
+ end
103
+ "--kubelet-extra-args \"#{flags}\""
104
+ end
105
+
106
+ def add_bootstrap_args(group)
107
+ group["bootstrap_args"] = base
108
+ group.except("taints")
109
+ end
110
+
111
+ def build_param(k, v)
112
+ {parameter_key: T[k.to_sym],
113
+ parameter_value: v.to_s}
114
+ end
115
+
116
+ end
117
+
118
+ end
@@ -0,0 +1,67 @@
1
+ require 'aws-sdk-route53'
2
+ require 'log'
3
+ require 'config'
4
+ require 'k8s/client'
5
+ module EksCli
6
+ module Route53
7
+ class Client
8
+
9
+ def initialize(cluster_name)
10
+ @cluster_name = cluster_name
11
+ end
12
+
13
+ def update_dns(hostname, k8s_service_name, route53_hosted_zone_id, elb_hosted_zone_id)
14
+ change_dns_target(hostname, k8s.get_elb(k8s_service_name), route53_hosted_zone_id, elb_hosted_zone_id)
15
+ end
16
+
17
+ private
18
+
19
+ def k8s
20
+ @k8s ||= K8s::Client.new(@cluster_name)
21
+ end
22
+
23
+ def change_dns_target(route53_host, elb_host, route53_hosted_zone_id, elb_hosted_zone_id)
24
+ Log.info "Setting Route53 record #{route53_host} --> #{elb_host}"
25
+ resp = client.change_resource_record_sets({
26
+ change_batch: {
27
+ changes: [
28
+ {
29
+ action: "UPSERT",
30
+ resource_record_set: {
31
+ name: route53_host,
32
+ type: "A",
33
+ alias_target: {
34
+ dns_name: elb_host,
35
+ evaluate_target_health: false,
36
+ hosted_zone_id: elb_hosted_zone_id,
37
+ },
38
+ },
39
+ },
40
+ ],
41
+ },
42
+ hosted_zone_id: route53_hosted_zone_id,
43
+ })
44
+ Log.info "Done: #{resp}"
45
+ end
46
+
47
+ def config
48
+ @config ||= Config[@cluster_name]
49
+ end
50
+
51
+ def elb_hosted_zone_id
52
+ config["elb_hosted_zone_id"]
53
+ end
54
+
55
+ def route53_hosted_zone_id
56
+ config["route53_hosted_zone_id"]
57
+ end
58
+
59
+ def client
60
+ @client ||= Aws::Route53::Client.new(region: config["region"])
61
+ end
62
+
63
+
64
+ end
65
+ end
66
+
67
+ end
@@ -0,0 +1,112 @@
1
+ require 'aws-sdk-ec2'
2
+ require 'config'
3
+ require 'log'
4
+
5
+ module EksCli
6
+ module VPC
7
+ class Client
8
+ def initialize(cluster_name)
9
+ @cluster_name = cluster_name
10
+ end
11
+
12
+ def set_inter_vpc_networking(old_vpc_id, old_vpc_sg_id)
13
+ @old_vpc = vpc_by_id(old_vpc_id)
14
+ Log.info "setting vpc networking between #{new_vpc.id} and #{old_vpc.id}"
15
+ peering_connection_id = create_vpc_peering_connection
16
+ update_route_tables(peering_connection_id)
17
+ allow_networking(old_vpc_sg_id, peering_connection_id)
18
+ end
19
+
20
+ def create_vpc_peering_connection
21
+ Log.info "creating VPC peering request between #{new_vpc.id} and #{old_vpc.id}"
22
+ pcr = client.create_vpc_peering_connection({
23
+ dry_run: false,
24
+ peer_vpc_id: old_vpc.id,
25
+ vpc_id: new_vpc.id,
26
+ })
27
+ Log.info "created peering request #{pcr}"
28
+ peering_connection_id = pcr.vpc_peering_connection.vpc_peering_connection_id
29
+ Log.info "accepting peering request"
30
+ res = client.accept_vpc_peering_connection({
31
+ dry_run: false,
32
+ vpc_peering_connection_id: peering_connection_id,
33
+ })
34
+ Log.info "request accepted: #{res}"
35
+ return peering_connection_id
36
+ end
37
+
38
+ def update_route_tables(peering_connection_id)
39
+ Log.info "updating route tables"
40
+ point_from(old_vpc, new_vpc, peering_connection_id)
41
+ point_from(new_vpc, old_vpc, peering_connection_id)
42
+ Log.info "done updating route tables"
43
+ end
44
+
45
+ def allow_networking(old_vpc_sg_id, peering_connection_id)
46
+ Log.info "allowing incoming traffic to sg #{old_vpc_sg_id} from #{config["nodes_sg_id"]} on vpc #{new_vpc.id}"
47
+ old_sg = Aws::EC2::SecurityGroup.new(old_vpc_sg_id, client: client)
48
+ res = old_sg.authorize_ingress(
49
+ ip_permissions: [
50
+ {
51
+ from_port: "-1",
52
+ ip_protocol: "-1",
53
+ to_port: "-1",
54
+ user_id_group_pairs: [
55
+ {
56
+ description: "Accept all traffic from new EKS cluster VPC",
57
+ group_id: config["nodes_sg_id"],
58
+ vpc_id: new_vpc.id,
59
+ vpc_peering_connection_id: peering_connection_id,
60
+ },
61
+ ],
62
+ },
63
+ ]
64
+ )
65
+ Log.info "done setting networking (#{res})"
66
+ end
67
+
68
+ def point_from(from_vpc, to_vpc, peering_connection_id)
69
+ Log.info "pointing from #{from_vpc.id} to #{to_vpc.id} via #{peering_connection_id}"
70
+ from_vpc.route_tables.each do |rt|
71
+ res = client.create_route({
72
+ destination_cidr_block: to_vpc.cidr_block,
73
+ gateway_id: peering_connection_id,
74
+ route_table_id: rt.id,
75
+ })
76
+ Log.info "set route #{res}"
77
+ end
78
+
79
+ end
80
+
81
+ def new_vpc
82
+ @new_vpc ||= vpc_by_id(new_vpc_id)
83
+ end
84
+
85
+ def old_vpc
86
+ @old_vpc
87
+ end
88
+
89
+ def vpc_by_id(id)
90
+ Aws::EC2::Vpc.new(id, client: client)
91
+ end
92
+
93
+ def config
94
+ @config ||= Config[@cluster_name]
95
+ end
96
+
97
+ def client
98
+ @client ||= Aws::EC2::Client.new(region: config["region"])
99
+ end
100
+
101
+ def new_vpc_id
102
+ @new_vpc_id ||= config["vpc_id"]
103
+ end
104
+
105
+ def old_vpc_id
106
+ @old_vpc_id
107
+ end
108
+
109
+ end
110
+ end
111
+
112
+ end
data/lib/eks_cli.rb ADDED
@@ -0,0 +1,5 @@
1
+ $root_dir = File.expand_path(File.dirname(path = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__))
2
+ helpers_dir = "#{$root_dir}/eks_cli"
3
+ $LOAD_PATH.unshift(helpers_dir) unless $LOAD_PATH.include?(helpers_dir)
4
+
5
+ require 'cli'
metadata ADDED
@@ -0,0 +1,183 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eks_cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Erez Rabih
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-11-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-sdk-iam
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: aws-sdk-eks
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: aws-sdk-ec2
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: aws-sdk-cloudformation
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: aws-sdk-route53
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: activesupport
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: kubeclient
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: A utility to manage and create EKS (Kubernetes) cluster on Amazon Web
126
+ Services
127
+ email: erez.rabih@gmail.com
128
+ executables:
129
+ - eks
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - ".gitignore"
134
+ - Gemfile
135
+ - Gemfile.lock
136
+ - README.md
137
+ - bin/eks
138
+ - eks_cli.gemspec
139
+ - lib/assets/default_storage_class.yaml
140
+ - lib/assets/nodegroup_cf_template.yaml
141
+ - lib/assets/nvidia_device_plugin.yaml
142
+ - lib/eks_cli.rb
143
+ - lib/eks_cli/cli.rb
144
+ - lib/eks_cli/cloudformation/client.rb
145
+ - lib/eks_cli/cloudformation/stack.rb
146
+ - lib/eks_cli/cloudformation/vpc.rb
147
+ - lib/eks_cli/config.rb
148
+ - lib/eks_cli/ec2/security_group.rb
149
+ - lib/eks_cli/eks/client.rb
150
+ - lib/eks_cli/eks/cluster.rb
151
+ - lib/eks_cli/iam/client.rb
152
+ - lib/eks_cli/k8s/auth.rb
153
+ - lib/eks_cli/k8s/client.rb
154
+ - lib/eks_cli/k8s/configmap_builder.rb
155
+ - lib/eks_cli/log.rb
156
+ - lib/eks_cli/nodegroup.rb
157
+ - lib/eks_cli/route53/client.rb
158
+ - lib/eks_cli/vpc/client.rb
159
+ homepage: https://github.com/nanit/eks_cli
160
+ licenses:
161
+ - MIT
162
+ metadata: {}
163
+ post_install_message:
164
+ rdoc_options: []
165
+ require_paths:
166
+ - lib
167
+ required_ruby_version: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ required_rubygems_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: '0'
177
+ requirements: []
178
+ rubyforge_project:
179
+ rubygems_version: 2.5.1
180
+ signing_key:
181
+ specification_version: 4
182
+ summary: Make EKS great again!
183
+ test_files: []