# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. Namespaces: =: com.mirantis.docker.kubernetes std: io.murano res: io.murano.resources sys: io.murano.system docker: com.mirantis.docker Name: KubernetesCluster Extends: std:Application Properties: name: Contract: $.string().notNull() masterNode: Contract: $.class(KubernetesMasterNode).notNull() minionNodes: Contract: - $.class(KubernetesMinionNode).notNull() - 1 nodeCount: Contract: $.int().notNull().check($ > 0) Usage: InOut gatewayCount: Contract: $.int().notNull().check($ > 0) Usage: InOut gatewayNodes: Contract: - $.class(KubernetesGatewayNode).notNull() - 1 useFlannel: Contract: $.bool().notNull() Default: false enableKubeDns: Contract: $.bool().notNull() Default: true enableDashboard: Contract: $.bool().notNull() Default: true dockerRegistry: Contract: $.string() dockerMirror: Contract: $.string() gcloudKey: Contract: $.string() serviceEndpoints: Contract: - port: $.int().notNull().check($ > 0) address: $.string().notNull() scope: $.string().notNull().check($ in list(public, cloud, internal, host)) portScope: $.string().notNull().check($ in list(public, cloud, internal, host)) containerPort: $.int().notNull().check($ > 0) protocol: $.string().notNull().check($ in list(TCP, UDP)) applicationName: $.string().notNull() podId: $.string().notNull() serviceName: $.string() Default: [] Usage: Out Methods: .init: Body: - $._environment: $.find(std:Environment).require() isAvailable: Body: - Return: $.masterNode.isAvailable() deploy: Body: - $.serviceEndpoints: $.getAttr(serviceEndpoints, list()) - If: not $.getAttr(deployed, false) Then: - $._environment.reporter.report($this, 'Creating VMs for Kubernetes cluster') - $securityGroupIngress: - ToPort: 4001 FromPort: 4001 IpProtocol: tcp External: false - ToPort: 7001 FromPort: 7001 IpProtocol: tcp External: false - ToPort: 10250 FromPort: 10250 IpProtocol: tcp External: false - ToPort: 8080 FromPort: 8080 IpProtocol: tcp External: $.masterNode.instance.assignFloatingIp - ToPort: 2380 FromPort: 2380 IpProtocol: tcp External: false - ToPort: 8285 FromPort: 8285 IpProtocol: udp External: false - $._environment.securityGroupManager.addGroupIngress($securityGroupIngress) - $.setAttr(deployed, true) - $prevNodeCount: $.getAttr(lastNodeCount, 0) - $prevGatewayCount: $.getAttr(lastGatewayCount, 0) - If: $prevNodeCount != $.nodeCount or $prevGatewayCount != $.gatewayCount Then: - $._environment.reporter.report($this, 'Setting up Kubernetes cluster') - Parallel: - $.masterNode.deployInstance() - $.minionNodes.take($.nodeCount).pselect($.deployInstance()) - $.gatewayNodes.take($.gatewayCount).pselect($.deployInstance()) - $.masterNode.setupEtcd() - $.minionNodes.skip($.nodeCount).select($.removeFromCluster()) - $.gatewayNodes.skip($.gatewayCount).select($.removeFromCluster()) - $.minionNodes.take($.nodeCount).select($.setupEtcd()) - $.gatewayNodes.take($.gatewayCount).select($.setupEtcd()) - $._deployContainersNetwork() - $.masterNode.setupNode() - Parallel: - $.minionNodes.take($.nodeCount).pselect($.setupNode()) - $.gatewayNodes.take($.gatewayCount).pselect($.setupNode()) - If: $.enableKubeDns Then: $._deployDns() - If: $.enableDashboard Then: - $._deployDashboard() - If: $.gatewayCount > 0 Then: - $msg: $.gatewayNodes.take($.gatewayCount).select('http://{0}:{1}'.format($.getIp(true), 9090)).join(', ') - $._environment.reporter.report($this, 'Dashboard UI is available at {0}'.format($msg)) Else: - $._environment.reporter.report($this, 'Dashboard UI is installed but is not accessible from outside') - $._environment.stack.push() - $._updateServicePublicIps() - $.setAttr(lastNodeCount, $.nodeCount) - $.setAttr(lastGatewayCount, $.gatewayCount) - $._environment.reporter.report($this, 'Kubernetes cluster is up and running') - $.setAttr(serviceEndpoints, $.serviceEndpoints) getIp: Body: Return: $.masterNode.getIp() _deployDashboard: Body: - If: not $.getAttr(uiDeployed, false) Then: - $securityGroupIngress: - ToPort: 9090 FromPort: 9090 IpProtocol: tcp External: $.masterNode.instance.assignFloatingIp - $._environment.securityGroupManager.addGroupIngress($securityGroupIngress) - $resources: new(sys:Resources) - $template: $resources.yaml('DeployDashboard.template').bind(dict(ip => $.getIp())) - $.masterNode.instance.agent.call($template, $resources) - $.setAttr(uiDeployed, true) _deployDns: Body: - If: not $.getAttr(dnsDeployed, false) Then: - $securityGroupIngress: - ToPort: 8001 FromPort: 8001 IpProtocol: tcp External: false - ToPort: 10053 FromPort: 10053 IpProtocol: udp External: false - ToPort: 10053 FromPort: 10053 IpProtocol: tcp External: false - ToPort: 53 FromPort: 53 IpProtocol: udp External: false - ToPort: 53 FromPort: 53 IpProtocol: tcp External: false - $._environment.securityGroupManager.addGroupIngress($securityGroupIngress) - $resources: new(sys:Resources) - $template: $resources.yaml('DeployKubeDns.template') - $.masterNode.instance.agent.call($template, $resources) - $.setAttr(dnsDeployed, true) _deployContainersNetwork: Body: - If: $.useFlannel Then: - $.masterNode.setupFlannel() - Parallel: - $.minionNodes.take($.nodeCount).pselect($.setupFlannel()) - $.gatewayNodes.take($.gatewayCount).pselect($.setupFlannel()) Else: - Parallel: - $.masterNode.setupCalico() - $.minionNodes.take($.nodeCount).pselect($.setupCalico()) - $.gatewayNodes.take($.gatewayCount).pselect($.setupCalico()) createPod: Arguments: - definition: Contract: {} - isNew: Contract: $.bool().notNull() Default: true Body: - $.deploy() - $resources: new(sys:Resources) - $template: $resources.yaml('UpdatePod.template').bind(dict( podDefinition => $definition, isNew => $isNew )) - $.masterNode.instance.agent.call($template, $resources) createReplicationController: Arguments: - definition: Contract: {} - isNew: Contract: $.bool().notNull() Default: true Body: - $.deploy() - $resources: new(sys:Resources) - $template: $resources.yaml('UpdateReplicationController.template').bind(dict( controllerDefinition => $definition, isNew => $isNew )) - $.masterNode.instance.agent.call($template, $resources) deleteReplicationController: Arguments: - id: Contract: $.string().notNull() Body: - $.deploy() - $resources: new(sys:Resources) - $template: $resources.yaml('DeleteReplicationController.template').bind(dict(rcId => $id)) - $.masterNode.instance.agent.call($template, $resources) deletePods: Arguments: - labels: Contract: $.string().notNull(): $.string().notNull() Body: - $.deploy() - $resources: new(sys:Resources) - $template: $resources.yaml('DeletePods.template').bind(dict(labels => $labels)) - $.masterNode.instance.agent.call($template, $resources) createService: Arguments: - applicationName: Contract: $.string().notNull() - applicationPorts: Contract: - $.class(docker:ApplicationPort) - podId: Contract: $.string().notNull() Body: - $.serviceEndpoints: $.getAttr(serviceEndpoints, list()) - $currentEndpoints: $.serviceEndpoints.where($.applicationName = $applicationName and $.podId = $podId and $.scope = internal) - $serviceName: format('{0}-{1}', randomName(), $applicationName).substring(0, 24).toLower() - $endpointMap: {} - For: endpoint In: $currentEndpoints Do: - $serviceName: $endpoint.serviceName - $key: format('{0}-{1}', $endpoint.containerPort, $endpoint.protocol) - $endpointMap[$key]: $endpoint - $serviceChanged: len(list($applicationPorts.where($.scope != host))) != len($currentEndpoints) - $servicePorts: [] - For: applicationPort In: $applicationPorts.where($.scope != host) Do: - $key: format('{0}-{1}', $applicationPort.port, $applicationPort.protocol) - $endpoint: $endpointMap.get($key) - If: $endpoint != null Then: - $record: - assignedPort: $endpoint.port applicationPort: $applicationPort Else: - $port: $._findUnusedPort($applicationPort.port, $applicationPort.protocol) - $record: - assignedPort: $port applicationPort: $applicationPort - $serviceChanged: true - $securityGroupIngress: - ToPort: $port FromPort: $port IpProtocol: $applicationPort.protocol.toLower() External: $applicationPort.scope = public - $._environment.securityGroupManager.addGroupIngress($securityGroupIngress) - $servicePorts: $servicePorts + $record - If: $serviceChanged Then: - $serviceIp: $._createOrUpdateService( name => $serviceName, ports => $servicePorts, podId => $podId, isNew => len($currentEndpoints) = 0 ) - $._updateEndpoints( ports => $servicePorts, applicationName => $applicationName, podId => $podId, serviceName => $serviceName, serviceIp => $serviceIp ) - $._environment.stack.push() - $.setAttr(serviceEndpoints, $.serviceEndpoints) _createOrUpdateService: Arguments: - name: Contract: $.string().notNull() - ports: Contract: - assignedPort: $.int().notNull() applicationPort: $.class(docker:ApplicationPort).notNull() - podId: Contract: $.string().notNull() - isNew: Contract: $.bool().notNull() Body: - $serviceDefinition: apiVersion: v1 kind: Service metadata: labels: name: $name name: $name spec: ports: $ports.select(dict( port => $.assignedPort, targetPort => $.applicationPort.port, protocol => $.applicationPort.protocol, name => str($.assignedPort) )) selector: id: $podId - If: $.gatewayCount = 0 Then: - $serviceDefinition.spec.publicIPs: $.minionNodes.take($.nodeCount).select($.getIp()) - $resources: new(sys:Resources) - $template: $resources.yaml('UpdateService.template').bind(dict( serviceDefinition => $serviceDefinition, isNew => $isNew )) - Return: $.masterNode.instance.agent.call($template, $resources) _updateEndpoints: Arguments: - ports: Contract: - assignedPort: $.int().notNull() applicationPort: $.class(docker:ApplicationPort).notNull() - applicationName: Contract: $.string().notNull() - podId: Contract: $.string().notNull() - serviceName: Contract: $.string().notNull() - serviceIp: Contract: $.string().notNull() Body: - $.serviceEndpoints: $.serviceEndpoints.where($.applicationName != $applicationName or $.podId != $podId) - For: port In: $ports Do: - $newEndpoint: port: $port.assignedPort address: $serviceIp scope: internal portScope: $port.applicationPort.scope applicationName: $applicationName containerPort: $port.applicationPort.port protocol: $port.applicationPort.protocol podId: $podId serviceName: $serviceName - $.serviceEndpoints: $.serviceEndpoints + list($newEndpoint) - If: $port.applicationPort.scope in list(public, cloud) Then: - If: $.gatewayCount > 0 Then: - $nodes: $.gatewayNodes.take($.gatewayCount) Else: - $nodes: $.minionNodes.take($.nodeCount) - For: t In: $nodes Do: - $newEndpoint.address: $t.getIp() - $newEndpoint.scope: cloud - $.serviceEndpoints: $.serviceEndpoints + list($newEndpoint) - If: $t.instance.floatingIpAddress != null and $port.applicationPort.scope = public Then: - $newEndpoint.address: $t.instance.floatingIpAddress - $newEndpoint.scope: public - $.serviceEndpoints: $.serviceEndpoints + list($newEndpoint) - $newEndpoint: port: $port.applicationPort.port address: '127.0.0.1' scope: host portScope: $port.applicationPort.scope containerPort: $port.applicationPort.port protocol: $port.applicationPort.protocol applicationName: $applicationName podId: $podId serviceName: null - $.serviceEndpoints: $.serviceEndpoints + list($newEndpoint) _updateServicePublicIps: Body: - $prevNodeCount: $.getAttr(lastNodeCount, 0) - $prevGatewayCount: $.getAttr(lastGatewayCount, 0) - $gatewayModeChanged: $prevGatewayCount != $.gatewayCount and $prevGatewayCount * $.gatewayCount = 0 - If: $prevGatewayCount > 0 and $.gatewayCount > 0 Then: - Return: - If: $prevGatewayCount = 0 and $.gatewayCount = 0 and $prevNodeCount = $.nodeCount Then: - Return: - $serviceNameMap: {} - For: endpoint In: $.serviceEndpoints Do: - $serviceName: $endpoint.serviceName - If: $serviceName != null Then: - $serviceNameMap[$serviceName]: true - $uniqueServiceNames: $serviceNameMap.keys() - If: len($uniqueServiceNames) > 0 Then: - $publicIPs: $.minionNodes.take($.nodeCount).select($.getIp()) - $resources: new(sys:Resources) - $template: $resources.yaml('PatchServices.template').bind(dict( services => $uniqueServiceNames, publicIPs => $publicIPs )) - $.masterNode.instance.agent.call($template, $resources) deleteServices: Arguments: - applicationName: Contract: $.string().notNull() - podId: Contract: $.string().notNull() Body: - $.serviceEndpoints: $.getAttr(serviceEndpoints, list()) - $resources: new(sys:Resources) - $services: $.serviceEndpoints.where($.scope = internal and $.podId = $podId).select($.serviceName) - $template: $resources.yaml('DeleteServices.template').bind(dict( services => $services )) - $.masterNode.instance.agent.call($template, $resources) - $.serviceEndpoints: $.serviceEndpoints.where($.podId != $podId) - $.setAttr(serviceEndpoints, $.serviceEndpoints) _findUnusedPort: Arguments: - initial: Contract: $.int().notNull() - protocol: Contract: $.string().notNull() Body: - If: $initial != 22 and $._checkIfPortIsNotUsed($initial, $protocol) Then: Return: $initial - $port: 1025 - While: not $._checkIfPortIsNotUsed($port, $protocol) Do: $port: $port + 1 - Return: $port _checkIfPortIsNotUsed: Arguments: - port: Contract: $.int().notNull() - protocol: Contract: $.string().notNull() Body: - Return: len(list($.serviceEndpoints.where($.port = $port).where($.protocol = $protocol))) = 0 scaleNodesUp: Usage: Action Body: - If: $.nodeCount < len($.minionNodes) Then: - $._environment.reporter.report($this, 'Scaling up Kubernetes cluster') - $.nodeCount: $.nodeCount + 1 - $.deploy() Else: - $._environment.reporter.report($this, 'The maximum number of nodes has been reached') scaleNodesDown: Usage: Action Body: - If: $.nodeCount > 1 Then: - $._environment.reporter.report($this, 'Scaling Kubernetes cluster down') - $.nodeCount: $.nodeCount - 1 - $.deploy() Else: - $._environment.reporter.report($this, 'At least one node must be in cluster') scaleGatewaysUp: Usage: Action Body: - If: $.gatewayCount < len($.gatewayNodes) Then: - $._environment.reporter.report($this, 'Adding new gateway node') - $.gatewayCount: $.gatewayCount + 1 - $.deploy() Else: - $._environment.reporter.report($this, 'The maximum number of nodes has been reached') scaleGatewaysDown: Usage: Action Body: - If: $.gatewayCount > 1 Then: - $._environment.reporter.report($this, 'Removing gateway node') - $.gatewayCount: $.gatewayCount - 1 - $.deploy() Else: - $._environment.reporter.report($this, 'No gateway nodes that can be removed') scaleRc: Arguments: - rcName: Contract: $.string().notNull() - newSize: Contract: $.int().notNull() Body: - $resources: new(sys:Resources) - $template: $resources.yaml('ScaleRc.template').bind(dict( rcName => $rcName, newSize => $newSize )) - $.masterNode.instance.agent.call($template, $resources) restartContainers: Arguments: - podName: Contract: $.string().notNull() Body: - $.minionNodes.take($.nodeCount).pselect($.restartContainers(podName => $podName))