hub-clusters-creator 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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