hub-clusters-creator 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2019 Rohith Jayawardene <gambol99@gmail.com>
4
+ #
5
+ # This program is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU General Public License
7
+ # as published by the Free Software Foundation; either version 2
8
+ # of the License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+
19
+ # rubocop:disable Metrics/LineLength,Metrics/MethodLength
20
+ module HubClustersCreator
21
+ module Providers
22
+ # Azure is the AKS namespace
23
+ module Azure
24
+ # Helpers providers a collection of useful methods
25
+ module Helpers
26
+ private
27
+
28
+ def wait(interval: 20, timeout: 900, max_retries: 10)
29
+ max_attempts = timeout / interval
30
+ retries = attempts = 0
31
+
32
+ while attempts < max_attempts
33
+ begin
34
+ return if yield
35
+ rescue StandardError => e
36
+ raise Exception, "failed waiting for resource, retry: #{retries}, error: #{e}" if retries > max_retries
37
+
38
+ retries += 1
39
+ end
40
+ sleep(interval)
41
+ attempts += 1
42
+ end
43
+ end
44
+
45
+ def hostname(fqdn)
46
+ fqdn.split('.').first
47
+ end
48
+
49
+ def dns(src, dest, zone, ttl: 120)
50
+ raise ArgumentError, "the domain: #{zone} does not exist" unless domain?(zone)
51
+
52
+ zone = domain(zone)
53
+ resource_group = zone.id.split('/')[4]
54
+ address = Azure::Dns::Mgmt::V2017_10_01::Models::ARecord.new
55
+ address.ipv4address = dest
56
+ record = Azure::Dns::Mgmt::V2017_10_01::Models::RecordSet.new
57
+ record.name = src
58
+ record.ttl = ttl
59
+ record.arecords = [address]
60
+
61
+ @dns.record_sets.create_or_update(resource_group, zone, src, 'A', record)
62
+ end
63
+
64
+ def domain?(name)
65
+ domains.map(&:name).include?(name)
66
+ end
67
+
68
+ def domains
69
+ @dns.zones.list
70
+ end
71
+
72
+ def deployment(group, name)
73
+ deployments(group).select { |x| x.name == name }.first
74
+ end
75
+
76
+ def deployment?(name, group)
77
+ deployments(group).map(&:name).include?(name)
78
+ end
79
+
80
+ def deployments(group)
81
+ @client.deployments.list_by_resource_group(group)
82
+ end
83
+
84
+ def managed_cluster?(name)
85
+ managed_clusters.map(&:name).include?(name)
86
+ end
87
+
88
+ def managed_clusters
89
+ @containers.managed_clusters.list
90
+ end
91
+
92
+ # resource_group? check is the resource group exists
93
+ def resource_group?(name)
94
+ resource_groups.map(&:name).include?(name)
95
+ end
96
+
97
+ # resource_groups returns a list of resource groups
98
+ def resource_groups
99
+ @client.resource_groups.list
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ # rubocop:enable Metrics/LineLength,Metrics/MethodLength
@@ -0,0 +1,125 @@
1
+ ---
2
+ type: object
3
+ title: Azure AKS Provider configuration
4
+ description: >
5
+ Defines the configuration required to initialize the provider.
6
+ required:
7
+ - client_id
8
+ - client_secret
9
+ - region
10
+ - subscription
11
+ - tenant
12
+
13
+ properties:
14
+ client_id:
15
+ $id: '#/config/client_id'
16
+ type: string
17
+ title: Service Principal Client ID
18
+ description: >
19
+ The associated client id from the service principal account you are
20
+ using to speak to the Azure API.
21
+ default: ''
22
+ pattern: ^.*$
23
+
24
+ client_secret:
25
+ $id: '#/config/client_secret'
26
+ type: string
27
+ title: Service Principal Client Secret
28
+ description: >
29
+ The client secret of the service principal being used to provision
30
+ the resources with.
31
+ default: ''
32
+ pattern: ^.*$
33
+
34
+ region:
35
+ $id: '#/config/region'
36
+ type: string
37
+ title: Azure Compute Region
38
+ description: >
39
+ The geographical region which you wish to build the cluster with.
40
+ default: ''
41
+ examples:
42
+ - uksouth
43
+ pattern: ^.*$
44
+
45
+ subscription:
46
+ $id: '#/config/subscription'
47
+ type: string
48
+ title: Subsription
49
+ description: >
50
+ The Azure client subscription you are using to provison the resources
51
+ under.
52
+ default: ''
53
+ pattern: ^.*$
54
+
55
+ tenant:
56
+ $id: '#/config/tenant'
57
+ type: string
58
+ title: Tenant ID
59
+ description: >
60
+ The application tenant id from the application registration / service
61
+ principal you are using. This can be found under the Application
62
+ Registration tab in the Azure portal
63
+ default: ''
64
+ pattern: ^.*$
65
+
66
+ ---
67
+ type: object
68
+ title: Azure AKS Cluster configuration
69
+ description: >
70
+ Create an Azure AKS managed kubernetes cluster
71
+ required:
72
+ - machine_type
73
+ - services_ipv4_cidr
74
+ - ssh_key
75
+ - version
76
+
77
+ properties:
78
+ machine_type:
79
+ $id: '#/properties/machine_type'
80
+ tag: default
81
+ type: string
82
+ title: Machine Type
83
+ description: >
84
+ The machine type which the default nodes pool should use.
85
+ examples:
86
+ - Standard_DS2_v2
87
+ pattern: ^(.*)$
88
+
89
+ services_ipv4_cidr:
90
+ $id: '#/properties/services_ipv4_cidr'
91
+ tag: advanced
92
+ type: string
93
+ title: Cluster Services CIDR
94
+ default: ''
95
+ description: >
96
+ An optional network cidr configured for the cluster services,
97
+ otherwise GCP will decide.
98
+ examples:
99
+ - '10.0.0.0/16'
100
+ pattern: ^(([\d]{1,3}\.){3}[\d]{1,3}\/[\d]{1,2}|)$
101
+
102
+ ssh_key:
103
+ $id: '#/properties/ssh_key'
104
+ tag: default
105
+ type: string
106
+ title: SSH Public Key
107
+ default: ''
108
+ description: >
109
+ A public ssh key used provision the compute nodes with
110
+ examples:
111
+ - ssh-rsa
112
+ pattern: ^ssh-rsa.*$
113
+
114
+ version:
115
+ $id: '#/properties/version'
116
+ tag: default
117
+ type: string
118
+ title: Initial Kubernetes Version
119
+ default: '1.14.3'
120
+ description: >
121
+ The initial kubernetes version which the cluster should be
122
+ configured with.
123
+ examples:
124
+ - 1.14.3
125
+ pattern: ^(.*)$
@@ -0,0 +1,226 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2019 Rohith Jayawardene <gambol99@gmail.com>
4
+ #
5
+ # This program is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU General Public License
7
+ # as published by the Free Software Foundation; either version 2
8
+ # of the License, or (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+ require 'hub-clusters-creator/template'
19
+ require 'hub-clusters-creator/logging'
20
+
21
+ # rubocop:disable Metrics/MethodLength,Metrics/LineLength
22
+ module HubClustersCreator
23
+ module Providers
24
+ # Bootstrap the provider of the bootstrap job
25
+ # rubocop:disable Metrics/ClassLength
26
+ class Bootstrap
27
+ include Logging
28
+ include HubClustersCreator::Utils::Template
29
+
30
+ DEFAULT_CLUSTER_ADMIN_ROLE = <<~YAML
31
+ apiVersion: v1
32
+ kind: ServiceAccount
33
+ metadata:
34
+ name: sysadmin
35
+ namespace: kube-system
36
+ YAML
37
+
38
+ DEFAULT_CLUSTER_ADMIN_BINDING = <<~YAML
39
+ apiVersion: rbac.authorization.k8s.io/v1
40
+ kind: ClusterRoleBinding
41
+ metadata:
42
+ name: cluster:admin
43
+ roleRef:
44
+ apiGroup: rbac.authorization.k8s.io
45
+ kind: ClusterRole
46
+ name: cluster-admin
47
+ subjects:
48
+ - kind: ServiceAccount
49
+ name: sysadmin
50
+ namespace: kube-system
51
+ YAML
52
+
53
+ # is the name of the container image
54
+ BOOTSTRAP_IMAGE = 'quay.io/appvia/hub-bootstrap:latest'
55
+ # is the name of the job
56
+ BOOTSTRAP_NAME = 'bootstrap'
57
+ # is the name of the namespace the job lives in
58
+ BOOTSTRAP_NAMESPACE = 'kube-system'
59
+
60
+ attr_accessor :client, :config, :name
61
+
62
+ def initialize(name, client, config)
63
+ @name = name
64
+ @client = client
65
+ @config = config
66
+ end
67
+
68
+ # provision_bootstrap is responsible for setting up the agents and strapper
69
+ # a) pushes in the configuration for the bootstrapper
70
+ # b) rolls out the kubernetes job to bootstrap the cluster
71
+ # c) grabs the services and provisions the dns
72
+ # rubocop:disable Metrics/AbcSize
73
+ def bootstrap(image = BOOTSTRAP_IMAGE)
74
+ client.wait_for_kubeapi
75
+
76
+ info 'applying the default cluster admin service account and role'
77
+ client.kubectl(DEFAULT_CLUSTER_ADMIN_ROLE)
78
+ client.kubectl(DEFAULT_CLUSTER_ADMIN_BINDING)
79
+
80
+ info 'attempting to bootstrap the cluster configuration'
81
+ client.kubectl(generate_bootstrap_config)
82
+ client.kubectl(generate_bootstrap_job(image))
83
+
84
+ info 'waiting for the bootstrap to complete successfully'
85
+ name = BOOTSTRAP_NAME
86
+ namespace = BOOTSTRAP_NAMESPACE
87
+
88
+ client.wait(name, namespace, 'jobs', version: 'batch/v1', interval: 10, timeout: 500) do |x|
89
+ x.status.nil? || x.status['succeeded'] <= 0 ? false : true
90
+ end
91
+ info 'bootstrap has successfully completed'
92
+
93
+ info 'waiting for grafana ingress load balancer to be provisioned'
94
+ # @step: wait for the ingress to appaar and provision and grab the address
95
+ @client.wait('loki-grafana', 'loki', 'ingresses', version: 'extensions/v1beta1') do |x|
96
+ x.status.loadBalancer.ingress.empty? ? false : true
97
+ end
98
+ end
99
+ # rubocop:enable Metrics/AbcSize
100
+
101
+ private
102
+
103
+ # generate_bootstrap_config returns the helm values for grafana
104
+ def generate_bootstrap_config
105
+ template = <<~YAML
106
+ apiVersion: v1
107
+ kind: ConfigMap
108
+ metadata:
109
+ name: #{BOOTSTRAP_NAME}
110
+ namespace: #{BOOTSTRAP_NAMESPACE}
111
+ data:
112
+ charts: |
113
+ loki/loki-stack,loki,--name loki --values /config/bundles/grafana.yaml
114
+ stable/prometheus,kube-system,--name prometheus
115
+ repositories: |
116
+ loki,https://grafana.github.io/loki/charts
117
+ grafana.yaml: |
118
+ loki:
119
+ enabled: true
120
+ networkPolicy:
121
+ enabled: true
122
+ promtail:
123
+ enabled: true
124
+ prometheus:
125
+ enabled: false
126
+ server:
127
+ fullnameOverride: prometheus-server
128
+ nodeExporter:
129
+ podSecurityPolicy:
130
+ enabled: true
131
+ networkPolicy:
132
+ enabled: true
133
+ grafana:
134
+ enabled: true
135
+ image:
136
+ repository: grafana/grafana
137
+ tag: <%= context[:grafana_version] || 'latest' %>
138
+ pullPolicy: IfNotPresent
139
+ sidecar:
140
+ datasources:
141
+ enabled: true
142
+ service:
143
+ type: NodePort
144
+ port: 80
145
+ targetPort: 3000
146
+ ingress:
147
+ enabled: true
148
+ hosts:
149
+ - <%= context[:grafana_hostname] %>
150
+ path: '/*'
151
+ networkPolicy:
152
+ enabled: true
153
+ persistence:
154
+ enabled: false
155
+ accessModes:
156
+ - ReadWriteOnce
157
+ size: <%= context[:grafana_disk_size] %><%= context[:grafana_disk_size].to_s.end_with?('Gi') ? '' : 'Gi' %>
158
+ grafana.ini:
159
+ server:
160
+ domain: <%= context[:grafana_hostname] %>
161
+ root_url: http://<%= context[:grafana_hostname] %>
162
+ paths:
163
+ data: /var/lib/grafana/data
164
+ logs: /var/log/grafana
165
+ plugins: /var/lib/grafana/plugins
166
+ provisioning: /etc/grafana/provisioning
167
+ analytics:
168
+ check_for_updates: true
169
+ log:
170
+ mode: console
171
+ grafana_net:
172
+ url: https://grafana.net
173
+ <%- unless (context[:github_client_id] || '').empty? -%>
174
+ auth.github:
175
+ allow_sign_up: true
176
+ <%- unless (context[:github_organization] || '').empty? %>
177
+ allowed_organizations: <%= context[:github_organization] %>
178
+ <%- end %>
179
+ api_url: https://api.github.com/user
180
+ auth_url: https://github.com/login/oauth/authorize
181
+ client_id: <%= context[:github_client_id] %>
182
+ client_secret: <%= context[:github_client_secret] %>
183
+ enabled: true
184
+ scopes: user,read:org
185
+ token_url: https://github.com/login/oauth/access_token
186
+ <%- end -%>
187
+ YAML
188
+ HubClustersCreator::Utils::Template::Render.new(config).render(template)
189
+ end
190
+
191
+ # generate_bootstrap_job is responsible for generating the bootstrap job
192
+ def generate_bootstrap_job(image)
193
+ template = <<-YAML
194
+ apiVersion: batch/v1
195
+ kind: Job
196
+ metadata:
197
+ name: #{BOOTSTRAP_NAME}
198
+ namespace: #{BOOTSTRAP_NAMESPACE}
199
+ spec:
200
+ backoffLimit: 20
201
+ template:
202
+ spec:
203
+ serviceAccountName: sysadmin
204
+ restartPolicy: OnFailure
205
+ containers:
206
+ - name: bootstrap
207
+ image: #{image}
208
+ imagePullPolicy: Always
209
+ env:
210
+ - name: CONFIG_DIR
211
+ value: /config
212
+ volumeMounts:
213
+ - name: bundle
214
+ mountPath: /config/bundles
215
+ volumes:
216
+ - name: bundle
217
+ configMap:
218
+ name: #{BOOTSTRAP_NAME}
219
+ YAML
220
+ template
221
+ end
222
+ end
223
+ # rubocop:enable Metrics/ClassLength
224
+ end
225
+ end
226
+ # rubocop:enable Metrics/MethodLength,Metrics/LineLength