Skip to content

Commit

Permalink
Merge pull request #6611 from Microsoft/users/ansheno/kubernetesSecre…
Browse files Browse the repository at this point in the history
…tsSection

Added secrets section in Kubernetes Task
  • Loading branch information
Anumita authored Mar 8, 2018
2 parents a842453 + 2578ff7 commit b57a75d
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 58 deletions.
18 changes: 11 additions & 7 deletions Tasks/Kubernetes/Strings/resources.resjson/en-US/resources.resjson
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
"loc.description": "Deploy, configure, update your Kubernetes cluster in Azure Container Service by running kubectl commands.",
"loc.instanceNameFormat": "kubectl $(command)",
"loc.group.displayName.commands": "Commands",
"loc.group.displayName.registry": "Container Registry Details",
"loc.group.displayName.secrets": "Secrets",
"loc.group.displayName.advanced": "Advanced",
"loc.group.displayName.output": "Output",
"loc.input.label.kubernetesServiceEndpoint": "Kubernetes Service Connection",
"loc.input.help.kubernetesServiceEndpoint": "Select a Kubernetes service connection.",
"loc.input.label.namespace": "Namespace",
"loc.input.help.namespace": "Name of the namespace to run the command in.",
"loc.input.label.command": "Command",
"loc.input.help.command": "Select a kubectl command.",
"loc.input.label.useConfigurationFile": "Use Configuration files",
Expand All @@ -17,6 +19,10 @@
"loc.input.help.configuration": "Filename, directory, or URL to kubernetes configuration files that will be used with the commands.",
"loc.input.label.arguments": "Arguments",
"loc.input.help.arguments": "Command arguments.",
"loc.input.label.secretType": "Type of secret",
"loc.input.help.secretType": "Select a type of secret",
"loc.input.label.secretArguments": "Arguments",
"loc.input.help.secretArguments": "Specify keys and literal values to insert in secret.For example, --from-literal=key1=value1 --from-literal=key2=\"top secret\". Please use double quotes to specify any literals that have spaces.",
"loc.input.label.containerRegistryType": "Container Registry type",
"loc.input.help.containerRegistryType": "Select a Container registry type.",
"loc.input.label.dockerRegistryEndpoint": "Docker Registry Connection",
Expand All @@ -26,27 +32,25 @@
"loc.input.label.azureContainerRegistry": "Azure Container Registry",
"loc.input.help.azureContainerRegistry": "Select an Azure Container Registry. Required for commands that need to authenticate with a registry.",
"loc.input.label.secretName": "Secret name",
"loc.input.help.secretName": "Name of the docker-registry secret. You can use this secret name in the Kubernetes YAML configuration file.",
"loc.input.help.secretName": "Name of the secret. You can use this secret name in the Kubernetes YAML configuration file.",
"loc.input.label.forceUpdate": "Force update secret",
"loc.input.help.forceUpdate": "Delete the docker-registry secret if it exists and create a new one with updated values.",
"loc.input.help.forceUpdate": "Delete the secret if it exists and create a new one with updated values.",
"loc.input.label.versionOrLocation": "Kubectl",
"loc.input.label.versionSpec": "Version Spec",
"loc.input.help.versionSpec": "Version Spec of version to get. Examples: 1.7.0, 1.x.0, 4.x.0, 6.10.0, >=6.10.0",
"loc.input.label.checkLatest": "Check for Latest Version",
"loc.input.help.checkLatest": "Always checks online for the latest available version (stable.txt) that satisfies the version spec. This is typically false unless you have a specific scenario to always get latest. This will cause it to incur download costs when potentially not necessary, especially with the hosted build pool.",
"loc.input.label.specifyLocation": "Path to Kubectl",
"loc.input.help.specifyLocation": "Full path to the kubectl.exe",
"loc.input.label.namespace": "Namespace",
"loc.input.help.namespace": "Name of the namespace to run the command in.",
"loc.input.label.cwd": "Working directory",
"loc.input.help.cwd": "Working directory for the Kubectl command.",
"loc.input.label.outputFormat": "Output format",
"loc.input.help.outputFormat": "Output format.",
"loc.input.label.kubectlOutput": "Output variable name",
"loc.input.help.kubectlOutput": "Name of the variable in which output of the command should be saved.",
"loc.messages.DownloadingClient": "Downloading kubernetes client.",
"loc.messages.CreatingSecret": "Executing create docker-registry %s secret.",
"loc.messages.DeleteSecret": "Executing delete docker-registry %s secret",
"loc.messages.CreatingSecret": "Executing create %s secret.",
"loc.messages.DeleteSecret": "Executing delete %s secret",
"loc.messages.DockerRegistryConnectionNotSpecified": "Docker Registry connection details not specified",
"loc.messages.FileNotFoundException": "Can not find file at location: %s",
"loc.messages.DownloadingKubeCtlFromUrl": "Downloading Kubectl from Url: %s",
Expand Down
43 changes: 43 additions & 0 deletions Tasks/Kubernetes/Tests/L0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ describe('Kubernetes Suite', function() {
delete process.env[shared.TestEnvVars.namespace];
delete process.env[shared.TestEnvVars.arguments];
delete process.env[shared.TestEnvVars.useConfigurationFile];
delete process.env[shared.TestEnvVars.secretType];
delete process.env[shared.TestEnvVars.secretArguments];
delete process.env[shared.TestEnvVars.secretName];
delete process.env[shared.TestEnvVars.forceUpdate];
delete process.env[shared.TestEnvVars.outputFormat];
Expand Down Expand Up @@ -269,6 +271,47 @@ describe('Kubernetes Suite', function() {
done();
});

it('Runs successfully for kubectl generic secrets with forceUpdate', (done:MochaDone) => {
let tp = path.join(__dirname, 'TestSetup.js');
let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp);
process.env[shared.TestEnvVars.command] = shared.Commands.get;
process.env[shared.TestEnvVars.arguments] = "pods";
process.env[shared.TestEnvVars.secretType] = "generic";
process.env[shared.TestEnvVars.secretArguments] = "--from-literal=key1=value1 --from-literal=key2=value2";
process.env[shared.TestEnvVars.secretName] = "my-secret";
tr.run();

assert(tr.invokedToolCount == 2, 'should have invoked tool one times. actual: ' + tr.invokedToolCount);
assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr');
assert(tr.succeeded, 'task should have succeeded');
assert(tr.stdout.indexOf(`DeleteSecret my-secret`) != -1, "kubectl delete should run");
assert(tr.stdout.indexOf(`[command]kubectl --kubeconfig ${shared.formatPath("newUserDir/config")} create secret generic my-secret --from-literal=key1=value1 --from-literal=key2=value2`) != -1, "kubectl create should run");
assert(tr.stdout.indexOf(`[command]kubectl --kubeconfig ${shared.formatPath("newUserDir/config")} get pods`) != -1, "kubectl get should run");
console.log(tr.stderr);
done();
});

it('Runs successfully for kubectl generic secrets without forceUpdate', (done:MochaDone) => {
let tp = path.join(__dirname, 'TestSetup.js');
let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp);
process.env[shared.TestEnvVars.command] = shared.Commands.get;
process.env[shared.TestEnvVars.arguments] = "pods";
process.env[shared.TestEnvVars.secretType] = "generic";
process.env[shared.TestEnvVars.secretArguments] = "--from-literal=key1=value1 --from-literal=key2=value2";
process.env[shared.TestEnvVars.secretName] = "my-secret";
process.env[shared.TestEnvVars.forceUpdate] = "false";
tr.run();

assert(tr.invokedToolCount == 2, 'should have invoked tool one times. actual: ' + tr.invokedToolCount);
assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr');
assert(tr.succeeded, 'task should have succeeded');
assert(tr.stdout.indexOf(`DeleteSecret my-secret`) == -1, "kubectl delete should not run");
assert(tr.stdout.indexOf(`[command]kubectl --kubeconfig ${shared.formatPath("newUserDir/config")} create secret generic my-secret --from-literal=key1=value1 --from-literal=key2=value2`) != -1, "kubectl create should run");
assert(tr.stdout.indexOf(`[command]kubectl --kubeconfig ${shared.formatPath("newUserDir/config")} get pods`) != -1, "kubectl get should run");
console.log(tr.stderr);
done();
});

it('Runs successfully for kubectl get and print the output in a particular format', (done:MochaDone) => {
let tp = path.join(__dirname, 'TestSetup.js');
let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp);
Expand Down
5 changes: 5 additions & 0 deletions Tasks/Kubernetes/Tests/TestSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ tr.setInput('useConfigurationFile', process.env[shared.TestEnvVars.useConfigurat
tr.setInput('configuration', ConfigurationFilePath);
tr.setInput('arguments', process.env[shared.TestEnvVars.arguments] || '');
tr.setInput('namespace', process.env[shared.TestEnvVars.namespace] || '');
tr.setInput('secretType', process.env[shared.TestEnvVars.secretType] || 'dockerRegistry');
tr.setInput('secretArguments', process.env[shared.TestEnvVars.secretArguments] || '');
tr.setInput('secretName', process.env[shared.TestEnvVars.secretName] || '');
tr.setInput('forceUpdate', process.env[shared.TestEnvVars.forceUpdate] || "true");
tr.setInput('versionOrLocation', process.env[shared.TestEnvVars.versionOrLocation] || 'version');
Expand Down Expand Up @@ -98,6 +100,9 @@ a.exec[`kubectl --kubeconfig ${KubconfigFile} create secret docker-registry my-s
a.exec[`kubectl --kubeconfig ${KubconfigFile} create secret docker-registry my-secret --docker-server=https://index.docker.io/v1/ --docker-username=test --docker-password=regpassword [email protected]`] = {
"code": 0
};
a.exec[`kubectl --kubeconfig ${KubconfigFile} create secret generic my-secret --from-literal=key1=value1 --from-literal=key2=value2`] = {
"code": 0
};
a.exec[`kubectl --kubeconfig ${KubconfigFile} get secrets my-secret -o yaml`] = {
"code": 0,
"stdout": "successfully got secret my-secret and printed it in the specified format"
Expand Down
2 changes: 2 additions & 0 deletions Tasks/Kubernetes/Tests/TestShared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export let TestEnvVars = {
configuration: "__configuration__",
arguments: "__arguments__",
namespace: "__namespace__",
secretType: "__secretType__",
secretArguments: "__secretArguments__",
secretName: "__secretName__",
forceUpdate: "__forceUpdate__",
versionOrLocation: "__versionOrLocation__",
Expand Down
4 changes: 2 additions & 2 deletions Tasks/Kubernetes/src/kubernetes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import path = require('path');

import ClusterConnection from "./clusterconnection";
import * as kubectl from "./kubernetescommand";
import * as kubectlRegistrySecret from "./kubernetesregistrysecret";
import * as kubectlSecret from "./kubernetessecret";

import AuthenticationTokenProvider from "docker-common/registryauthenticationprovider/authenticationtokenprovider"
import ACRAuthenticationTokenProvider from "docker-common/registryauthenticationprovider/acrauthenticationtokenprovider"
Expand Down Expand Up @@ -42,7 +42,7 @@ function run(clusterConnection: ClusterConnection, registryAuthenticationToken:
var secretName = tl.getInput("secretName", false);

if(secretName) {
kubectlRegistrySecret.run(clusterConnection, registryAuthenticationToken, secretName).fin(function cleanup(){
kubectlSecret.run(clusterConnection, registryAuthenticationToken, secretName).fin(function cleanup(){
clusterConnection.close();
}).then(function success() {
executeKubectlCommand(clusterConnection);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,32 @@ import ClusterConnection from "./clusterconnection";
import AuthenticationToken from "docker-common/registryauthenticationprovider/registryauthenticationtoken"

export function run(connection: ClusterConnection, authenticationToken: AuthenticationToken, secret: string): any {

if(tl.getBoolInput("forceUpdate") == true) {
return deleteSecret(connection, secret).fin(() =>{
return createSecret(connection, authenticationToken, secret);
});
} else {
return createSecret(connection, authenticationToken, secret);
}
}

function createSecret(connection: ClusterConnection, authenticationToken: AuthenticationToken, secret: string): any {
var typeOfSecret = tl.getInput("secretType", true);
if (typeOfSecret === "dockerRegistry")
{
return createDockerRegistrySecret(connection, authenticationToken, secret);
}
else if (typeOfSecret === "generic")
{
return createGenericSecret(connection, secret);
}
}

function deleteSecret(connection: ClusterConnection, secret: string): any {
tl.debug(tl.loc('DeleteSecret', secret));
var command = connection.createCommand();
command.arg(getNameSpace());
command.arg("delete");
command.arg("secret");
command.arg(secret);
Expand All @@ -33,12 +46,13 @@ function deleteSecret(connection: ClusterConnection, secret: string): any {
return connection.execCommand(command, executionOption);
}

function createSecret(connection: ClusterConnection, authenticationToken: AuthenticationToken, secret: string): any {
function createDockerRegistrySecret(connection: ClusterConnection, authenticationToken: AuthenticationToken, secret: string): any {

if(authenticationToken)
{
tl.debug(tl.loc('CreatingSecret', secret));
var command = connection.createCommand();
command.arg(getNameSpace());
command.arg("create")
command.arg("secret");
command.arg("docker-registry");
Expand All @@ -47,6 +61,7 @@ function createSecret(connection: ClusterConnection, authenticationToken: Authen
command.arg("--docker-username="+ authenticationToken.getUsername());
command.arg("--docker-password="+ authenticationToken.getPassword());
command.arg("--docker-email="+ authenticationToken.getEmail());

return connection.execCommand(command);
}
else
Expand All @@ -55,4 +70,33 @@ function createSecret(connection: ClusterConnection, authenticationToken: Authen
throw new Error(tl.loc("DockerRegistryConnectionNotSpecified"));
}

}

function createGenericSecret(connection: ClusterConnection, secret: string): any {

tl.debug(tl.loc('CreatingSecret', secret));
var command = connection.createCommand();
command.arg(getNameSpace());
command.arg("create")
command.arg("secret");
command.arg("generic");
command.arg(secret);
var secretArguments = tl.getInput("secretArguments", false);
if (secretArguments)
{
command.line(secretArguments);
}

return connection.execCommand(command);
}

function getNameSpace(): string[] {
var args: string[] =[];
var namespace = tl.getInput("namespace", false);
if(namespace) {
args[0] = "-n";
args[1] = namespace;
}

return args;
}
Loading

0 comments on commit b57a75d

Please sign in to comment.