зеркало из https://github.com/Azure/k8s-deploy.git
action
This commit is contained in:
Родитель
4085ca3bbc
Коммит
8d56cba217
|
@ -261,7 +261,6 @@ FakesAssemblies/
|
|||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
|
119
README.md
119
README.md
|
@ -1,3 +1,120 @@
|
|||
# Deploy manifest action for Kubernetes
|
||||
Use this action to bake and deploy manifests to Kubernetes clusters.
|
||||
|
||||
Assumes that the deployment target K8s cluster context was set earlier in the workflow by using either [`Azure/aks-set-context`](https://github.com/Azure/aks-set-context/tree/releases/v1) or [`Azure/k8s-set-context`](https://github.com/Azure/k8s-set-context/tree/releases/v1)
|
||||
|
||||
#### Artifact substitution
|
||||
The deploy action takes as input a list of container images which can be specified along with their tags or digests. The same is substituted into the non-templatized version of manifest files before applying to the cluster to ensure that the right version of the image is pulled by the cluster nodes.
|
||||
|
||||
#### Manifest stability
|
||||
Rollout status is checked for the Kubernetes objects deployed. This is done to incorporate stability checks while computing the task status as success/failure.
|
||||
|
||||
#### Secret handling
|
||||
The manifest files specfied as inputs are augmented with appropriate imagePullSecrets before deploying to the cluster.
|
||||
|
||||
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v1
|
||||
with:
|
||||
namespace: 'myapp' # optional
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }} '
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
manifests: '/manifests/*.*'
|
||||
kubectl-version: 'latest' # optional
|
||||
```
|
||||
Refer to the action metadata file for details about all the inputs https://github.com/Azure/k8s-deploy/blob/master/action.yml
|
||||
|
||||
## End to end workflow for building container images and deploying to an Azure Kubernetes Service cluster
|
||||
|
||||
```yaml
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- uses: Azure/docker-login@v1
|
||||
with:
|
||||
login-server: contoso.azurecr.io
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- run: |
|
||||
docker build . -t contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
docker push contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
|
||||
# Set the target AKS cluster.
|
||||
- uses: Azure/aks-set-context@v1
|
||||
with:
|
||||
creds: '${{ secrets.AZURE_CREDENTIALS }}'
|
||||
cluster-name: contoso
|
||||
resource-group: contoso-rg
|
||||
|
||||
- uses: Azure/k8s-create-secret@v1
|
||||
with:
|
||||
container-registry-url: contoso.azurecr.io
|
||||
container-registry-username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
secret-name: demo-k8s-secret
|
||||
|
||||
- uses: Azure/k8s-deploy@v1
|
||||
with:
|
||||
manifests: |
|
||||
manifests/deployment.yml
|
||||
manifests/service.yml
|
||||
images: |
|
||||
demo.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
imagepullsecrets: |
|
||||
demo-k8s-secret
|
||||
```
|
||||
|
||||
## End to end workflow for building container images and deploying to a Kubernetes cluster
|
||||
|
||||
```yaml
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- uses: Azure/docker-login@v1
|
||||
with:
|
||||
login-server: contoso.azurecr.io
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- run: |
|
||||
docker build . -t contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
docker push contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
|
||||
- uses: Azure/k8s-set-context@v1
|
||||
with:
|
||||
kubeconfig: ${{ secrets.KUBE_CONFIG }}
|
||||
|
||||
- uses: Azure/k8s-create-secret@v1
|
||||
with:
|
||||
container-registry-url: contoso.azurecr.io
|
||||
container-registry-username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
secret-name: demo-k8s-secret
|
||||
|
||||
- uses: Azure/k8s-deploy@v1
|
||||
with:
|
||||
manifests: |
|
||||
manifests/deployment.yml
|
||||
manifests/service.yml
|
||||
images: |
|
||||
demo.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
imagepullsecrets: |
|
||||
demo-k8s-secret
|
||||
```
|
||||
|
||||
# Contributing
|
||||
|
||||
|
@ -11,4 +128,4 @@ provided by the bot. You will only need to do this once across all repos using o
|
|||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
|
@ -0,0 +1,26 @@
|
|||
name: 'Deploy to Kubernetes cluster'
|
||||
description: 'Deploy to Kubernetes cluster'
|
||||
inputs:
|
||||
# Please ensure you have used either azure/k8s-actions/aks-set-context or azure/k8s-actions/k8s-set-context in the workflow before this action
|
||||
namespace:
|
||||
description: 'Choose the target Kubernetes namespace. If the namespace is not provided, the commands will run in the default namespace.'
|
||||
required: false
|
||||
manifests:
|
||||
description: 'Path to the manifest files which will be used for deployment.'
|
||||
required: true
|
||||
default: ''
|
||||
images:
|
||||
description: 'Fully qualified resource URL of the image(s) to be used for substitutions on the manifest files
|
||||
Example: contosodemo.azurecr.io/helloworld:test'
|
||||
required: false
|
||||
imagepullsecrets:
|
||||
description: 'Name of a docker-registry secret that has already been set up within the cluster. Each of these secret names are added under imagePullSecrets field for the workloads found in the input manifest files'
|
||||
required: false
|
||||
kubectl-version:
|
||||
description: 'Version of kubectl. Installs a specific version of kubectl binary'
|
||||
required: false
|
||||
branding:
|
||||
color: 'green' # optional, decorates the entry in the GitHub Marketplace
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'lib/run.js'
|
|
@ -0,0 +1,71 @@
|
|||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
const util = require("util");
|
||||
const fs = require("fs");
|
||||
const toolCache = require("@actions/tool-cache");
|
||||
const core = require("@actions/core");
|
||||
const kubectlToolName = 'kubectl';
|
||||
const stableKubectlVersion = 'v1.15.0';
|
||||
const stableVersionUrl = 'https://storage.googleapis.com/kubernetes-release/release/stable.txt';
|
||||
function getExecutableExtension() {
|
||||
if (os.type().match(/^Win/)) {
|
||||
return '.exe';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
function getkubectlDownloadURL(version) {
|
||||
switch (os.type()) {
|
||||
case 'Linux':
|
||||
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/amd64/kubectl', version);
|
||||
case 'Darwin':
|
||||
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/darwin/amd64/kubectl', version);
|
||||
case 'Windows_NT':
|
||||
default:
|
||||
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/windows/amd64/kubectl.exe', version);
|
||||
}
|
||||
}
|
||||
function getStableKubectlVersion() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return toolCache.downloadTool(stableVersionUrl).then((downloadPath) => {
|
||||
let version = fs.readFileSync(downloadPath, 'utf8').toString().trim();
|
||||
if (!version) {
|
||||
version = stableKubectlVersion;
|
||||
}
|
||||
return version;
|
||||
}, (error) => {
|
||||
core.debug(error);
|
||||
core.warning('GetStableVersionFailed');
|
||||
return stableKubectlVersion;
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.getStableKubectlVersion = getStableKubectlVersion;
|
||||
function downloadKubectl(version) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let cachedToolpath = toolCache.find(kubectlToolName, version);
|
||||
let kubectlDownloadPath = '';
|
||||
if (!cachedToolpath) {
|
||||
try {
|
||||
kubectlDownloadPath = yield toolCache.downloadTool(getkubectlDownloadURL(version));
|
||||
}
|
||||
catch (exception) {
|
||||
throw new Error('DownloadKubectlFailed');
|
||||
}
|
||||
cachedToolpath = yield toolCache.cacheFile(kubectlDownloadPath, kubectlToolName + getExecutableExtension(), kubectlToolName, version);
|
||||
}
|
||||
const kubectlPath = path.join(cachedToolpath, kubectlToolName + getExecutableExtension());
|
||||
fs.chmodSync(kubectlPath, '777');
|
||||
return kubectlPath;
|
||||
});
|
||||
}
|
||||
exports.downloadKubectl = downloadKubectl;
|
|
@ -0,0 +1,141 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const core = require("@actions/core");
|
||||
const utils_1 = require("./utils");
|
||||
function getImagePullSecrets(inputObject) {
|
||||
if (!inputObject || !inputObject.spec) {
|
||||
return;
|
||||
}
|
||||
if (utils_1.isEqual(inputObject.kind, 'pod')
|
||||
&& inputObject
|
||||
&& inputObject.spec
|
||||
&& inputObject.spec.imagePullSecrets) {
|
||||
return inputObject.spec.imagePullSecrets;
|
||||
}
|
||||
else if (utils_1.isEqual(inputObject.kind, 'cronjob')
|
||||
&& inputObject
|
||||
&& inputObject.spec
|
||||
&& inputObject.spec.jobTemplate
|
||||
&& inputObject.spec.jobTemplate.spec
|
||||
&& inputObject.spec.jobTemplate.spec.template
|
||||
&& inputObject.spec.jobTemplate.spec.template.spec
|
||||
&& inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets) {
|
||||
return inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets;
|
||||
}
|
||||
else if (inputObject
|
||||
&& inputObject.spec
|
||||
&& inputObject.spec.template
|
||||
&& inputObject.spec.template.spec
|
||||
&& inputObject.spec.template.spec.imagePullSecrets) {
|
||||
return inputObject.spec.template.spec.imagePullSecrets;
|
||||
}
|
||||
}
|
||||
function setImagePullSecrets(inputObject, newImagePullSecrets) {
|
||||
if (!inputObject || !inputObject.spec || !newImagePullSecrets) {
|
||||
return;
|
||||
}
|
||||
if (utils_1.isEqual(inputObject.kind, 'pod')) {
|
||||
if (inputObject
|
||||
&& inputObject.spec) {
|
||||
if (newImagePullSecrets.length > 0) {
|
||||
inputObject.spec.imagePullSecrets = newImagePullSecrets;
|
||||
}
|
||||
else {
|
||||
delete inputObject.spec.imagePullSecrets;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (utils_1.isEqual(inputObject.kind, 'cronjob')) {
|
||||
if (inputObject
|
||||
&& inputObject.spec
|
||||
&& inputObject.spec.jobTemplate
|
||||
&& inputObject.spec.jobTemplate.spec
|
||||
&& inputObject.spec.jobTemplate.spec.template
|
||||
&& inputObject.spec.jobTemplate.spec.template.spec) {
|
||||
if (newImagePullSecrets.length > 0) {
|
||||
inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets = newImagePullSecrets;
|
||||
}
|
||||
else {
|
||||
delete inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!!inputObject.spec.template && !!inputObject.spec.template.spec) {
|
||||
if (inputObject
|
||||
&& inputObject.spec
|
||||
&& inputObject.spec.template
|
||||
&& inputObject.spec.template.spec) {
|
||||
if (newImagePullSecrets.length > 0) {
|
||||
inputObject.spec.template.spec.imagePullSecrets = newImagePullSecrets;
|
||||
}
|
||||
else {
|
||||
delete inputObject.spec.template.spec.imagePullSecrets;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function substituteImageNameInSpecContent(currentString, imageName, imageNameWithNewTag) {
|
||||
if (currentString.indexOf(imageName) < 0) {
|
||||
core.debug(`No occurence of replacement token: ${imageName} found`);
|
||||
return currentString;
|
||||
}
|
||||
return currentString.split('\n').reduce((acc, line) => {
|
||||
const imageKeyword = line.match(/^ *image:/);
|
||||
if (imageKeyword) {
|
||||
const [currentImageName, currentImageTag] = line
|
||||
.substring(imageKeyword[0].length) // consume the line from keyword onwards
|
||||
.trim()
|
||||
.replace(/[',"]/g, '') // replace allowed quotes with nothing
|
||||
.split(':');
|
||||
if (currentImageName === imageName) {
|
||||
return acc + `${imageKeyword[0]} ${imageNameWithNewTag}\n`;
|
||||
}
|
||||
}
|
||||
return acc + line + '\n';
|
||||
}, '');
|
||||
}
|
||||
function updateContainerImagesInManifestFiles(contents, containers) {
|
||||
if (!!containers && containers.length > 0) {
|
||||
containers.forEach((container) => {
|
||||
let imageName = container.split(':')[0];
|
||||
if (imageName.indexOf('@') > 0) {
|
||||
imageName = imageName.split('@')[0];
|
||||
}
|
||||
if (contents.indexOf(imageName) > 0) {
|
||||
contents = substituteImageNameInSpecContent(contents, imageName, container);
|
||||
}
|
||||
});
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
exports.updateContainerImagesInManifestFiles = updateContainerImagesInManifestFiles;
|
||||
function updateImagePullSecrets(inputObject, newImagePullSecrets) {
|
||||
if (!inputObject || !inputObject.spec || !newImagePullSecrets) {
|
||||
return;
|
||||
}
|
||||
let newImagePullSecretsObjects;
|
||||
if (newImagePullSecrets.length > 0) {
|
||||
newImagePullSecretsObjects = Array.from(newImagePullSecrets, x => { return !!x ? { 'name': x } : null; });
|
||||
}
|
||||
else {
|
||||
newImagePullSecretsObjects = [];
|
||||
}
|
||||
let existingImagePullSecretObjects = getImagePullSecrets(inputObject);
|
||||
if (!existingImagePullSecretObjects) {
|
||||
existingImagePullSecretObjects = new Array();
|
||||
}
|
||||
existingImagePullSecretObjects = existingImagePullSecretObjects.concat(newImagePullSecretsObjects);
|
||||
setImagePullSecrets(inputObject, existingImagePullSecretObjects);
|
||||
}
|
||||
exports.updateImagePullSecrets = updateImagePullSecrets;
|
||||
const workloadTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob'];
|
||||
function isWorkloadEntity(kind) {
|
||||
if (!kind) {
|
||||
core.debug('ResourceKindNotDefined');
|
||||
return false;
|
||||
}
|
||||
return workloadTypes.some((type) => {
|
||||
return utils_1.isEqual(type, kind);
|
||||
});
|
||||
}
|
||||
exports.isWorkloadEntity = isWorkloadEntity;
|
|
@ -0,0 +1,163 @@
|
|||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const toolCache = require("@actions/tool-cache");
|
||||
const core = require("@actions/core");
|
||||
const io = require("@actions/io");
|
||||
const toolrunner_1 = require("@actions/exec/lib/toolrunner");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const yaml = require("js-yaml");
|
||||
const utils_1 = require("./utils");
|
||||
const kubernetes_utils_1 = require("./kubernetes-utils");
|
||||
const kubectl_util_1 = require("./kubectl-util");
|
||||
let kubectlPath = "";
|
||||
function setKubectlPath() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (core.getInput('kubectl-version')) {
|
||||
const version = core.getInput('kubect-version');
|
||||
kubectlPath = toolCache.find('kubectl', version);
|
||||
if (!kubectlPath) {
|
||||
kubectlPath = yield installKubectl(version);
|
||||
}
|
||||
}
|
||||
else {
|
||||
kubectlPath = yield io.which('kubectl', false);
|
||||
if (!kubectlPath) {
|
||||
const allVersions = toolCache.findAllVersions('kubectl');
|
||||
kubectlPath = allVersions.length > 0 ? toolCache.find('kubectl', allVersions[0]) : '';
|
||||
if (!kubectlPath) {
|
||||
throw new Error('Kubectl is not installed, either add install-kubectl action or provide "kubectl-version" input to download kubectl');
|
||||
}
|
||||
kubectlPath = path.join(kubectlPath, `kubectl${utils_1.getExecutableExtension()}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function deploy(manifests, namespace) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (manifests) {
|
||||
for (var i = 0; i < manifests.length; i++) {
|
||||
let manifest = manifests[i];
|
||||
let toolRunner = new toolrunner_1.ToolRunner(kubectlPath, ['apply', '-f', manifest, '--namespace', namespace]);
|
||||
yield toolRunner.exec();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function checkRolloutStatus(name, kind, namespace) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const toolrunner = new toolrunner_1.ToolRunner(kubectlPath, ['rollout', 'status', `${kind.trim()}/${name.trim()}`, `--namespace`, namespace]);
|
||||
return toolrunner.exec();
|
||||
});
|
||||
}
|
||||
function checkManifestsStability(manifests, namespace) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
manifests.forEach((manifest) => {
|
||||
let content = fs.readFileSync(manifest).toString();
|
||||
yaml.safeLoadAll(content, function (inputObject) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (!!inputObject.kind && !!inputObject.metadata && !!inputObject.metadata.name) {
|
||||
let kind = inputObject.kind;
|
||||
switch (kind.toLowerCase()) {
|
||||
case 'deployment':
|
||||
case 'daemonset':
|
||||
case 'statefulset':
|
||||
yield checkRolloutStatus(inputObject.metadata.name, kind, namespace);
|
||||
break;
|
||||
default:
|
||||
core.debug(`No rollout check for kind: ${inputObject.kind}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
function getManifestFileName(kind, name) {
|
||||
const filePath = kind + '_' + name + '_' + utils_1.getCurrentTime().toString();
|
||||
const tempDirectory = process.env['RUNNER_TEMP'];
|
||||
const fileName = path.join(tempDirectory, path.basename(filePath));
|
||||
return fileName;
|
||||
}
|
||||
function writeObjectsToFile(inputObjects) {
|
||||
const newFilePaths = [];
|
||||
if (!!inputObjects) {
|
||||
inputObjects.forEach((inputObject) => {
|
||||
try {
|
||||
const inputObjectString = JSON.stringify(inputObject);
|
||||
if (!!inputObject.kind && !!inputObject.metadata && !!inputObject.metadata.name) {
|
||||
const fileName = getManifestFileName(inputObject.kind, inputObject.metadata.name);
|
||||
fs.writeFileSync(path.join(fileName), inputObjectString);
|
||||
newFilePaths.push(fileName);
|
||||
}
|
||||
else {
|
||||
core.debug('Input object is not proper K8s resource object. Object: ' + inputObjectString);
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
core.debug('Exception occurred while wrting object to file : ' + inputObject + ' . Exception: ' + ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
return newFilePaths;
|
||||
}
|
||||
function updateManifests(manifests, imagesToOverride, imagepullsecrets) {
|
||||
const newObjectsList = [];
|
||||
manifests.forEach((filePath) => {
|
||||
let fileContents = fs.readFileSync(filePath).toString();
|
||||
fileContents = kubernetes_utils_1.updateContainerImagesInManifestFiles(fileContents, imagesToOverride.split('\n'));
|
||||
yaml.safeLoadAll(fileContents, function (inputObject) {
|
||||
if (!!imagepullsecrets && !!inputObject && !!inputObject.kind) {
|
||||
if (kubernetes_utils_1.isWorkloadEntity(inputObject.kind)) {
|
||||
kubernetes_utils_1.updateImagePullSecrets(inputObject, imagepullsecrets.split('\n'));
|
||||
}
|
||||
}
|
||||
newObjectsList.push(inputObject);
|
||||
});
|
||||
});
|
||||
return writeObjectsToFile(newObjectsList);
|
||||
}
|
||||
function installKubectl(version) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (utils_1.isEqual(version, 'latest')) {
|
||||
version = yield kubectl_util_1.getStableKubectlVersion();
|
||||
}
|
||||
return yield kubectl_util_1.downloadKubectl(version);
|
||||
});
|
||||
}
|
||||
function checkClusterContext() {
|
||||
if (!process.env["KUBECONFIG"]) {
|
||||
throw new Error('Cluster context not set. Use k8ssetcontext action to set cluster context');
|
||||
}
|
||||
}
|
||||
function run() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
checkClusterContext();
|
||||
yield setKubectlPath();
|
||||
let manifestsInput = core.getInput('manifests');
|
||||
if (!manifestsInput) {
|
||||
core.setFailed('No manifests supplied to deploy');
|
||||
}
|
||||
let namespace = core.getInput('namespace');
|
||||
if (!namespace) {
|
||||
namespace = 'default';
|
||||
}
|
||||
let manifests = manifestsInput.split('\n');
|
||||
const imagesToOverride = core.getInput('images');
|
||||
const imagePullSecretsToAdd = core.getInput('imagepullsecrets');
|
||||
if (!!imagePullSecretsToAdd || !!imagesToOverride) {
|
||||
manifests = updateManifests(manifests, imagesToOverride, imagePullSecretsToAdd);
|
||||
}
|
||||
yield deploy(manifests, namespace);
|
||||
yield checkManifestsStability(manifests, namespace);
|
||||
});
|
||||
}
|
||||
run().catch(core.setFailed);
|
|
@ -0,0 +1,26 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const os = require("os");
|
||||
function isEqual(str1, str2) {
|
||||
if (!str1)
|
||||
str1 = "";
|
||||
if (!str2)
|
||||
str2 = "";
|
||||
return str1.toLowerCase() === str2.toLowerCase();
|
||||
}
|
||||
exports.isEqual = isEqual;
|
||||
function getRandomInt(max) {
|
||||
return Math.floor(Math.random() * Math.floor(max));
|
||||
}
|
||||
exports.getRandomInt = getRandomInt;
|
||||
function getExecutableExtension() {
|
||||
if (os.type().match(/^Win/)) {
|
||||
return '.exe';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
exports.getExecutableExtension = getExecutableExtension;
|
||||
function getCurrentTime() {
|
||||
return new Date().getTime();
|
||||
}
|
||||
exports.getCurrentTime = getCurrentTime;
|
|
@ -0,0 +1,98 @@
|
|||
{
|
||||
"name": "k8s-deploy-action",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@actions/core": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.1.0.tgz",
|
||||
"integrity": "sha512-KKpo3xzo0Zsikni9tbOsEQkxZBGDsYSJZNkTvmo0gPSXrc98TBOcdTvKwwjitjkjHkreTggWdB1ACiAFVgsuzA=="
|
||||
},
|
||||
"@actions/exec": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.1.tgz",
|
||||
"integrity": "sha512-nvFkxwiicvpzNiCBF4wFBDfnBvi7xp/as7LE1hBxBxKG2L29+gkIPBiLKMVORL+Hg3JNf07AKRfl0V5djoypjQ=="
|
||||
},
|
||||
"@actions/io": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.1.tgz",
|
||||
"integrity": "sha512-rhq+tfZukbtaus7xyUtwKfuiCRXd1hWSfmJNEpFgBQJ4woqPEpsBw04awicjwz9tyG2/MVhAEMfVn664Cri5zA=="
|
||||
},
|
||||
"@actions/tool-cache": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.1.1.tgz",
|
||||
"integrity": "sha512-AILekrrj/L4N/5z5TGtUKVie4nKjxDioCgOEymyYxzPhGfjIxfE71tN2VTTpiICEWJ883rPRj2+WinTr1b6yVA==",
|
||||
"requires": {
|
||||
"@actions/core": "^1.1.0",
|
||||
"@actions/exec": "^1.0.1",
|
||||
"@actions/io": "^1.0.1",
|
||||
"semver": "^6.1.0",
|
||||
"typed-rest-client": "^1.4.0",
|
||||
"uuid": "^3.3.2"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "12.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.4.tgz",
|
||||
"integrity": "sha512-W0+n1Y+gK/8G2P/piTkBBN38Qc5Q1ZSO6B5H3QmPCUewaiXOo2GCAWZ4ElZCcNhjJuBSUSLGFUJnmlCn5+nxOQ==",
|
||||
"dev": true
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.13.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
|
||||
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||
},
|
||||
"tunnel": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.4.tgz",
|
||||
"integrity": "sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM="
|
||||
},
|
||||
"typed-rest-client": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.5.0.tgz",
|
||||
"integrity": "sha512-DVZRlmsfnTjp6ZJaatcdyvvwYwbWvR4YDNFDqb+qdTxpvaVP99YCpBkA8rxsLtAPjBVoDe4fNsnMIdZTiPuKWg==",
|
||||
"requires": {
|
||||
"tunnel": "0.0.4",
|
||||
"underscore": "1.8.3"
|
||||
}
|
||||
},
|
||||
"underscore": {
|
||||
"version": "1.8.3",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
|
||||
"integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
|
||||
"integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "k8s-deploy-action",
|
||||
"version": "0.0.0",
|
||||
"author": "Deepak Sattiraju",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "tsc --outDir ./lib --rootDir ./src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/tool-cache": "^1.0.0",
|
||||
"@actions/io": "^1.0.0",
|
||||
"@actions/core": "^1.0.0",
|
||||
"@actions/exec": "^1.0.0",
|
||||
"js-yaml": "3.13.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.0.10"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as util from 'util';
|
||||
import * as fs from 'fs';
|
||||
|
||||
import * as toolCache from '@actions/tool-cache';
|
||||
import * as core from '@actions/core';
|
||||
|
||||
const kubectlToolName = 'kubectl';
|
||||
const stableKubectlVersion = 'v1.15.0';
|
||||
const stableVersionUrl = 'https://storage.googleapis.com/kubernetes-release/release/stable.txt';
|
||||
|
||||
function getExecutableExtension(): string {
|
||||
if (os.type().match(/^Win/)) {
|
||||
return '.exe';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function getkubectlDownloadURL(version: string): string {
|
||||
switch (os.type()) {
|
||||
case 'Linux':
|
||||
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/amd64/kubectl', version);
|
||||
|
||||
case 'Darwin':
|
||||
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/darwin/amd64/kubectl', version);
|
||||
|
||||
case 'Windows_NT':
|
||||
default:
|
||||
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/windows/amd64/kubectl.exe', version);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStableKubectlVersion(): Promise<string> {
|
||||
return toolCache.downloadTool(stableVersionUrl).then((downloadPath) => {
|
||||
let version = fs.readFileSync(downloadPath, 'utf8').toString().trim();
|
||||
if (!version) {
|
||||
version = stableKubectlVersion;
|
||||
}
|
||||
return version;
|
||||
}, (error) => {
|
||||
core.debug(error);
|
||||
core.warning('GetStableVersionFailed');
|
||||
return stableKubectlVersion;
|
||||
});
|
||||
}
|
||||
|
||||
export async function downloadKubectl(version: string): Promise<string> {
|
||||
let cachedToolpath = toolCache.find(kubectlToolName, version);
|
||||
let kubectlDownloadPath = '';
|
||||
if (!cachedToolpath) {
|
||||
try {
|
||||
kubectlDownloadPath = await toolCache.downloadTool(getkubectlDownloadURL(version));
|
||||
} catch (exception) {
|
||||
throw new Error('DownloadKubectlFailed');
|
||||
}
|
||||
|
||||
cachedToolpath = await toolCache.cacheFile(kubectlDownloadPath, kubectlToolName + getExecutableExtension(), kubectlToolName, version);
|
||||
}
|
||||
|
||||
const kubectlPath = path.join(cachedToolpath, kubectlToolName + getExecutableExtension());
|
||||
fs.chmodSync(kubectlPath, '777');
|
||||
return kubectlPath;
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
import * as core from '@actions/core';
|
||||
import { isEqual } from "./utils";
|
||||
|
||||
function getImagePullSecrets(inputObject: any) {
|
||||
if (!inputObject || !inputObject.spec) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEqual(inputObject.kind, 'pod')
|
||||
&& inputObject
|
||||
&& inputObject.spec
|
||||
&& inputObject.spec.imagePullSecrets) {
|
||||
|
||||
return inputObject.spec.imagePullSecrets;
|
||||
} else if (isEqual(inputObject.kind, 'cronjob')
|
||||
&& inputObject
|
||||
&& inputObject.spec
|
||||
&& inputObject.spec.jobTemplate
|
||||
&& inputObject.spec.jobTemplate.spec
|
||||
&& inputObject.spec.jobTemplate.spec.template
|
||||
&& inputObject.spec.jobTemplate.spec.template.spec
|
||||
&& inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets) {
|
||||
|
||||
return inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets;
|
||||
} else if (inputObject
|
||||
&& inputObject.spec
|
||||
&& inputObject.spec.template
|
||||
&& inputObject.spec.template.spec
|
||||
&& inputObject.spec.template.spec.imagePullSecrets) {
|
||||
|
||||
return inputObject.spec.template.spec.imagePullSecrets;
|
||||
}
|
||||
}
|
||||
|
||||
function setImagePullSecrets(inputObject: any, newImagePullSecrets: any) {
|
||||
if (!inputObject || !inputObject.spec || !newImagePullSecrets) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEqual(inputObject.kind, 'pod')) {
|
||||
if (inputObject
|
||||
&& inputObject.spec) {
|
||||
if (newImagePullSecrets.length > 0) {
|
||||
inputObject.spec.imagePullSecrets = newImagePullSecrets;
|
||||
} else {
|
||||
delete inputObject.spec.imagePullSecrets;
|
||||
}
|
||||
}
|
||||
} else if (isEqual(inputObject.kind, 'cronjob')) {
|
||||
if (inputObject
|
||||
&& inputObject.spec
|
||||
&& inputObject.spec.jobTemplate
|
||||
&& inputObject.spec.jobTemplate.spec
|
||||
&& inputObject.spec.jobTemplate.spec.template
|
||||
&& inputObject.spec.jobTemplate.spec.template.spec) {
|
||||
if (newImagePullSecrets.length > 0) {
|
||||
inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets = newImagePullSecrets;
|
||||
} else {
|
||||
delete inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets;
|
||||
}
|
||||
}
|
||||
} else if (!!inputObject.spec.template && !!inputObject.spec.template.spec) {
|
||||
if (inputObject
|
||||
&& inputObject.spec
|
||||
&& inputObject.spec.template
|
||||
&& inputObject.spec.template.spec) {
|
||||
if (newImagePullSecrets.length > 0) {
|
||||
inputObject.spec.template.spec.imagePullSecrets = newImagePullSecrets;
|
||||
} else {
|
||||
delete inputObject.spec.template.spec.imagePullSecrets;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function substituteImageNameInSpecContent(currentString: string, imageName: string, imageNameWithNewTag: string) {
|
||||
if (currentString.indexOf(imageName) < 0) {
|
||||
core.debug(`No occurence of replacement token: ${imageName} found`);
|
||||
return currentString;
|
||||
}
|
||||
|
||||
return currentString.split('\n').reduce((acc, line) => {
|
||||
const imageKeyword = line.match(/^ *image:/);
|
||||
if (imageKeyword) {
|
||||
const [currentImageName, currentImageTag] = line
|
||||
.substring(imageKeyword[0].length) // consume the line from keyword onwards
|
||||
.trim()
|
||||
.replace(/[',"]/g, '') // replace allowed quotes with nothing
|
||||
.split(':');
|
||||
|
||||
if (currentImageName === imageName) {
|
||||
return acc + `${imageKeyword[0]} ${imageNameWithNewTag}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
return acc + line + '\n';
|
||||
}, '');
|
||||
}
|
||||
|
||||
export function updateContainerImagesInManifestFiles(contents, containers: string[]): string {
|
||||
if (!!containers && containers.length > 0) {
|
||||
containers.forEach((container: string) => {
|
||||
let imageName = container.split(':')[0];
|
||||
if (imageName.indexOf('@') > 0) {
|
||||
imageName = imageName.split('@')[0];
|
||||
}
|
||||
if (contents.indexOf(imageName) > 0) {
|
||||
contents = substituteImageNameInSpecContent(contents, imageName, container);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
export function updateImagePullSecrets(inputObject: any, newImagePullSecrets: string[]) {
|
||||
if (!inputObject || !inputObject.spec || !newImagePullSecrets) {
|
||||
return;
|
||||
}
|
||||
|
||||
let newImagePullSecretsObjects;
|
||||
if (newImagePullSecrets.length > 0) {
|
||||
newImagePullSecretsObjects = Array.from(newImagePullSecrets, x => { return !!x ? { 'name': x } : null; });
|
||||
} else {
|
||||
newImagePullSecretsObjects = [];
|
||||
}
|
||||
let existingImagePullSecretObjects: any = getImagePullSecrets(inputObject);
|
||||
if (!existingImagePullSecretObjects) {
|
||||
existingImagePullSecretObjects = new Array();
|
||||
}
|
||||
|
||||
existingImagePullSecretObjects = existingImagePullSecretObjects.concat(newImagePullSecretsObjects);
|
||||
setImagePullSecrets(inputObject, existingImagePullSecretObjects);
|
||||
}
|
||||
|
||||
const workloadTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob'];
|
||||
|
||||
export function isWorkloadEntity(kind: string): boolean {
|
||||
if (!kind) {
|
||||
core.debug('ResourceKindNotDefined');
|
||||
return false;
|
||||
}
|
||||
|
||||
return workloadTypes.some((type: string) => {
|
||||
return isEqual(type, kind);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
import * as toolCache from '@actions/tool-cache';
|
||||
import * as core from '@actions/core';
|
||||
import * as io from '@actions/io';
|
||||
import { ToolRunner } from "@actions/exec/lib/toolrunner";
|
||||
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as yaml from 'js-yaml';
|
||||
|
||||
import { getExecutableExtension, isEqual, getCurrentTime } from "./utils";
|
||||
import { isWorkloadEntity, updateContainerImagesInManifestFiles, updateImagePullSecrets } from "./kubernetes-utils";
|
||||
import { downloadKubectl, getStableKubectlVersion } from "./kubectl-util";
|
||||
|
||||
let kubectlPath = "";
|
||||
|
||||
async function setKubectlPath() {
|
||||
if (core.getInput('kubectl-version')) {
|
||||
const version = core.getInput('kubect-version');
|
||||
kubectlPath = toolCache.find('kubectl', version);
|
||||
if (!kubectlPath) {
|
||||
kubectlPath = await installKubectl(version);
|
||||
}
|
||||
} else {
|
||||
kubectlPath = await io.which('kubectl', false);
|
||||
if (!kubectlPath) {
|
||||
const allVersions = toolCache.findAllVersions('kubectl');
|
||||
kubectlPath = allVersions.length > 0 ? toolCache.find('kubectl', allVersions[0]) : '';
|
||||
if (!kubectlPath) {
|
||||
throw new Error('Kubectl is not installed, either add install-kubectl action or provide "kubectl-version" input to download kubectl');
|
||||
}
|
||||
kubectlPath = path.join(kubectlPath, `kubectl${getExecutableExtension()}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function deploy(manifests: string[], namespace: string) {
|
||||
if (manifests) {
|
||||
for (var i = 0; i < manifests.length; i++) {
|
||||
let manifest = manifests[i];
|
||||
let toolRunner = new ToolRunner(kubectlPath, ['apply', '-f', manifest, '--namespace', namespace]);
|
||||
await toolRunner.exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function checkRolloutStatus(name: string, kind: string, namespace: string) {
|
||||
const toolrunner = new ToolRunner(kubectlPath, ['rollout', 'status', `${kind.trim()}/${name.trim()}`, `--namespace`, namespace]);
|
||||
return toolrunner.exec();
|
||||
}
|
||||
|
||||
async function checkManifestsStability(manifests: string[], namespace: string) {
|
||||
manifests.forEach((manifest) => {
|
||||
let content = fs.readFileSync(manifest).toString();
|
||||
yaml.safeLoadAll(content, async function (inputObject: any) {
|
||||
if (!!inputObject.kind && !!inputObject.metadata && !!inputObject.metadata.name) {
|
||||
let kind: string = inputObject.kind;
|
||||
switch (kind.toLowerCase()) {
|
||||
case 'deployment':
|
||||
case 'daemonset':
|
||||
case 'statefulset':
|
||||
await checkRolloutStatus(inputObject.metadata.name, kind, namespace);
|
||||
break;
|
||||
default:
|
||||
core.debug(`No rollout check for kind: ${inputObject.kind}`)
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getManifestFileName(kind: string, name: string) {
|
||||
const filePath = kind + '_' + name + '_' + getCurrentTime().toString();
|
||||
const tempDirectory = process.env['RUNNER_TEMP'];
|
||||
const fileName = path.join(tempDirectory, path.basename(filePath));
|
||||
return fileName;
|
||||
}
|
||||
|
||||
function writeObjectsToFile(inputObjects: any[]): string[] {
|
||||
const newFilePaths = [];
|
||||
|
||||
if (!!inputObjects) {
|
||||
inputObjects.forEach((inputObject: any) => {
|
||||
try {
|
||||
const inputObjectString = JSON.stringify(inputObject);
|
||||
if (!!inputObject.kind && !!inputObject.metadata && !!inputObject.metadata.name) {
|
||||
const fileName = getManifestFileName(inputObject.kind, inputObject.metadata.name);
|
||||
fs.writeFileSync(path.join(fileName), inputObjectString);
|
||||
newFilePaths.push(fileName);
|
||||
} else {
|
||||
core.debug('Input object is not proper K8s resource object. Object: ' + inputObjectString);
|
||||
}
|
||||
} catch (ex) {
|
||||
core.debug('Exception occurred while wrting object to file : ' + inputObject + ' . Exception: ' + ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return newFilePaths;
|
||||
}
|
||||
|
||||
function updateManifests(manifests: string[], imagesToOverride: string, imagepullsecrets: string): string[] {
|
||||
const newObjectsList = [];
|
||||
manifests.forEach((filePath: string) => {
|
||||
let fileContents = fs.readFileSync(filePath).toString();
|
||||
fileContents = updateContainerImagesInManifestFiles(fileContents, imagesToOverride.split('\n'));
|
||||
yaml.safeLoadAll(fileContents, function (inputObject: any) {
|
||||
if (!!imagepullsecrets && !!inputObject && !!inputObject.kind) {
|
||||
if (isWorkloadEntity(inputObject.kind)) {
|
||||
updateImagePullSecrets(inputObject, imagepullsecrets.split('\n'));
|
||||
}
|
||||
}
|
||||
newObjectsList.push(inputObject);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
return writeObjectsToFile(newObjectsList);
|
||||
}
|
||||
|
||||
async function installKubectl(version: string) {
|
||||
if (isEqual(version, 'latest')) {
|
||||
version = await getStableKubectlVersion();
|
||||
}
|
||||
return await downloadKubectl(version);
|
||||
}
|
||||
|
||||
function checkClusterContext() {
|
||||
if (!process.env["KUBECONFIG"]) {
|
||||
throw new Error('Cluster context not set. Use k8ssetcontext action to set cluster context');
|
||||
}
|
||||
}
|
||||
|
||||
async function run() {
|
||||
checkClusterContext();
|
||||
await setKubectlPath();
|
||||
let manifestsInput = core.getInput('manifests');
|
||||
if (!manifestsInput) {
|
||||
core.setFailed('No manifests supplied to deploy');
|
||||
}
|
||||
let namespace = core.getInput('namespace');
|
||||
if (!namespace) {
|
||||
namespace = 'default';
|
||||
}
|
||||
|
||||
let manifests = manifestsInput.split('\n');
|
||||
const imagesToOverride = core.getInput('images');
|
||||
const imagePullSecretsToAdd = core.getInput('imagepullsecrets');
|
||||
if (!!imagePullSecretsToAdd || !!imagesToOverride) {
|
||||
manifests = updateManifests(manifests, imagesToOverride, imagePullSecretsToAdd)
|
||||
}
|
||||
await deploy(manifests, namespace);
|
||||
await checkManifestsStability(manifests, namespace);
|
||||
}
|
||||
|
||||
run().catch(core.setFailed);
|
|
@ -0,0 +1,22 @@
|
|||
import * as os from 'os';
|
||||
|
||||
export function isEqual(str1: string, str2: string) {
|
||||
if (!str1) str1 = "";
|
||||
if (!str2) str2 = "";
|
||||
return str1.toLowerCase() === str2.toLowerCase();
|
||||
}
|
||||
|
||||
export function getRandomInt(max: number) {
|
||||
return Math.floor(Math.random() * Math.floor(max));
|
||||
}
|
||||
|
||||
export function getExecutableExtension(): string {
|
||||
if (os.type().match(/^Win/)) {
|
||||
return '.exe';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
export function getCurrentTime(): number {
|
||||
return new Date().getTime();
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES6",
|
||||
"module": "commonjs"
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
Загрузка…
Ссылка в новой задаче