Skip to content

Commit

Permalink
v4 new release (#253)
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] authored Oct 31, 2022
1 parent 2ee6236 commit a2de818
Show file tree
Hide file tree
Showing 8 changed files with 419 additions and 49 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Following are the key capabilities of this action:
</tr>
<tr>
<td>manifests </br></br>(Required)</td>
<td>Path to the manifest files to be used for deployment. These can also be directories containing manifest files, in which case, all manifest files in the referenced directory at every depth will be deployed. Files not ending in .yml or .yaml will be ignored.</td>
<td>Path to the manifest files to be used for deployment. These can also be directories containing manifest files, in which case, all manifest files in the referenced directory at every depth will be deployed, or URLs to manifest files (like https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/controllers/nginx-deployment.yaml). Files and URLs not ending in .yml or .yaml will be ignored.</td>
</tr>
<tr>
<td>strategy </br></br>(Required)</td>
Expand Down Expand Up @@ -471,3 +471,7 @@ 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.

## Support

k8s-deploy is an open source project that is [**not** covered by the Microsoft Azure support policy](https://support.microsoft.com/en-us/help/2941892/support-for-linux-and-open-source-technology-in-azure). [Please search open issues here](https://github.com/Azure/k8s-deploy/issues), and if your issue isn't already represented please [open a new one](https://github.com/Azure/k8s-deploy/issues/new/choose). The project maintainers will respond to the best of their abilities.
3 changes: 2 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ module.exports = {
transform: {
'^.+\\.ts$': 'ts-jest'
},
verbose: true
verbose: true,
testTimeout: 9000
}
198 changes: 174 additions & 24 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20221,7 +20221,7 @@ function run() {
.split(/[\n,;]+/) // split into each individual manifest
.map((manifest) => manifest.trim()) // remove surrounding whitespace
.filter((manifest) => manifest.length > 0); // remove any blanks
const fullManifestFilePaths = fileUtils_1.getFilesFromDirectories(manifestFilePaths);
const fullManifestFilePaths = yield fileUtils_1.getFilesFromDirectoriesAndURLs(manifestFilePaths);
const kubectlPath = yield kubectl_1.getKubectlPath();
const namespace = core.getInput('namespace') || 'default';
const isPrivateCluster = core.getInput('private-cluster').toLowerCase() === 'true';
Expand Down Expand Up @@ -21867,6 +21867,53 @@ class DockerExec {
exports.DockerExec = DockerExec;


/***/ }),

/***/ 9939:
/***/ ((__unused_webpack_module, exports) => {

"use strict";

Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getErrorMessage = exports.combine = exports.map = exports.failed = exports.succeeded = void 0;
function succeeded(e) {
return e.succeeded;
}
exports.succeeded = succeeded;
function failed(e) {
return !e.succeeded;
}
exports.failed = failed;
function map(e, fn) {
if (failed(e)) {
return { succeeded: false, error: e.error };
}
return { succeeded: true, result: fn(e.result) };
}
exports.map = map;
function combine(es) {
const failures = es.filter(failed);
if (failures.length > 0) {
return {
succeeded: false,
error: failures.map((f) => f.error).join('\n')
};
}
return {
succeeded: true,
result: es.map((e) => e.result)
};
}
exports.combine = combine;
function getErrorMessage(error) {
if (error instanceof Error) {
return error.message;
}
return String(error);
}
exports.getErrorMessage = getErrorMessage;


/***/ }),

/***/ 3067:
Expand Down Expand Up @@ -22199,7 +22246,11 @@ class PrivateKubectl extends kubectl_1.Kubectl {
args.unshift('kubectl');
let kubectlCmd = args.join(' ');
let addFileFlag = false;
let eo = { silent };
let eo = {
silent: true,
failOnStdErr: false,
ignoreReturnCode: true
};
if (this.containsFilenames(kubectlCmd)) {
// For private clusters, files will referenced solely by their basename
kubectlCmd = this.replaceFilnamesWithBasenames(kubectlCmd);
Expand Down Expand Up @@ -22231,7 +22282,18 @@ class PrivateKubectl extends kubectl_1.Kubectl {
}
}
core.debug(`private cluster Kubectl run with invoke command: ${kubectlCmd}`);
return yield exec_1.getExecOutput('az', privateClusterArgs, eo);
const runOutput = yield exec_1.getExecOutput('az', [...privateClusterArgs, '-o', 'json'], eo);
const runObj = JSON.parse(runOutput.stdout);
if (!silent)
core.info(runObj.logs);
if (runOutput.exitCode !== 0 && runObj.exitCode !== 0) {
throw Error(`failed private cluster Kubectl command: ${kubectlCmd}`);
}
return {
exitCode: runObj.exitCode,
stdout: runObj.logs,
stderr: ''
};
});
}
replaceFilnamesWithBasenames(kubectlCmd) {
Expand Down Expand Up @@ -22443,17 +22505,31 @@ exports.checkDockerPath = checkDockerPath;
/***/ }),

/***/ 7446:
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {

"use strict";

var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
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) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getFilesFromDirectories = exports.writeManifestToFile = exports.writeObjectsToFile = exports.getTempDirectory = void 0;
exports.writeYamlFromURLToFile = exports.getFilesFromDirectoriesAndURLs = exports.writeManifestToFile = exports.writeObjectsToFile = exports.getTempDirectory = exports.urlFileKind = void 0;
const fs = __nccwpck_require__(7147);
const https = __nccwpck_require__(5687);
const path = __nccwpck_require__(1017);
const core = __nccwpck_require__(6024);
const os = __nccwpck_require__(2037);
const yaml = __nccwpck_require__(3607);
const errorable_1 = __nccwpck_require__(9939);
const timeUtils_1 = __nccwpck_require__(4046);
const githubUtils_1 = __nccwpck_require__(9976);
exports.urlFileKind = 'urlfile';
function getTempDirectory() {
return process.env['runner.tempDirectory'] || os.tmpdir();
}
Expand Down Expand Up @@ -22499,30 +22575,104 @@ function getManifestFileName(kind, name) {
const tempDirectory = getTempDirectory();
return path.join(tempDirectory, path.basename(filePath));
}
function getFilesFromDirectories(filePaths) {
const fullPathSet = new Set();
filePaths.forEach((fileName) => {
try {
if (fs.lstatSync(fileName).isDirectory()) {
recurisveManifestGetter(fileName).forEach((file) => {
fullPathSet.add(file);
});
}
else if (getFileExtension(fileName) === 'yml' ||
getFileExtension(fileName) === 'yaml') {
fullPathSet.add(fileName);
function getFilesFromDirectoriesAndURLs(filePaths) {
return __awaiter(this, void 0, void 0, function* () {
const fullPathSet = new Set();
let fileCounter = 0;
for (const fileName of filePaths) {
try {
if (githubUtils_1.isHttpUrl(fileName)) {
try {
const tempFilePath = yield writeYamlFromURLToFile(fileName, fileCounter++);
fullPathSet.add(tempFilePath);
}
catch (e) {
throw Error(`encountered error trying to pull YAML from URL ${fileName}: ${e}`);
}
}
else if (fs.lstatSync(fileName).isDirectory()) {
recurisveManifestGetter(fileName).forEach((file) => {
fullPathSet.add(file);
});
}
else if (getFileExtension(fileName) === 'yml' ||
getFileExtension(fileName) === 'yaml') {
fullPathSet.add(fileName);
}
else {
core.debug(`Detected non-manifest file, ${fileName}, continuing... `);
}
}
else {
core.debug(`Detected non-manifest file, ${fileName}, continuing... `);
catch (ex) {
throw Error(`Exception occurred while reading the file ${fileName}: ${ex}`);
}
}
catch (ex) {
throw Error(`Exception occurred while reading the file ${fileName}: ${ex}`);
}
const arr = Array.from(fullPathSet);
return arr;
});
}
exports.getFilesFromDirectoriesAndURLs = getFilesFromDirectoriesAndURLs;
function writeYamlFromURLToFile(url, fileNumber) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
https
.get(url, (response) => __awaiter(this, void 0, void 0, function* () {
var _a;
const code = (_a = response.statusCode) !== null && _a !== void 0 ? _a : 0;
if (code >= 400) {
reject(Error(`received response status ${response.statusMessage} from url ${url}`));
}
const targetPath = getManifestFileName(exports.urlFileKind, fileNumber.toString());
// save the file to disk
const fileWriter = fs
.createWriteStream(targetPath)
.on('finish', () => {
const verification = verifyYaml(targetPath, url);
if (errorable_1.succeeded(verification)) {
core.debug(`outputting YAML contents from ${url} to ${targetPath}: ${JSON.stringify(verification.result)}`);
resolve(targetPath);
}
else {
reject(verification.error);
}
});
response.pipe(fileWriter);
}))
.on('error', (error) => {
reject(error);
});
});
});
return Array.from(fullPathSet);
}
exports.getFilesFromDirectories = getFilesFromDirectories;
exports.writeYamlFromURLToFile = writeYamlFromURLToFile;
function verifyYaml(filepath, url) {
const fileContents = fs.readFileSync(filepath).toString();
let inputObjects;
try {
inputObjects = yaml.safeLoadAll(fileContents);
}
catch (e) {
return {
succeeded: false,
error: `failed to parse manifest from url ${url}: ${e}`
};
}
if (!inputObjects || inputObjects.length == 0) {
return {
succeeded: false,
error: `failed to parse manifest from url ${url}: no objects detected in manifest`
};
}
for (const obj of inputObjects) {
if (!obj.kind || !obj.apiVersion || !obj.metadata) {
return {
succeeded: false,
error: `failed to parse manifest from ${url}: missing fields`
};
}
}
return { succeeded: true, result: inputObjects };
}
function recurisveManifestGetter(dirName) {
const toRet = [];
fs.readdirSync(dirName).forEach((fileName) => {
Expand Down
6 changes: 4 additions & 2 deletions src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {promote} from './actions/promote'
import {reject} from './actions/reject'
import {Action, parseAction} from './types/action'
import {parseDeploymentStrategy} from './types/deploymentStrategy'
import {getFilesFromDirectories} from './utilities/fileUtils'
import {getFilesFromDirectoriesAndURLs} from './utilities/fileUtils'
import {PrivateKubectl} from './types/privatekubectl'

export async function run() {
Expand All @@ -26,7 +26,9 @@ export async function run() {
.map((manifest) => manifest.trim()) // remove surrounding whitespace
.filter((manifest) => manifest.length > 0) // remove any blanks

const fullManifestFilePaths = getFilesFromDirectories(manifestFilePaths)
const fullManifestFilePaths = await getFilesFromDirectoriesAndURLs(
manifestFilePaths
)
const kubectlPath = await getKubectlPath()
const namespace = core.getInput('namespace') || 'default'
const isPrivateCluster =
Expand Down
48 changes: 48 additions & 0 deletions src/types/errorable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
export interface Succeeded<T> {
readonly succeeded: true
readonly result: T
}

export interface Failed {
readonly succeeded: false
readonly error: string
}

export type Errorable<T> = Succeeded<T> | Failed

export function succeeded<T>(e: Errorable<T>): e is Succeeded<T> {
return e.succeeded
}

export function failed<T>(e: Errorable<T>): e is Failed {
return !e.succeeded
}

export function map<T, U>(e: Errorable<T>, fn: (t: T) => U): Errorable<U> {
if (failed(e)) {
return {succeeded: false, error: e.error}
}
return {succeeded: true, result: fn(e.result)}
}

export function combine<T>(es: Errorable<T>[]): Errorable<T[]> {
const failures = es.filter(failed)
if (failures.length > 0) {
return {
succeeded: false,
error: failures.map((f) => f.error).join('\n')
}
}

return {
succeeded: true,
result: es.map((e) => (e as Succeeded<T>).result)
}
}

export function getErrorMessage(error: unknown) {
if (error instanceof Error) {
return error.message
}
return String(error)
}
26 changes: 24 additions & 2 deletions src/types/privatekubectl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ export class PrivateKubectl extends Kubectl {
args.unshift('kubectl')
let kubectlCmd = args.join(' ')
let addFileFlag = false
let eo = <ExecOptions>{silent}
let eo = <ExecOptions>{
silent: true,
failOnStdErr: false,
ignoreReturnCode: true
}

if (this.containsFilenames(kubectlCmd)) {
// For private clusters, files will referenced solely by their basename
Expand Down Expand Up @@ -52,7 +56,25 @@ export class PrivateKubectl extends Kubectl {
core.debug(
`private cluster Kubectl run with invoke command: ${kubectlCmd}`
)
return await getExecOutput('az', privateClusterArgs, eo)

const runOutput = await getExecOutput(
'az',
[...privateClusterArgs, '-o', 'json'],
eo
)
const runObj: {logs: string; exitCode: number} = JSON.parse(
runOutput.stdout
)
if (!silent) core.info(runObj.logs)
if (runOutput.exitCode !== 0 && runObj.exitCode !== 0) {
throw Error(`failed private cluster Kubectl command: ${kubectlCmd}`)
}

return {
exitCode: runObj.exitCode,
stdout: runObj.logs,
stderr: ''
} as ExecOutput
}

private replaceFilnamesWithBasenames(kubectlCmd: string) {
Expand Down
Loading

0 comments on commit a2de818

Please sign in to comment.