| /* |
| Copyright(c) 2018 AT&T Intellectual Property. All rights reserved. |
| |
| 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. |
| */ |
| |
| /* |
| * Query Kubernetes for status of deployments and extract readiness information |
| */ |
| |
| const fs = require('fs'); |
| const https = require('https'); |
| |
| const K8S_CREDS = '/var/run/secrets/kubernetes.io/serviceaccount'; |
| const K8S_HOST = 'kubernetes.default.svc.cluster.local'; // Full name to match cert for TLS |
| const K8S_PATH = 'apis/apps/v1beta2/namespaces/'; |
| |
| const CFY_LABEL = 'cfydeployment'; // All k8s deployments created by Cloudify--and only k8s deployments created by Cloudify--have this label |
| const MAX_DEPS = 1000; // Maximum number of k8s deployments to return from a query to k8s |
| |
| //Get token and CA cert |
| const ca = fs.readFileSync(K8S_CREDS + '/ca.crt'); |
| const token = fs.readFileSync(K8S_CREDS + '/token'); |
| |
| const summarizeDeploymentList = function(list) { |
| // list is a DeploymentList object returned by k8s |
| // Individual deployments are in the array 'items' |
| |
| let ret = |
| { |
| type: "summary", |
| count: 0, |
| ready: 0, |
| items: [] |
| }; |
| |
| // Extract readiness information |
| for (let deployment of list.items) { |
| ret.items.push( |
| { |
| name: deployment.metadata.name, |
| ready: deployment.status.readyReplicas || 0, |
| unavailable: deployment.status.unavailableReplicas || 0 |
| } |
| ); |
| ret.count ++; |
| ret.ready = ret.ready + (deployment.status.readyReplicas || 0); |
| } |
| |
| return ret; |
| }; |
| |
| const summarizeDeployment = function(deployment) { |
| // deployment is a Deployment object returned by k8s |
| // we make it look enough like a DeploymentList object to |
| // satisfy summarizeDeploymentList |
| return summarizeDeploymentList({items: [deployment]}); |
| }; |
| |
| const queryKubernetes = function(path, callback) { |
| // Make GET request to Kubernetes API |
| const options = { |
| host: K8S_HOST, |
| path: "/" + path, |
| ca : ca, |
| headers: { |
| Authorization: 'bearer ' + token |
| } |
| }; |
| console.log ("request url: " + options.host + options.path); |
| const req = https.get(options, function(resp) { |
| let rawBody = ""; |
| resp.on("data", function(data) { |
| rawBody += data; |
| }); |
| resp.on("error", function (error) { |
| console.error("error: " + error); |
| callback(error, null, null) |
| }); |
| resp.on("end", function() { |
| console.log ("status: " + resp.statusCode ? resp.statusCode: "NONE") |
| callback(null, resp, JSON.parse(rawBody)); |
| }); |
| }); |
| req.end(); |
| }; |
| |
| const getStatus = function(path, extract, callback) { |
| // Get info from k8s and extract readiness info |
| queryKubernetes(path, function(error, res, body) { |
| let ret = body; |
| if (!error && res && res.statusCode === 200) { |
| ret = extract(body); |
| } |
| callback (error, res, ret); |
| }); |
| }; |
| |
| const getStatusSinglePromise = function (item) { |
| // Expect item to be of the form {namespace: "namespace", deployment: "deployment_name"} |
| return new Promise(function(resolve, reject){ |
| const path = K8S_PATH + item.namespace + '/deployments/' + item.deployment; |
| queryKubernetes(path, function(error, res, body){ |
| if (error) { |
| reject(error); |
| } |
| else if (res.statusCode === 404) { |
| // Treat absent deployment as if it's an unhealthy deployment |
| resolve ({ |
| metadata: {name: item.deployment}, |
| status: {unavailableReplicas: 1} |
| }); |
| } |
| else if (res.statusCode != 200) { |
| reject(body); |
| } |
| else { |
| resolve(body); |
| } |
| }); |
| }); |
| } |
| exports.getStatusNamespace = function (namespace, callback) { |
| // Get readiness information for all deployments in namespace |
| const path = K8S_PATH + namespace + '/deployments'; |
| getStatus(path, summarizeDeploymentList, callback); |
| }; |
| |
| exports.getStatusSingle = function (namespace, deployment, callback) { |
| // Get readiness information for a single deployment |
| const path = K8S_PATH + namespace + '/deployments/' + deployment; |
| getStatus(path, summarizeDeployment, callback); |
| }; |
| |
| exports.getStatusListPromise = function (list) { |
| // List is of the form [{namespace: "namespace", deployment: "deployment_name"}, ... ] |
| const p = Promise.all(list.map(getStatusSinglePromise)) |
| return p.then(function(results) { |
| return summarizeDeploymentList({items: results}); |
| }); |
| } |
| |
| exports.getDCAEDeploymentsPromise = function (namespace) { |
| // Return list of the form [{namespace: "namespace"}, deployment: "deployment_name"]. |
| // List contains all k8s deployments in the specified namespace that were deployed |
| // by Cloudify, based on Cloudify's use of a "marker" label on each k8s deployment that |
| // the k8s plugin created. |
| |
| return new Promise(function(resolve, reject) { |
| const path = K8S_PATH + namespace + '/deployments?labelSelector=' + CFY_LABEL + '&limit=' + MAX_DEPS |
| queryKubernetes(path, function(error, res, body){ |
| if (error) { |
| reject(error); |
| } |
| else if (res.statusCode !== 200) { |
| reject(body); |
| } |
| else { |
| resolve(body.items.map(function(i) {return {namespace : namespace, deployment: i.metadata.name};})); |
| } |
| }); |
| }); |
| }; |