blob: 565c1e0873287fc4daeec53562e7bb99b918df1d [file] [log] [blame]
Jack Lucas2832ba22018-04-20 13:22:05 +00001/*
2Copyright(c) 2018 AT&T Intellectual Property. All rights reserved.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing,
12software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
13CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and limitations under the License.
15*/
16
17/*
18 * Query Kubernetes for status of deployments and extract readiness information
19 */
20
21const fs = require('fs');
Jack Lucase60d88b2018-12-12 16:48:41 -050022const https = require('https');
Jack Lucas2832ba22018-04-20 13:22:05 +000023
24const K8S_CREDS = '/var/run/secrets/kubernetes.io/serviceaccount';
Jack Lucase60d88b2018-12-12 16:48:41 -050025const K8S_HOST = 'kubernetes.default.svc.cluster.local'; // Full name to match cert for TLS
Jack Lucas2832ba22018-04-20 13:22:05 +000026const K8S_PATH = 'apis/apps/v1beta2/namespaces/';
27
Jack Lucase60d88b2018-12-12 16:48:41 -050028const CFY_LABEL = 'cfydeployment'; // All k8s deployments created by Cloudify--and only k8s deployments created by Cloudify--have this label
29const MAX_DEPS = 1000; // Maximum number of k8s deployments to return from a query to k8s
30
Jack Lucas2832ba22018-04-20 13:22:05 +000031//Get token and CA cert
32const ca = fs.readFileSync(K8S_CREDS + '/ca.crt');
33const token = fs.readFileSync(K8S_CREDS + '/token');
34
35const summarizeDeploymentList = function(list) {
Jack Lucas1d746682018-12-13 17:24:29 -050036 // list is a DeploymentList object returned by k8s
37 // Individual deployments are in the array 'items'
38
39 let ret =
40 {
41 type: "summary",
42 count: 0,
43 ready: 0,
44 items: []
45 };
46
47 // Extract readiness information
48 for (let deployment of list.items) {
49 ret.items.push(
50 {
51 name: deployment.metadata.name,
52 ready: deployment.status.readyReplicas || 0,
53 unavailable: deployment.status.unavailableReplicas || 0
54 }
55 );
56 ret.count ++;
57 ret.ready = ret.ready + (deployment.status.readyReplicas || 0);
58 }
59
60 return ret;
Jack Lucas2832ba22018-04-20 13:22:05 +000061};
62
63const summarizeDeployment = function(deployment) {
Jack Lucas1d746682018-12-13 17:24:29 -050064 // deployment is a Deployment object returned by k8s
65 // we make it look enough like a DeploymentList object to
66 // satisfy summarizeDeploymentList
67 return summarizeDeploymentList({items: [deployment]});
Jack Lucas2832ba22018-04-20 13:22:05 +000068};
69
70const queryKubernetes = function(path, callback) {
Jack Lucas1d746682018-12-13 17:24:29 -050071 // Make GET request to Kubernetes API
72 const options = {
73 host: K8S_HOST,
74 path: "/" + path,
75 ca : ca,
76 headers: {
77 Authorization: 'bearer ' + token
78 }
79 };
80 console.log ("request url: " + options.host + options.path);
81 const req = https.get(options, function(resp) {
82 let rawBody = "";
83 resp.on("data", function(data) {
84 rawBody += data;
85 });
86 resp.on("error", function (error) {
87 console.error("error: " + error);
88 callback(error, null, null)
89 });
90 resp.on("end", function() {
91 console.log ("status: " + resp.statusCode ? resp.statusCode: "NONE")
92 callback(null, resp, JSON.parse(rawBody));
93 });
94 });
95 req.end();
Jack Lucas2832ba22018-04-20 13:22:05 +000096};
97
98const getStatus = function(path, extract, callback) {
Jack Lucas1d746682018-12-13 17:24:29 -050099 // Get info from k8s and extract readiness info
100 queryKubernetes(path, function(error, res, body) {
101 let ret = body;
102 if (!error && res && res.statusCode === 200) {
103 ret = extract(body);
104 }
105 callback (error, res, ret);
106 });
Jack Lucas2832ba22018-04-20 13:22:05 +0000107};
108
Jack Lucase6d18572018-05-09 22:44:31 +0000109const getStatusSinglePromise = function (item) {
Jack Lucas1d746682018-12-13 17:24:29 -0500110 // Expect item to be of the form {namespace: "namespace", deployment: "deployment_name"}
111 return new Promise(function(resolve, reject){
112 const path = K8S_PATH + item.namespace + '/deployments/' + item.deployment;
113 queryKubernetes(path, function(error, res, body){
114 if (error) {
115 reject(error);
116 }
117 else if (res.statusCode === 404) {
118 // Treat absent deployment as if it's an unhealthy deployment
119 resolve ({
120 metadata: {name: item.deployment},
121 status: {unavailableReplicas: 1}
122 });
123 }
124 else if (res.statusCode != 200) {
125 reject(body);
126 }
127 else {
128 resolve(body);
129 }
130 });
131 });
Jack Lucase6d18572018-05-09 22:44:31 +0000132}
Jack Lucas2832ba22018-04-20 13:22:05 +0000133exports.getStatusNamespace = function (namespace, callback) {
Jack Lucas1d746682018-12-13 17:24:29 -0500134 // Get readiness information for all deployments in namespace
135 const path = K8S_PATH + namespace + '/deployments';
136 getStatus(path, summarizeDeploymentList, callback);
Jack Lucas2832ba22018-04-20 13:22:05 +0000137};
138
139exports.getStatusSingle = function (namespace, deployment, callback) {
Jack Lucas1d746682018-12-13 17:24:29 -0500140 // Get readiness information for a single deployment
141 const path = K8S_PATH + namespace + '/deployments/' + deployment;
142 getStatus(path, summarizeDeployment, callback);
Jack Lucase6d18572018-05-09 22:44:31 +0000143};
144
145exports.getStatusListPromise = function (list) {
Jack Lucas1d746682018-12-13 17:24:29 -0500146 // List is of the form [{namespace: "namespace", deployment: "deployment_name"}, ... ]
147 const p = Promise.all(list.map(getStatusSinglePromise))
148 return p.then(function(results) {
149 return summarizeDeploymentList({items: results});
150 });
Jack Lucase60d88b2018-12-12 16:48:41 -0500151}
152
153exports.getDCAEDeploymentsPromise = function (namespace) {
Jack Lucas1d746682018-12-13 17:24:29 -0500154 // Return list of the form [{namespace: "namespace"}, deployment: "deployment_name"].
155 // List contains all k8s deployments in the specified namespace that were deployed
156 // by Cloudify, based on Cloudify's use of a "marker" label on each k8s deployment that
157 // the k8s plugin created.
Jack Lucase60d88b2018-12-12 16:48:41 -0500158
Jack Lucas1d746682018-12-13 17:24:29 -0500159 return new Promise(function(resolve, reject) {
160 const path = K8S_PATH + namespace + '/deployments?labelSelector=' + CFY_LABEL + '&limit=' + MAX_DEPS
161 queryKubernetes(path, function(error, res, body){
162 if (error) {
163 reject(error);
164 }
165 else if (res.statusCode !== 200) {
166 reject(body);
167 }
168 else {
169 resolve(body.items.map(function(i) {return {namespace : namespace, deployment: i.metadata.name};}));
170 }
171 });
172 });
Jack Lucase60d88b2018-12-12 16:48:41 -0500173};