mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-02-05 13:25:19 +02:00
Add kubernetesDeploy step (#1073)
* Add kubernetesDeploy step Co-authored-by: Sven Merk <33895725+nevskrem@users.noreply.github.com>
This commit is contained in:
parent
7ead134d68
commit
73ab887f25
@ -9,7 +9,7 @@ RUN go test ./... -cover
|
||||
# execute build
|
||||
RUN export GIT_COMMIT=$(git rev-parse HEAD) && \
|
||||
export GIT_REPOSITORY=$(git config --get remote.origin.url) && \
|
||||
go build \
|
||||
CGO_ENABLED=0 go build \
|
||||
-ldflags \
|
||||
"-X github.com/SAP/jenkins-library/cmd.GitCommit=${GIT_COMMIT} \
|
||||
-X github.com/SAP/jenkins-library/pkg/log.LibraryRepository=${GIT_REPOSITORY}" \
|
||||
|
@ -98,6 +98,11 @@ func generateConfig() error {
|
||||
return errors.Wrap(err, "getting step config failed")
|
||||
}
|
||||
|
||||
// apply context conditions if context configuration is requested
|
||||
if configOptions.contextConfig {
|
||||
applyContextConditions(metadata, &stepConfig)
|
||||
}
|
||||
|
||||
myConfigJSON, _ := config.GetJSON(stepConfig.Config)
|
||||
|
||||
fmt.Println(myConfigJSON)
|
||||
@ -129,3 +134,33 @@ func defaultsAndFilters(metadata *config.StepData, stepName string) ([]io.ReadCl
|
||||
//ToDo: retrieve default values from metadata
|
||||
return nil, metadata.GetParameterFilters(), nil
|
||||
}
|
||||
|
||||
func applyContextConditions(metadata config.StepData, stepConfig *config.StepConfig) {
|
||||
//consider conditions for context configuration
|
||||
|
||||
//containers
|
||||
applyContainerConditions(metadata.Spec.Containers, stepConfig)
|
||||
|
||||
//sidecars
|
||||
applyContainerConditions(metadata.Spec.Sidecars, stepConfig)
|
||||
|
||||
//ToDo: remove all unnecessary sub maps?
|
||||
// e.g. extract delete() from applyContainerConditions - loop over all stepConfig.Config[param.Value] and remove ...
|
||||
}
|
||||
|
||||
func applyContainerConditions(containers []config.Container, stepConfig *config.StepConfig) {
|
||||
for _, container := range containers {
|
||||
if len(container.Conditions) > 0 {
|
||||
for _, param := range container.Conditions[0].Params {
|
||||
if container.Conditions[0].ConditionRef == "strings-equal" && stepConfig.Config[param.Name] == param.Value {
|
||||
var containerConf map[string]interface{}
|
||||
containerConf = stepConfig.Config[param.Value].(map[string]interface{})
|
||||
for key, value := range containerConf {
|
||||
stepConfig.Config[key] = value
|
||||
}
|
||||
delete(stepConfig.Config, param.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
@ -86,5 +87,93 @@ func TestDefaultsAndFilters(t *testing.T) {
|
||||
assert.Equal(t, 1, len(filters.All), "wrong number of filter values")
|
||||
assert.NoError(t, err, "error occured but none expected")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestApplyContextConditions(t *testing.T) {
|
||||
|
||||
tt := []struct {
|
||||
metadata config.StepData
|
||||
conf config.StepConfig
|
||||
expected map[string]interface{}
|
||||
}{
|
||||
{
|
||||
metadata: config.StepData{Spec: config.StepSpec{Containers: []config.Container{}}},
|
||||
conf: config.StepConfig{Config: map[string]interface{}{}},
|
||||
expected: map[string]interface{}{},
|
||||
},
|
||||
{
|
||||
metadata: config.StepData{Spec: config.StepSpec{Containers: []config.Container{
|
||||
{
|
||||
Image: "myTestImage:latest",
|
||||
Conditions: []config.Condition{
|
||||
{
|
||||
ConditionRef: "strings-equal",
|
||||
Params: []config.Param{
|
||||
{Name: "param1", Value: "val2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}},
|
||||
conf: config.StepConfig{Config: map[string]interface{}{
|
||||
"param1": "val1",
|
||||
"val1": map[string]interface{}{"dockerImage": "myTestImage:latest"},
|
||||
}},
|
||||
expected: map[string]interface{}{
|
||||
"param1": "val1",
|
||||
"val1": map[string]interface{}{"dockerImage": "myTestImage:latest"},
|
||||
},
|
||||
},
|
||||
{
|
||||
metadata: config.StepData{Spec: config.StepSpec{Containers: []config.Container{
|
||||
{
|
||||
Image: "myTestImage:latest",
|
||||
Conditions: []config.Condition{
|
||||
{
|
||||
ConditionRef: "strings-equal",
|
||||
Params: []config.Param{
|
||||
{Name: "param1", Value: "val1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}},
|
||||
conf: config.StepConfig{Config: map[string]interface{}{
|
||||
"param1": "val1",
|
||||
"val1": map[string]interface{}{"dockerImage": "myTestImage:latest"},
|
||||
}},
|
||||
expected: map[string]interface{}{
|
||||
"param1": "val1",
|
||||
"dockerImage": "myTestImage:latest",
|
||||
},
|
||||
},
|
||||
{
|
||||
metadata: config.StepData{Spec: config.StepSpec{Sidecars: []config.Container{
|
||||
{
|
||||
Image: "myTestImage:latest",
|
||||
Conditions: []config.Condition{
|
||||
{
|
||||
ConditionRef: "strings-equal",
|
||||
Params: []config.Param{
|
||||
{Name: "param1", Value: "val1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}},
|
||||
conf: config.StepConfig{Config: map[string]interface{}{
|
||||
"param1": "val1",
|
||||
"val1": map[string]interface{}{"dockerImage": "myTestImage:latest"},
|
||||
}},
|
||||
expected: map[string]interface{}{
|
||||
"param1": "val1",
|
||||
"dockerImage": "myTestImage:latest",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for run, test := range tt {
|
||||
applyContextConditions(test.metadata, &test.conf)
|
||||
assert.Equalf(t, test.expected, test.conf.Config, fmt.Sprintf("Run %v failed", run))
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,14 @@ type execRunner interface {
|
||||
Stderr(err io.Writer)
|
||||
}
|
||||
|
||||
type envExecRunner interface {
|
||||
RunExecutable(e string, p ...string) error
|
||||
Dir(d string)
|
||||
Env(e []string)
|
||||
Stdout(out io.Writer)
|
||||
Stderr(err io.Writer)
|
||||
}
|
||||
|
||||
type shellRunner interface {
|
||||
RunShell(s string, c string) error
|
||||
Dir(d string)
|
||||
|
231
cmd/kubernetesDeploy.go
Normal file
231
cmd/kubernetesDeploy.go
Normal file
@ -0,0 +1,231 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/SAP/jenkins-library/pkg/command"
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
)
|
||||
|
||||
func kubernetesDeploy(config kubernetesDeployOptions) error {
|
||||
c := command.Command{}
|
||||
// reroute stderr output to logging framework, stdout will be used for command interactions
|
||||
c.Stderr(log.Entry().Writer())
|
||||
runKubernetesDeploy(config, &c, log.Entry().Writer())
|
||||
return nil
|
||||
}
|
||||
|
||||
func runKubernetesDeploy(config kubernetesDeployOptions, command envExecRunner, stdout io.Writer) {
|
||||
if config.DeployTool == "helm" {
|
||||
runHelmDeploy(config, command, stdout)
|
||||
} else {
|
||||
runKubectlDeploy(config, command)
|
||||
}
|
||||
}
|
||||
|
||||
func runHelmDeploy(config kubernetesDeployOptions, command envExecRunner, stdout io.Writer) {
|
||||
_, containerRegistry, err := splitRegistryURL(config.ContainerRegistryURL)
|
||||
if err != nil {
|
||||
log.Entry().WithError(err).Fatalf("Container registry url '%v' incorrect", config.ContainerRegistryURL)
|
||||
}
|
||||
containerImageName, containerImageTag, err := splitFullImageName(config.Image)
|
||||
if err != nil {
|
||||
log.Entry().WithError(err).Fatalf("Container image '%v' incorrect", config.Image)
|
||||
}
|
||||
helmLogFields := map[string]interface{}{}
|
||||
helmLogFields["Chart Path"] = config.ChartPath
|
||||
helmLogFields["Namespace"] = config.Namespace
|
||||
helmLogFields["Deployment Name"] = config.DeploymentName
|
||||
helmLogFields["Context"] = config.KubeContext
|
||||
helmLogFields["Kubeconfig"] = config.KubeConfig
|
||||
log.Entry().WithFields(helmLogFields).Debug("Calling Helm")
|
||||
|
||||
helmEnv := []string{fmt.Sprintf("KUBECONFIG=%v", config.KubeConfig)}
|
||||
if len(config.TillerNamespace) > 0 {
|
||||
helmEnv = append(helmEnv, fmt.Sprintf("TILLER_NAMESPACE=%v", config.TillerNamespace))
|
||||
}
|
||||
log.Entry().Debugf("Helm Env: %v", helmEnv)
|
||||
command.Env(helmEnv)
|
||||
command.Stdout(stdout)
|
||||
|
||||
initParams := []string{"init", "--client-only"}
|
||||
if err := command.RunExecutable("helm", initParams...); err != nil {
|
||||
log.Entry().WithError(err).Fatal("Helm init called failed")
|
||||
}
|
||||
|
||||
var dockerRegistrySecret bytes.Buffer
|
||||
command.Stdout(&dockerRegistrySecret)
|
||||
kubeParams := []string{
|
||||
"--insecure-skip-tls-verify=true",
|
||||
"create",
|
||||
"secret",
|
||||
"docker-registry",
|
||||
"regsecret",
|
||||
fmt.Sprintf("--docker-server=%v", containerRegistry),
|
||||
fmt.Sprintf("--docker-username=%v", config.ContainerRegistryUser),
|
||||
fmt.Sprintf("--docker-password=%v", config.ContainerRegistryPassword),
|
||||
"--dry-run=true",
|
||||
"--output=json",
|
||||
}
|
||||
log.Entry().Infof("Calling kubectl create secret --dry-run=true ...")
|
||||
log.Entry().Debugf("kubectl parameters %v", kubeParams)
|
||||
if err := command.RunExecutable("kubectl", kubeParams...); err != nil {
|
||||
log.Entry().WithError(err).Fatal("Retrieving Docker config via kubectl failed")
|
||||
}
|
||||
log.Entry().Debugf("Secret created: %v", string(dockerRegistrySecret.Bytes()))
|
||||
|
||||
var dockerRegistrySecretData struct {
|
||||
Kind string `json:"kind"`
|
||||
Data struct {
|
||||
DockerConfJSON string `json:".dockerconfigjson"`
|
||||
} `json:"data"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
if err := json.Unmarshal(dockerRegistrySecret.Bytes(), &dockerRegistrySecretData); err != nil {
|
||||
log.Entry().WithError(err).Fatal("Reading docker registry secret json failed")
|
||||
}
|
||||
|
||||
ingressHosts := ""
|
||||
for i, h := range config.IngressHosts {
|
||||
ingressHosts += fmt.Sprintf(",ingress.hosts[%v]=%v", i, h)
|
||||
}
|
||||
|
||||
upgradeParams := []string{
|
||||
"upgrade",
|
||||
config.DeploymentName,
|
||||
config.ChartPath,
|
||||
"--install",
|
||||
"--force",
|
||||
"--namespace",
|
||||
config.Namespace,
|
||||
"--wait",
|
||||
"--timeout",
|
||||
strconv.Itoa(config.HelmDeployWaitSeconds),
|
||||
"--set",
|
||||
fmt.Sprintf("image.repository=%v/%v,image.tag=%v,secret.dockerconfigjson=%v%v", containerRegistry, containerImageName, containerImageTag, dockerRegistrySecretData.Data.DockerConfJSON, ingressHosts),
|
||||
}
|
||||
|
||||
if len(config.KubeContext) > 0 {
|
||||
upgradeParams = append(upgradeParams, "--kube-context", config.KubeContext)
|
||||
}
|
||||
|
||||
if len(config.AdditionalParameters) > 0 {
|
||||
upgradeParams = append(upgradeParams, config.AdditionalParameters...)
|
||||
}
|
||||
|
||||
command.Stdout(stdout)
|
||||
log.Entry().Info("Calling helm upgrade ...")
|
||||
log.Entry().Debugf("Helm parameters %v", upgradeParams)
|
||||
command.RunExecutable("helm", upgradeParams...)
|
||||
if err := command.RunExecutable("helm", upgradeParams...); err != nil {
|
||||
log.Entry().WithError(err).Fatal("Helm upgrade call failed")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func runKubectlDeploy(config kubernetesDeployOptions, command envExecRunner) {
|
||||
_, containerRegistry, err := splitRegistryURL(config.ContainerRegistryURL)
|
||||
if err != nil {
|
||||
log.Entry().WithError(err).Fatalf("Container registry url '%v' incorrect", config.ContainerRegistryURL)
|
||||
}
|
||||
|
||||
kubeParams := []string{
|
||||
"--insecure-skip-tls-verify=true",
|
||||
fmt.Sprintf("--namespace=%v", config.Namespace),
|
||||
}
|
||||
|
||||
if len(config.KubeConfig) > 0 {
|
||||
log.Entry().Info("Using KUBECONFIG environment for authentication.")
|
||||
kubeEnv := []string{fmt.Sprintf("KUBECONFIG=%v", config.KubeConfig)}
|
||||
command.Env(kubeEnv)
|
||||
if len(config.KubeContext) > 0 {
|
||||
kubeParams = append(kubeParams, fmt.Sprintf("--context=%v", config.KubeContext))
|
||||
}
|
||||
|
||||
} else {
|
||||
log.Entry().Info("Using --token parameter for authentication.")
|
||||
kubeParams = append(kubeParams, fmt.Sprintf("--server=%v", config.APIServer))
|
||||
kubeParams = append(kubeParams, fmt.Sprintf("--token=%v", config.KubeToken))
|
||||
}
|
||||
|
||||
if config.CreateDockerRegistrySecret {
|
||||
if len(config.ContainerRegistryUser)+len(config.ContainerRegistryPassword) == 0 {
|
||||
log.Entry().Fatal("Cannot create Container registry secret without proper registry username/password")
|
||||
}
|
||||
// first check if secret already exists
|
||||
kubeCheckParams := append(kubeParams, "get", "secret", config.ContainerRegistrySecret)
|
||||
if err := command.RunExecutable("kubectl", kubeCheckParams...); err != nil {
|
||||
log.Entry().Infof("Registry secret '%v' does not exist, let's create it ...", config.ContainerRegistrySecret)
|
||||
kubeSecretParams := append(
|
||||
kubeParams,
|
||||
"create",
|
||||
"secret",
|
||||
"docker-registry",
|
||||
config.ContainerRegistrySecret,
|
||||
fmt.Sprintf("--docker-server=%v", containerRegistry),
|
||||
fmt.Sprintf("--docker-username=%v", config.ContainerRegistryUser),
|
||||
fmt.Sprintf("--docker-password=%v", config.ContainerRegistryPassword),
|
||||
)
|
||||
log.Entry().Infof("Creating container registry secret '%v'", config.ContainerRegistrySecret)
|
||||
log.Entry().Debugf("Running kubectl with following parameters: %v", kubeSecretParams)
|
||||
if err := command.RunExecutable("kubectl", kubeSecretParams...); err != nil {
|
||||
log.Entry().WithError(err).Fatal("Creating container registry secret failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appTemplate, err := ioutil.ReadFile(config.AppTemplate)
|
||||
if err != nil {
|
||||
log.Entry().WithError(err).Fatalf("Error when reading appTemplate '%v'", config.AppTemplate)
|
||||
}
|
||||
|
||||
// Update image name in deployment yaml, expects placeholder like 'image: <image-name>'
|
||||
re := regexp.MustCompile(`image:[ ]*<image-name>`)
|
||||
appTemplate = []byte(re.ReplaceAllString(string(appTemplate), fmt.Sprintf("image: %v/%v", containerRegistry, config.Image)))
|
||||
|
||||
err = ioutil.WriteFile(config.AppTemplate, appTemplate, 0700)
|
||||
if err != nil {
|
||||
log.Entry().WithError(err).Fatalf("Error when updating appTemplate '%v'", config.AppTemplate)
|
||||
}
|
||||
|
||||
kubeApplyParams := append(kubeParams, "apply", "--filename", config.AppTemplate)
|
||||
if len(config.AdditionalParameters) > 0 {
|
||||
kubeApplyParams = append(kubeApplyParams, config.AdditionalParameters...)
|
||||
}
|
||||
|
||||
if err := command.RunExecutable("kubectl", kubeApplyParams...); err != nil {
|
||||
log.Entry().Debugf("Running kubectl with following parameters: %v", kubeApplyParams)
|
||||
log.Entry().WithError(err).Fatal("Deployment with kubectl failed.")
|
||||
}
|
||||
}
|
||||
|
||||
func splitRegistryURL(registryURL string) (protocol, registry string, err error) {
|
||||
parts := strings.Split(registryURL, "://")
|
||||
if len(parts) != 2 || len(parts[1]) == 0 {
|
||||
return "", "", fmt.Errorf("Failed to split registry url '%v'", registryURL)
|
||||
}
|
||||
return parts[0], parts[1], nil
|
||||
}
|
||||
|
||||
func splitFullImageName(image string) (imageName, tag string, err error) {
|
||||
parts := strings.Split(image, ":")
|
||||
switch len(parts) {
|
||||
case 0:
|
||||
return "", "", fmt.Errorf("Failed to split image name '%v'", image)
|
||||
case 1:
|
||||
if len(parts[0]) > 0 {
|
||||
return parts[0], "", nil
|
||||
}
|
||||
return "", "", fmt.Errorf("Failed to split image name '%v'", image)
|
||||
case 2:
|
||||
return parts[0], parts[1], nil
|
||||
}
|
||||
return "", "", fmt.Errorf("Failed to split image name '%v'", image)
|
||||
}
|
267
cmd/kubernetesDeploy_generated.go
Normal file
267
cmd/kubernetesDeploy_generated.go
Normal file
@ -0,0 +1,267 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/SAP/jenkins-library/pkg/config"
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type kubernetesDeployOptions struct {
|
||||
AdditionalParameters []string `json:"additionalParameters,omitempty"`
|
||||
APIServer string `json:"apiServer,omitempty"`
|
||||
AppTemplate string `json:"appTemplate,omitempty"`
|
||||
ChartPath string `json:"chartPath,omitempty"`
|
||||
ContainerRegistryPassword string `json:"containerRegistryPassword,omitempty"`
|
||||
ContainerRegistryURL string `json:"containerRegistryUrl,omitempty"`
|
||||
ContainerRegistryUser string `json:"containerRegistryUser,omitempty"`
|
||||
ContainerRegistrySecret string `json:"containerRegistrySecret,omitempty"`
|
||||
CreateDockerRegistrySecret bool `json:"createDockerRegistrySecret,omitempty"`
|
||||
DeploymentName string `json:"deploymentName,omitempty"`
|
||||
DeployTool string `json:"deployTool,omitempty"`
|
||||
HelmDeployWaitSeconds int `json:"helmDeployWaitSeconds,omitempty"`
|
||||
Image string `json:"image,omitempty"`
|
||||
IngressHosts []string `json:"ingressHosts,omitempty"`
|
||||
KubeConfig string `json:"kubeConfig,omitempty"`
|
||||
KubeContext string `json:"kubeContext,omitempty"`
|
||||
KubeToken string `json:"kubeToken,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
TillerNamespace string `json:"tillerNamespace,omitempty"`
|
||||
}
|
||||
|
||||
var myKubernetesDeployOptions kubernetesDeployOptions
|
||||
|
||||
// KubernetesDeployCommand Deployment to Kubernetes test or production namespace within the specified Kubernetes cluster.
|
||||
func KubernetesDeployCommand() *cobra.Command {
|
||||
metadata := kubernetesDeployMetadata()
|
||||
|
||||
var createKubernetesDeployCmd = &cobra.Command{
|
||||
Use: "kubernetesDeploy",
|
||||
Short: "Deployment to Kubernetes test or production namespace within the specified Kubernetes cluster.",
|
||||
Long: `Deployment to Kubernetes test or production namespace within the specified Kubernetes cluster.
|
||||
|
||||
!!! note "Deployment supports multiple deployment tools"
|
||||
Currently the following are supported:
|
||||
|
||||
* [Helm](https://helm.sh/) command line tool and [Helm Charts](https://docs.helm.sh/developing_charts/#charts).
|
||||
* [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/) and ` + "`" + `kubectl apply` + "`" + ` command.
|
||||
|
||||
## Helm
|
||||
Following helm command will be executed by default:
|
||||
|
||||
` + "`" + `` + "`" + `` + "`" + `
|
||||
helm upgrade <deploymentName> <chartPath> --install --force --namespace <namespace> --wait --timeout <helmDeployWaitSeconds> --set "image.repository=<yourRegistry>/<yourImageName>,image.tag=<yourImageTag>,secret.dockerconfigjson=<dockerSecret>,ingress.hosts[0]=<ingressHosts[0]>,,ingress.hosts[1]=<ingressHosts[1]>,...
|
||||
` + "`" + `` + "`" + `` + "`" + `
|
||||
|
||||
* ` + "`" + `yourRegistry` + "`" + ` will be retrieved from ` + "`" + `containerRegistryUrl` + "`" + `
|
||||
* ` + "`" + `yourImageName` + "`" + `, ` + "`" + `yourImageTag` + "`" + ` will be retrieved from ` + "`" + `image` + "`" + `
|
||||
* ` + "`" + `dockerSecret` + "`" + ` will be calculated with a call to ` + "`" + `kubectl create secret docker-registry regsecret --docker-server=<yourRegistry> --docker-username=<containerRegistryUser> --docker-password=<containerRegistryPassword> --dry-run=true --output=json'` + "`" + ``,
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
log.SetStepName("kubernetesDeploy")
|
||||
log.SetVerbose(GeneralConfig.Verbose)
|
||||
return PrepareConfig(cmd, &metadata, "kubernetesDeploy", &myKubernetesDeployOptions, config.OpenPiperFile)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
return kubernetesDeploy(myKubernetesDeployOptions)
|
||||
},
|
||||
}
|
||||
|
||||
addKubernetesDeployFlags(createKubernetesDeployCmd)
|
||||
return createKubernetesDeployCmd
|
||||
}
|
||||
|
||||
func addKubernetesDeployFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().StringSliceVar(&myKubernetesDeployOptions.AdditionalParameters, "additionalParameters", []string{}, "Defines additional parameters for \"helm install\" or \"kubectl apply\" command.")
|
||||
cmd.Flags().StringVar(&myKubernetesDeployOptions.APIServer, "apiServer", os.Getenv("PIPER_apiServer"), "Defines the Url of the API Server of the Kubernetes cluster.")
|
||||
cmd.Flags().StringVar(&myKubernetesDeployOptions.AppTemplate, "appTemplate", os.Getenv("PIPER_appTemplate"), "Defines the filename for the kubernetes app template (e.g. k8s_apptemplate.yaml)")
|
||||
cmd.Flags().StringVar(&myKubernetesDeployOptions.ChartPath, "chartPath", os.Getenv("PIPER_chartPath"), "Defines the chart path for deployments using helm.")
|
||||
cmd.Flags().StringVar(&myKubernetesDeployOptions.ContainerRegistryPassword, "containerRegistryPassword", os.Getenv("PIPER_containerRegistryPassword"), "Password for container registry access - typically provided by the CI/CD environment.")
|
||||
cmd.Flags().StringVar(&myKubernetesDeployOptions.ContainerRegistryURL, "containerRegistryUrl", os.Getenv("PIPER_containerRegistryUrl"), "http(s) url of the Container registry.")
|
||||
cmd.Flags().StringVar(&myKubernetesDeployOptions.ContainerRegistryUser, "containerRegistryUser", os.Getenv("PIPER_containerRegistryUser"), "Username for container registry access - typically provided by the CI/CD environment.")
|
||||
cmd.Flags().StringVar(&myKubernetesDeployOptions.ContainerRegistrySecret, "containerRegistrySecret", "regsecret", "Name of the container registry secret used for pulling containers from the registry.")
|
||||
cmd.Flags().BoolVar(&myKubernetesDeployOptions.CreateDockerRegistrySecret, "createDockerRegistrySecret", false, "Toggle to turn on Regsecret creation with a \"deployTool:kubectl\" deployment.")
|
||||
cmd.Flags().StringVar(&myKubernetesDeployOptions.DeploymentName, "deploymentName", os.Getenv("PIPER_deploymentName"), "Defines the name of the deployment.")
|
||||
cmd.Flags().StringVar(&myKubernetesDeployOptions.DeployTool, "deployTool", "kubectl", "Defines the tool which should be used for deployment.")
|
||||
cmd.Flags().IntVar(&myKubernetesDeployOptions.HelmDeployWaitSeconds, "helmDeployWaitSeconds", 300, "Number of seconds before helm deploy returns.")
|
||||
cmd.Flags().StringVar(&myKubernetesDeployOptions.Image, "image", os.Getenv("PIPER_image"), "Full name of the image to be deployed.")
|
||||
cmd.Flags().StringSliceVar(&myKubernetesDeployOptions.IngressHosts, "ingressHosts", []string{}, "List of ingress hosts to be exposed via helm deployment.")
|
||||
cmd.Flags().StringVar(&myKubernetesDeployOptions.KubeConfig, "kubeConfig", os.Getenv("PIPER_kubeConfig"), "Defines the path to the \"kubeconfig\" file.")
|
||||
cmd.Flags().StringVar(&myKubernetesDeployOptions.KubeContext, "kubeContext", os.Getenv("PIPER_kubeContext"), "Defines the context to use from the \"kubeconfig\" file.")
|
||||
cmd.Flags().StringVar(&myKubernetesDeployOptions.KubeToken, "kubeToken", os.Getenv("PIPER_kubeToken"), "Contains the id_token used by kubectl for authentication. Consider using kubeConfig parameter instead.")
|
||||
cmd.Flags().StringVar(&myKubernetesDeployOptions.Namespace, "namespace", "default", "Defines the target Kubernetes namespace for the deployment.")
|
||||
cmd.Flags().StringVar(&myKubernetesDeployOptions.TillerNamespace, "tillerNamespace", os.Getenv("PIPER_tillerNamespace"), "Defines optional tiller namespace for deployments using helm.")
|
||||
|
||||
cmd.MarkFlagRequired("chartPath")
|
||||
cmd.MarkFlagRequired("containerRegistryUrl")
|
||||
cmd.MarkFlagRequired("deploymentName")
|
||||
cmd.MarkFlagRequired("deployTool")
|
||||
cmd.MarkFlagRequired("image")
|
||||
}
|
||||
|
||||
// retrieve step metadata
|
||||
func kubernetesDeployMetadata() config.StepData {
|
||||
var theMetaData = config.StepData{
|
||||
Spec: config.StepSpec{
|
||||
Inputs: config.StepInputs{
|
||||
Parameters: []config.StepParameters{
|
||||
{
|
||||
Name: "additionalParameters",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "[]string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{{Name: "helmDeploymentParameters"}},
|
||||
},
|
||||
{
|
||||
Name: "apiServer",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{{Name: "k8sAPIServer"}},
|
||||
},
|
||||
{
|
||||
Name: "appTemplate",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{{Name: "k8sAppTemplate"}},
|
||||
},
|
||||
{
|
||||
Name: "chartPath",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: true,
|
||||
Aliases: []config.Alias{{Name: "helmChartPath"}},
|
||||
},
|
||||
{
|
||||
Name: "containerRegistryPassword",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "containerRegistryUrl",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: true,
|
||||
Aliases: []config.Alias{{Name: "dockerRegistryUrl"}},
|
||||
},
|
||||
{
|
||||
Name: "containerRegistryUser",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "containerRegistrySecret",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "createDockerRegistrySecret",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "bool",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "deploymentName",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: true,
|
||||
Aliases: []config.Alias{{Name: "helmDeploymentName"}},
|
||||
},
|
||||
{
|
||||
Name: "deployTool",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: true,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "helmDeployWaitSeconds",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "int",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "image",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: true,
|
||||
Aliases: []config.Alias{{Name: "deployImage"}},
|
||||
},
|
||||
{
|
||||
Name: "ingressHosts",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "[]string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "kubeConfig",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "kubeContext",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "kubeToken",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{},
|
||||
},
|
||||
{
|
||||
Name: "namespace",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{{Name: "helmDeploymentNamespace"}, {Name: "k8sDeploymentNamespace"}},
|
||||
},
|
||||
{
|
||||
Name: "tillerNamespace",
|
||||
ResourceRef: []config.ResourceReference{},
|
||||
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||
Type: "string",
|
||||
Mandatory: false,
|
||||
Aliases: []config.Alias{{Name: "helmTillerNamespace"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return theMetaData
|
||||
}
|
16
cmd/kubernetesDeploy_generated_test.go
Normal file
16
cmd/kubernetesDeploy_generated_test.go
Normal file
@ -0,0 +1,16 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestKubernetesDeployCommand(t *testing.T) {
|
||||
|
||||
testCmd := KubernetesDeployCommand()
|
||||
|
||||
// only high level testing performed - details are tested in step generation procudure
|
||||
assert.Equal(t, "kubernetesDeploy", testCmd.Use, "command name incorrect")
|
||||
|
||||
}
|
273
cmd/kubernetesDeploy_test.go
Normal file
273
cmd/kubernetesDeploy_test.go
Normal file
@ -0,0 +1,273 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRunKubernetesDeploy(t *testing.T) {
|
||||
|
||||
t.Run("test helm", func(t *testing.T) {
|
||||
opts := kubernetesDeployOptions{
|
||||
ContainerRegistryURL: "https://my.registry:55555",
|
||||
ContainerRegistryUser: "registryUser",
|
||||
ContainerRegistryPassword: "********",
|
||||
ChartPath: "path/to/chart",
|
||||
DeploymentName: "deploymentName",
|
||||
DeployTool: "helm",
|
||||
HelmDeployWaitSeconds: 400,
|
||||
IngressHosts: []string{"ingress.host1", "ingress.host2"},
|
||||
Image: "path/to/Image:latest",
|
||||
AdditionalParameters: []string{"--testParam", "testValue"},
|
||||
KubeContext: "testCluster",
|
||||
Namespace: "deploymentNamespace",
|
||||
}
|
||||
|
||||
dockerConfigJSON := `{"kind": "Secret","data":{".dockerconfigjson": "ThisIsOurBase64EncodedSecret=="}}`
|
||||
|
||||
e := execMockRunner{
|
||||
stdoutReturn: map[string]string{
|
||||
"kubectl --insecure-skip-tls-verify=true create secret docker-registry regsecret --docker-server=my.registry:55555 --docker-username=registryUser --docker-password=******** --dry-run=true --output=json": dockerConfigJSON,
|
||||
},
|
||||
}
|
||||
|
||||
var stdout bytes.Buffer
|
||||
|
||||
runKubernetesDeploy(opts, &e, &stdout)
|
||||
|
||||
assert.Equal(t, "helm", e.calls[0].exec, "Wrong init command")
|
||||
assert.Equal(t, []string{"init", "--client-only"}, e.calls[0].params, "Wrong init parameters")
|
||||
|
||||
assert.Equal(t, "kubectl", e.calls[1].exec, "Wrong secret creation command")
|
||||
assert.Equal(t, []string{"--insecure-skip-tls-verify=true", "create", "secret", "docker-registry", "regsecret", "--docker-server=my.registry:55555", "--docker-username=registryUser", "--docker-password=********", "--dry-run=true", "--output=json"}, e.calls[1].params, "Wrong secret creation parameters")
|
||||
|
||||
assert.Equal(t, "helm", e.calls[2].exec, "Wrong upgrade command")
|
||||
assert.Equal(t, []string{
|
||||
"upgrade",
|
||||
"deploymentName",
|
||||
"path/to/chart",
|
||||
"--install",
|
||||
"--force",
|
||||
"--namespace",
|
||||
"deploymentNamespace",
|
||||
"--wait",
|
||||
"--timeout",
|
||||
"400",
|
||||
"--set",
|
||||
"image.repository=my.registry:55555/path/to/Image,image.tag=latest,secret.dockerconfigjson=ThisIsOurBase64EncodedSecret==,ingress.hosts[0]=ingress.host1,ingress.hosts[1]=ingress.host2",
|
||||
"--kube-context",
|
||||
"testCluster",
|
||||
"--testParam",
|
||||
"testValue",
|
||||
}, e.calls[2].params, "Wrong upgrade parameters")
|
||||
})
|
||||
|
||||
t.Run("test kubectl - create secret/kubeconfig", func(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
defer os.RemoveAll(dir) // clean up
|
||||
assert.NoError(t, err, "Error when creating temp dir")
|
||||
|
||||
opts := kubernetesDeployOptions{
|
||||
AppTemplate: filepath.Join(dir, "test.yaml"),
|
||||
ContainerRegistryURL: "https://my.registry:55555",
|
||||
ContainerRegistryUser: "registryUser",
|
||||
ContainerRegistryPassword: "********",
|
||||
ContainerRegistrySecret: "regSecret",
|
||||
CreateDockerRegistrySecret: true,
|
||||
DeployTool: "kubectl",
|
||||
Image: "path/to/Image:latest",
|
||||
AdditionalParameters: []string{"--testParam", "testValue"},
|
||||
KubeConfig: "This is my kubeconfig",
|
||||
KubeContext: "testCluster",
|
||||
Namespace: "deploymentNamespace",
|
||||
}
|
||||
|
||||
kubeYaml := `kind: Deployment
|
||||
metadata:
|
||||
spec:
|
||||
spec:
|
||||
image: <image-name>`
|
||||
|
||||
ioutil.WriteFile(opts.AppTemplate, []byte(kubeYaml), 0755)
|
||||
|
||||
e := execMockRunner{
|
||||
shouldFailOnCommand: map[string]error{
|
||||
"kubectl --insecure-skip-tls-verify=true --namespace=deploymentNamespace --context=testCluster get secret regSecret": fmt.Errorf("secret not found"),
|
||||
},
|
||||
}
|
||||
var stdout bytes.Buffer
|
||||
runKubernetesDeploy(opts, &e, &stdout)
|
||||
|
||||
assert.Equal(t, e.env[0], []string{"KUBECONFIG=This is my kubeconfig"})
|
||||
|
||||
assert.Equal(t, "kubectl", e.calls[0].exec, "Wrong secret lookup command")
|
||||
assert.Equal(t, []string{
|
||||
"--insecure-skip-tls-verify=true",
|
||||
fmt.Sprintf("--namespace=%v", opts.Namespace),
|
||||
fmt.Sprintf("--context=%v", opts.KubeContext),
|
||||
"get",
|
||||
"secret",
|
||||
opts.ContainerRegistrySecret,
|
||||
}, e.calls[0].params, "kubectl parameters incorrect")
|
||||
|
||||
assert.Equal(t, "kubectl", e.calls[1].exec, "Wrong secret create command")
|
||||
assert.Equal(t, []string{
|
||||
"--insecure-skip-tls-verify=true",
|
||||
fmt.Sprintf("--namespace=%v", opts.Namespace),
|
||||
fmt.Sprintf("--context=%v", opts.KubeContext),
|
||||
"create",
|
||||
"secret",
|
||||
"docker-registry",
|
||||
opts.ContainerRegistrySecret,
|
||||
"--docker-server=my.registry:55555",
|
||||
fmt.Sprintf("--docker-username=%v", opts.ContainerRegistryUser),
|
||||
fmt.Sprintf("--docker-password=%v", opts.ContainerRegistryPassword),
|
||||
}, e.calls[1].params, "kubectl parameters incorrect")
|
||||
|
||||
assert.Equal(t, "kubectl", e.calls[2].exec, "Wrong apply command")
|
||||
assert.Equal(t, []string{
|
||||
"--insecure-skip-tls-verify=true",
|
||||
fmt.Sprintf("--namespace=%v", opts.Namespace),
|
||||
fmt.Sprintf("--context=%v", opts.KubeContext),
|
||||
"apply",
|
||||
"--filename",
|
||||
opts.AppTemplate,
|
||||
"--testParam",
|
||||
"testValue",
|
||||
}, e.calls[2].params, "kubectl parameters incorrect")
|
||||
|
||||
appTemplate, err := ioutil.ReadFile(opts.AppTemplate)
|
||||
assert.Contains(t, string(appTemplate), "my.registry:55555/path/to/Image:latest")
|
||||
})
|
||||
|
||||
t.Run("test kubectl - lookup secret/kubeconfig", func(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
defer os.RemoveAll(dir) // clean up
|
||||
assert.NoError(t, err, "Error when creating temp dir")
|
||||
|
||||
opts := kubernetesDeployOptions{
|
||||
AppTemplate: filepath.Join(dir, "test.yaml"),
|
||||
ContainerRegistryURL: "https://my.registry:55555",
|
||||
ContainerRegistryUser: "registryUser",
|
||||
ContainerRegistryPassword: "********",
|
||||
ContainerRegistrySecret: "regSecret",
|
||||
CreateDockerRegistrySecret: true,
|
||||
DeployTool: "kubectl",
|
||||
Image: "path/to/Image:latest",
|
||||
KubeConfig: "This is my kubeconfig",
|
||||
Namespace: "deploymentNamespace",
|
||||
}
|
||||
|
||||
ioutil.WriteFile(opts.AppTemplate, []byte("testYaml"), 0755)
|
||||
|
||||
e := execMockRunner{}
|
||||
|
||||
var stdout bytes.Buffer
|
||||
runKubernetesDeploy(opts, &e, &stdout)
|
||||
|
||||
assert.Equal(t, "kubectl", e.calls[0].exec, "Wrong secret lookup command")
|
||||
assert.Equal(t, []string{
|
||||
"--insecure-skip-tls-verify=true",
|
||||
fmt.Sprintf("--namespace=%v", opts.Namespace),
|
||||
"get",
|
||||
"secret",
|
||||
opts.ContainerRegistrySecret,
|
||||
}, e.calls[0].params, "kubectl parameters incorrect")
|
||||
|
||||
assert.Equal(t, "kubectl", e.calls[1].exec, "Wrong apply command")
|
||||
assert.Equal(t, []string{
|
||||
"--insecure-skip-tls-verify=true",
|
||||
fmt.Sprintf("--namespace=%v", opts.Namespace),
|
||||
"apply",
|
||||
"--filename",
|
||||
opts.AppTemplate,
|
||||
}, e.calls[1].params, "kubectl parameters incorrect")
|
||||
})
|
||||
|
||||
t.Run("test kubectl - token only", func(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
defer os.RemoveAll(dir) // clean up
|
||||
assert.NoError(t, err, "Error when creating temp dir")
|
||||
|
||||
opts := kubernetesDeployOptions{
|
||||
APIServer: "https://my.api.server",
|
||||
AppTemplate: filepath.Join(dir, "test.yaml"),
|
||||
ContainerRegistryURL: "https://my.registry:55555",
|
||||
ContainerRegistryUser: "registryUser",
|
||||
ContainerRegistryPassword: "********",
|
||||
ContainerRegistrySecret: "regSecret",
|
||||
DeployTool: "kubectl",
|
||||
Image: "path/to/Image:latest",
|
||||
KubeToken: "testToken",
|
||||
Namespace: "deploymentNamespace",
|
||||
}
|
||||
|
||||
ioutil.WriteFile(opts.AppTemplate, []byte("testYaml"), 0755)
|
||||
|
||||
e := execMockRunner{
|
||||
shouldFailOnCommand: map[string]error{},
|
||||
}
|
||||
var stdout bytes.Buffer
|
||||
runKubernetesDeploy(opts, &e, &stdout)
|
||||
|
||||
assert.Equal(t, "kubectl", e.calls[0].exec, "Wrong apply command")
|
||||
assert.Equal(t, []string{
|
||||
"--insecure-skip-tls-verify=true",
|
||||
fmt.Sprintf("--namespace=%v", opts.Namespace),
|
||||
fmt.Sprintf("--server=%v", opts.APIServer),
|
||||
fmt.Sprintf("--token=%v", opts.KubeToken),
|
||||
"apply",
|
||||
"--filename",
|
||||
opts.AppTemplate,
|
||||
}, e.calls[0].params, "kubectl parameters incorrect")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSplitRegistryURL(t *testing.T) {
|
||||
tt := []struct {
|
||||
in string
|
||||
outProtocol string
|
||||
outRegistry string
|
||||
outError error
|
||||
}{
|
||||
{in: "https://my.registry.com", outProtocol: "https", outRegistry: "my.registry.com", outError: nil},
|
||||
{in: "https://", outProtocol: "", outRegistry: "", outError: fmt.Errorf("Failed to split registry url 'https://'")},
|
||||
{in: "my.registry.com", outProtocol: "", outRegistry: "", outError: fmt.Errorf("Failed to split registry url 'my.registry.com'")},
|
||||
{in: "", outProtocol: "", outRegistry: "", outError: fmt.Errorf("Failed to split registry url ''")},
|
||||
{in: "https://https://my.registry.com", outProtocol: "", outRegistry: "", outError: fmt.Errorf("Failed to split registry url 'https://https://my.registry.com'")},
|
||||
}
|
||||
|
||||
for _, test := range tt {
|
||||
p, r, err := splitRegistryURL(test.in)
|
||||
assert.Equal(t, test.outProtocol, p, "Protocol value unexpected")
|
||||
assert.Equal(t, test.outRegistry, r, "Registry value unexpected")
|
||||
assert.Equal(t, test.outError, err, "Error value not as expected")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSplitImageName(t *testing.T) {
|
||||
tt := []struct {
|
||||
in string
|
||||
outImage string
|
||||
outTag string
|
||||
outError error
|
||||
}{
|
||||
{in: "", outImage: "", outTag: "", outError: fmt.Errorf("Failed to split image name ''")},
|
||||
{in: "path/to/image", outImage: "path/to/image", outTag: "", outError: nil},
|
||||
{in: "path/to/image:tag", outImage: "path/to/image", outTag: "tag", outError: nil},
|
||||
{in: "https://my.registry.com/path/to/image:tag", outImage: "", outTag: "", outError: fmt.Errorf("Failed to split image name 'https://my.registry.com/path/to/image:tag'")},
|
||||
}
|
||||
for _, test := range tt {
|
||||
i, tag, err := splitFullImageName(test.in)
|
||||
assert.Equal(t, test.outImage, i, "Image value unexpected")
|
||||
assert.Equal(t, test.outTag, tag, "Tag value unexpected")
|
||||
assert.Equal(t, test.outError, err, "Error value not as expected")
|
||||
}
|
||||
}
|
@ -46,6 +46,7 @@ func Execute() {
|
||||
rootCmd.AddCommand(VersionCommand())
|
||||
rootCmd.AddCommand(DetectExecuteScanCommand())
|
||||
rootCmd.AddCommand(KarmaExecuteTestsCommand())
|
||||
rootCmd.AddCommand(KubernetesDeployCommand())
|
||||
rootCmd.AddCommand(XsDeployCommand())
|
||||
rootCmd.AddCommand(GithubPublishReleaseCommand())
|
||||
rootCmd.AddCommand(GithubCreatePullRequestCommand())
|
||||
|
@ -13,11 +13,14 @@ import (
|
||||
)
|
||||
|
||||
type execMockRunner struct {
|
||||
dir []string
|
||||
calls []execCall
|
||||
stdout io.Writer
|
||||
stderr io.Writer
|
||||
shouldFailWith error
|
||||
dir []string
|
||||
env [][]string
|
||||
calls []execCall
|
||||
stdout io.Writer
|
||||
stderr io.Writer
|
||||
stdoutReturn map[string]string
|
||||
shouldFailWith error
|
||||
shouldFailOnCommand map[string]error
|
||||
}
|
||||
|
||||
type execCall struct {
|
||||
@ -27,6 +30,7 @@ type execCall struct {
|
||||
|
||||
type shellMockRunner struct {
|
||||
dir string
|
||||
env [][]string
|
||||
calls []string
|
||||
shell []string
|
||||
stdout io.Writer
|
||||
@ -38,12 +42,26 @@ func (m *execMockRunner) Dir(d string) {
|
||||
m.dir = append(m.dir, d)
|
||||
}
|
||||
|
||||
func (m *execMockRunner) Env(e []string) {
|
||||
m.env = append(m.env, e)
|
||||
}
|
||||
|
||||
func (m *execMockRunner) RunExecutable(e string, p ...string) error {
|
||||
if m.shouldFailWith != nil {
|
||||
return m.shouldFailWith
|
||||
}
|
||||
|
||||
exec := execCall{exec: e, params: p}
|
||||
m.calls = append(m.calls, exec)
|
||||
|
||||
if c := strings.Join(append([]string{e}, p...), " "); m.shouldFailOnCommand != nil && m.shouldFailOnCommand[c] != nil {
|
||||
return m.shouldFailOnCommand[c]
|
||||
}
|
||||
|
||||
if c := strings.Join(append([]string{e}, p...), " "); m.stdoutReturn != nil && len(m.stdoutReturn[c]) > 0 {
|
||||
m.stdout.Write([]byte(m.stdoutReturn[c]))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -59,6 +77,10 @@ func (m *shellMockRunner) Dir(d string) {
|
||||
m.dir = d
|
||||
}
|
||||
|
||||
func (m *shellMockRunner) Env(e []string) {
|
||||
m.env = append(m.env, e)
|
||||
}
|
||||
|
||||
func (m *shellMockRunner) RunShell(s string, c string) error {
|
||||
|
||||
if m.shouldFailWith != nil {
|
||||
|
@ -16,6 +16,7 @@ type Command struct {
|
||||
dir string
|
||||
stdout io.Writer
|
||||
stderr io.Writer
|
||||
env []string
|
||||
}
|
||||
|
||||
// Dir sets the working directory for the execution
|
||||
@ -23,6 +24,11 @@ func (c *Command) Dir(d string) {
|
||||
c.dir = d
|
||||
}
|
||||
|
||||
// Env sets explicit environment variables to be used for execution
|
||||
func (c *Command) Env(e []string) {
|
||||
c.env = e
|
||||
}
|
||||
|
||||
// Stdout ..
|
||||
func (c *Command) Stdout(stdout io.Writer) {
|
||||
c.stdout = stdout
|
||||
@ -43,7 +49,14 @@ func (c *Command) RunShell(shell, script string) error {
|
||||
|
||||
cmd := ExecCommand(shell)
|
||||
|
||||
cmd.Dir = c.dir
|
||||
if len(c.dir) > 0 {
|
||||
cmd.Dir = c.dir
|
||||
}
|
||||
|
||||
if len(c.env) > 0 {
|
||||
cmd.Env = c.env
|
||||
}
|
||||
|
||||
in := bytes.Buffer{}
|
||||
in.Write([]byte(script))
|
||||
cmd.Stdin = &in
|
||||
@ -65,6 +78,10 @@ func (c *Command) RunExecutable(executable string, params ...string) error {
|
||||
cmd.Dir = c.dir
|
||||
}
|
||||
|
||||
if len(c.env) > 0 {
|
||||
cmd.Env = c.env
|
||||
}
|
||||
|
||||
if err := runCmd(cmd, _out, _err); err != nil {
|
||||
return errors.Wrapf(err, "running command '%v' failed", executable)
|
||||
}
|
||||
|
@ -208,6 +208,7 @@ func (m *StepData) GetContextParameterFilters() StepFilters {
|
||||
for _, condition := range container.Conditions {
|
||||
for _, dependentParam := range condition.Params {
|
||||
parameterKeys = append(parameterKeys, dependentParam.Value)
|
||||
parameterKeys = append(parameterKeys, dependentParam.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -216,6 +217,7 @@ func (m *StepData) GetContextParameterFilters() StepFilters {
|
||||
if len(m.Spec.Sidecars) > 0 {
|
||||
//ToDo: support fallback for "dockerName" configuration property -> via aliasing?
|
||||
containerFilters = append(containerFilters, []string{"containerName", "containerPortMappings", "dockerName", "sidecarEnvVars", "sidecarImage", "sidecarName", "sidecarOptions", "sidecarPullImage", "sidecarReadyCommand", "sidecarVolumeBind", "sidecarWorkspace"}...)
|
||||
//ToDo: add condition param.Value and param.Name to filter as for Containers
|
||||
}
|
||||
if len(containerFilters) > 0 {
|
||||
filters.All = append(filters.All, containerFilters...)
|
||||
|
@ -266,12 +266,12 @@ func TestGetContextParameterFilters(t *testing.T) {
|
||||
|
||||
t.Run("Containers", func(t *testing.T) {
|
||||
filters := metadata2.GetContextParameterFilters()
|
||||
assert.Equal(t, []string{"containerCommand", "containerShell", "dockerEnvVars", "dockerImage", "dockerOptions", "dockerPullImage", "dockerVolumeBind", "dockerWorkspace", "pip"}, filters.All, "incorrect filter All")
|
||||
assert.NotEqual(t, []string{"containerCommand", "containerShell", "dockerEnvVars", "dockerImage", "dockerOptions", "dockerPullImage", "dockerVolumeBind", "dockerWorkspace", "pip"}, filters.General, "incorrect filter General")
|
||||
assert.Equal(t, []string{"containerCommand", "containerShell", "dockerEnvVars", "dockerImage", "dockerOptions", "dockerPullImage", "dockerVolumeBind", "dockerWorkspace", "pip"}, filters.Steps, "incorrect filter Steps")
|
||||
assert.Equal(t, []string{"containerCommand", "containerShell", "dockerEnvVars", "dockerImage", "dockerOptions", "dockerPullImage", "dockerVolumeBind", "dockerWorkspace", "pip"}, filters.Stages, "incorrect filter Stages")
|
||||
assert.Equal(t, []string{"containerCommand", "containerShell", "dockerEnvVars", "dockerImage", "dockerOptions", "dockerPullImage", "dockerVolumeBind", "dockerWorkspace", "pip"}, filters.Parameters, "incorrect filter Parameters")
|
||||
assert.NotEqual(t, []string{"containerCommand", "containerShell", "dockerEnvVars", "dockerImage", "dockerOptions", "dockerPullImage", "dockerVolumeBind", "dockerWorkspace", "pip"}, filters.Env, "incorrect filter Env")
|
||||
assert.Equal(t, []string{"containerCommand", "containerShell", "dockerEnvVars", "dockerImage", "dockerOptions", "dockerPullImage", "dockerVolumeBind", "dockerWorkspace", "pip", "scanType"}, filters.All, "incorrect filter All")
|
||||
assert.NotEqual(t, []string{"containerCommand", "containerShell", "dockerEnvVars", "dockerImage", "dockerOptions", "dockerPullImage", "dockerVolumeBind", "dockerWorkspace", "pip", "scanType"}, filters.General, "incorrect filter General")
|
||||
assert.Equal(t, []string{"containerCommand", "containerShell", "dockerEnvVars", "dockerImage", "dockerOptions", "dockerPullImage", "dockerVolumeBind", "dockerWorkspace", "pip", "scanType"}, filters.Steps, "incorrect filter Steps")
|
||||
assert.Equal(t, []string{"containerCommand", "containerShell", "dockerEnvVars", "dockerImage", "dockerOptions", "dockerPullImage", "dockerVolumeBind", "dockerWorkspace", "pip", "scanType"}, filters.Stages, "incorrect filter Stages")
|
||||
assert.Equal(t, []string{"containerCommand", "containerShell", "dockerEnvVars", "dockerImage", "dockerOptions", "dockerPullImage", "dockerVolumeBind", "dockerWorkspace", "pip", "scanType"}, filters.Parameters, "incorrect filter Parameters")
|
||||
assert.NotEqual(t, []string{"containerCommand", "containerShell", "dockerEnvVars", "dockerImage", "dockerOptions", "dockerPullImage", "dockerVolumeBind", "dockerWorkspace", "pip", "scanType"}, filters.Env, "incorrect filter Env")
|
||||
})
|
||||
|
||||
t.Run("Sidecars", func(t *testing.T) {
|
||||
|
@ -68,12 +68,16 @@ func setDefaultStepParameters(stepData *config.StepData) {
|
||||
switch param.Type {
|
||||
case "bool":
|
||||
param.Default = "false"
|
||||
case "int":
|
||||
param.Default = "0"
|
||||
}
|
||||
} else {
|
||||
switch param.Type {
|
||||
case "string":
|
||||
case "bool":
|
||||
param.Default = fmt.Sprintf("\"%v\"", param.Default)
|
||||
case "int":
|
||||
param.Default = fmt.Sprintf("%v", param.Default)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,12 +214,14 @@ func setDefaultParameters(stepData *config.StepData) (bool, error) {
|
||||
|
||||
if param.Default == nil {
|
||||
switch param.Type {
|
||||
case "string":
|
||||
param.Default = fmt.Sprintf("os.Getenv(\"PIPER_%v\")", param.Name)
|
||||
osImportRequired = true
|
||||
case "bool":
|
||||
// ToDo: Check if default should be read from env
|
||||
param.Default = "false"
|
||||
case "int":
|
||||
param.Default = "0"
|
||||
case "string":
|
||||
param.Default = fmt.Sprintf("os.Getenv(\"PIPER_%v\")", param.Name)
|
||||
osImportRequired = true
|
||||
case "[]string":
|
||||
// ToDo: Check if default should be read from env
|
||||
param.Default = "[]string{}"
|
||||
@ -228,14 +230,16 @@ func setDefaultParameters(stepData *config.StepData) (bool, error) {
|
||||
}
|
||||
} else {
|
||||
switch param.Type {
|
||||
case "string":
|
||||
param.Default = fmt.Sprintf("\"%v\"", param.Default)
|
||||
case "bool":
|
||||
boolVal := "false"
|
||||
if param.Default.(bool) == true {
|
||||
boolVal = "true"
|
||||
}
|
||||
param.Default = boolVal
|
||||
case "int":
|
||||
param.Default = fmt.Sprintf("%v", param.Default)
|
||||
case "string":
|
||||
param.Default = fmt.Sprintf("\"%v\"", param.Default)
|
||||
case "[]string":
|
||||
param.Default = fmt.Sprintf("[]string{\"%v\"}", strings.Join(getStringSliceFromInterface(param.Default), "\", \""))
|
||||
default:
|
||||
@ -430,6 +434,8 @@ func flagType(paramType string) string {
|
||||
switch paramType {
|
||||
case "bool":
|
||||
theFlagType = "BoolVar"
|
||||
case "int":
|
||||
theFlagType = "IntVar"
|
||||
case "string":
|
||||
theFlagType = "StringVar"
|
||||
case "[]string":
|
||||
|
@ -115,12 +115,14 @@ func TestSetDefaultParameters(t *testing.T) {
|
||||
Spec: config.StepSpec{
|
||||
Inputs: config.StepInputs{
|
||||
Parameters: []config.StepParameters{
|
||||
{Name: "param0", Scope: []string{"GENERAL"}, Type: "string", Default: "val0"},
|
||||
{Name: "param1", Scope: []string{"STEPS"}, Type: "string"},
|
||||
{Name: "param2", Scope: []string{"STAGES"}, Type: "bool", Default: true},
|
||||
{Name: "param3", Scope: []string{"PARAMETERS"}, Type: "bool"},
|
||||
{Name: "param4", Scope: []string{"ENV"}, Type: "[]string", Default: stringSliceDefault},
|
||||
{Name: "param5", Scope: []string{"ENV"}, Type: "[]string"},
|
||||
{Name: "param0", Type: "string", Default: "val0"},
|
||||
{Name: "param1", Type: "string"},
|
||||
{Name: "param2", Type: "bool", Default: true},
|
||||
{Name: "param3", Type: "bool"},
|
||||
{Name: "param4", Type: "[]string", Default: stringSliceDefault},
|
||||
{Name: "param5", Type: "[]string"},
|
||||
{Name: "param6", Type: "int"},
|
||||
{Name: "param7", Type: "int", Default: 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -133,6 +135,8 @@ func TestSetDefaultParameters(t *testing.T) {
|
||||
"false",
|
||||
"[]string{\"val4_1\", \"val4_2\"}",
|
||||
"[]string{}",
|
||||
"0",
|
||||
"1",
|
||||
}
|
||||
|
||||
osImport, err := setDefaultParameters(&stepData)
|
||||
@ -152,8 +156,8 @@ func TestSetDefaultParameters(t *testing.T) {
|
||||
Spec: config.StepSpec{
|
||||
Inputs: config.StepInputs{
|
||||
Parameters: []config.StepParameters{
|
||||
{Name: "param0", Scope: []string{"GENERAL"}, Type: "int", Default: 10},
|
||||
{Name: "param1", Scope: []string{"GENERAL"}, Type: "int"},
|
||||
{Name: "param0", Type: "n/a", Default: 10},
|
||||
{Name: "param1", Type: "n/a"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -162,7 +166,7 @@ func TestSetDefaultParameters(t *testing.T) {
|
||||
Spec: config.StepSpec{
|
||||
Inputs: config.StepInputs{
|
||||
Parameters: []config.StepParameters{
|
||||
{Name: "param1", Scope: []string{"GENERAL"}, Type: "int"},
|
||||
{Name: "param1", Type: "n/a"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -246,6 +250,7 @@ func TestFlagType(t *testing.T) {
|
||||
expected string
|
||||
}{
|
||||
{input: "bool", expected: "BoolVar"},
|
||||
{input: "int", expected: "IntVar"},
|
||||
{input: "string", expected: "StringVar"},
|
||||
{input: "[]string", expected: "StringSliceVar"},
|
||||
}
|
||||
|
220
resources/metadata/kubernetesdeploy.yaml
Normal file
220
resources/metadata/kubernetesdeploy.yaml
Normal file
@ -0,0 +1,220 @@
|
||||
metadata:
|
||||
name: kubernetesDeploy
|
||||
description: Deployment to Kubernetes test or production namespace within the specified Kubernetes cluster.
|
||||
longDescription: |-
|
||||
Deployment to Kubernetes test or production namespace within the specified Kubernetes cluster.
|
||||
|
||||
!!! note "Deployment supports multiple deployment tools"
|
||||
Currently the following are supported:
|
||||
|
||||
* [Helm](https://helm.sh/) command line tool and [Helm Charts](https://docs.helm.sh/developing_charts/#charts).
|
||||
* [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/) and `kubectl apply` command.
|
||||
|
||||
## Helm
|
||||
Following helm command will be executed by default:
|
||||
|
||||
```
|
||||
helm upgrade <deploymentName> <chartPath> --install --force --namespace <namespace> --wait --timeout <helmDeployWaitSeconds> --set "image.repository=<yourRegistry>/<yourImageName>,image.tag=<yourImageTag>,secret.dockerconfigjson=<dockerSecret>,ingress.hosts[0]=<ingressHosts[0]>,,ingress.hosts[1]=<ingressHosts[1]>,...
|
||||
```
|
||||
|
||||
* `yourRegistry` will be retrieved from `containerRegistryUrl`
|
||||
* `yourImageName`, `yourImageTag` will be retrieved from `image`
|
||||
* `dockerSecret` will be calculated with a call to `kubectl create secret docker-registry regsecret --docker-server=<yourRegistry> --docker-username=<containerRegistryUser> --docker-password=<containerRegistryPassword> --dry-run=true --output=json'`
|
||||
spec:
|
||||
inputs:
|
||||
secrets:
|
||||
- name: kubeConfigFileCredentialsId
|
||||
type: jenkins
|
||||
- name: kubeTokenCredentialsId
|
||||
type: jenkins
|
||||
- name: dockerCredentialsId
|
||||
type: jenkins
|
||||
resources:
|
||||
- name: deployDescriptor
|
||||
type: stash
|
||||
params:
|
||||
- name: additionalParameters
|
||||
aliases:
|
||||
- name: helmDeploymentParameters
|
||||
type: '[]string'
|
||||
description: Defines additional parameters for \"helm install\" or \"kubectl apply\" command.
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: apiServer
|
||||
aliases:
|
||||
- name: k8sAPIServer
|
||||
type: string
|
||||
description: Defines the Url of the API Server of the Kubernetes cluster.
|
||||
scope:
|
||||
- GENERAL
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: appTemplate
|
||||
aliases:
|
||||
- name: k8sAppTemplate
|
||||
type: string
|
||||
description: Defines the filename for the kubernetes app template (e.g. k8s_apptemplate.yaml)
|
||||
mandatory: false
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: chartPath
|
||||
aliases:
|
||||
- name: helmChartPath
|
||||
type: string
|
||||
description: Defines the chart path for deployments using helm.
|
||||
mandatory: true
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: containerRegistryPassword
|
||||
description: Password for container registry access - typically provided by the CI/CD environment.
|
||||
type: string
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: containerRegistryUrl
|
||||
aliases:
|
||||
- name: dockerRegistryUrl
|
||||
type: string
|
||||
description: http(s) url of the Container registry.
|
||||
scope:
|
||||
- GENERAL
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
mandatory: true
|
||||
- name: containerRegistryUser
|
||||
description: Username for container registry access - typically provided by the CI/CD environment.
|
||||
type: string
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: containerRegistrySecret
|
||||
description: Name of the container registry secret used for pulling containers from the registry.
|
||||
type: string
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
default: regsecret
|
||||
- name: createDockerRegistrySecret
|
||||
type: bool
|
||||
description: Toggle to turn on Regsecret creation with a \"deployTool:kubectl\" deployment.
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
default: false
|
||||
- name: deploymentName
|
||||
aliases:
|
||||
- name: helmDeploymentName
|
||||
type: string
|
||||
description: Defines the name of the deployment.
|
||||
mandatory: true
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: deployTool
|
||||
type: string
|
||||
description: Defines the tool which should be used for deployment.
|
||||
mandatory: true
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
default: kubectl
|
||||
- name: helmDeployWaitSeconds
|
||||
type: int
|
||||
description: Number of seconds before helm deploy returns.
|
||||
mandatory: false
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
default: 300
|
||||
- name: image
|
||||
aliases:
|
||||
- name: deployImage
|
||||
type: string
|
||||
description: Full name of the image to be deployed.
|
||||
mandatory: true
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: ingressHosts
|
||||
type: '[]string'
|
||||
description: List of ingress hosts to be exposed via helm deployment.
|
||||
mandatory: false
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: kubeConfig
|
||||
type: string
|
||||
description: Defines the path to the \"kubeconfig\" file.
|
||||
scope:
|
||||
- GENERAL
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: kubeContext
|
||||
type: string
|
||||
description: Defines the context to use from the \"kubeconfig\" file.
|
||||
mandatory: false
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: kubeToken
|
||||
type: string
|
||||
description: Contains the id_token used by kubectl for authentication. Consider using kubeConfig parameter instead.
|
||||
scope:
|
||||
- GENERAL
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
- name: namespace
|
||||
aliases:
|
||||
- name: helmDeploymentNamespace
|
||||
- name: k8sDeploymentNamespace
|
||||
type: string
|
||||
description: Defines the target Kubernetes namespace for the deployment.
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
default: default
|
||||
- name: tillerNamespace
|
||||
aliases:
|
||||
- name: helmTillerNamespace
|
||||
type: string
|
||||
description: Defines optional tiller namespace for deployments using helm.
|
||||
scope:
|
||||
- PARAMETERS
|
||||
- STAGES
|
||||
- STEPS
|
||||
containers:
|
||||
- image: dtzar/helm-kubectl:2.12.1
|
||||
workingDir: /config
|
||||
conditions:
|
||||
- conditionRef: strings-equal
|
||||
params:
|
||||
- name: deployTool
|
||||
value: helm
|
||||
- image: dtzar/helm-kubectl:2.12.1
|
||||
workingDir: /config
|
||||
conditions:
|
||||
- conditionRef: strings-equal
|
||||
params:
|
||||
- name: deployTool
|
||||
value: kubectl
|
@ -116,6 +116,7 @@ public class CommonStepsTest extends BasePiperTest{
|
||||
'piperStageWrapper', //intended to be called from within stages
|
||||
'buildSetResult',
|
||||
'githubPublishRelease', //implementing new golang pattern without fields
|
||||
'kubernetesDeploy', //implementing new golang pattern without fields
|
||||
'xsDeploy', //implementing new golang pattern without fields
|
||||
]
|
||||
|
||||
|
96
test/groovy/KubernetesDeployTest.groovy
Normal file
96
test/groovy/KubernetesDeployTest.groovy
Normal file
@ -0,0 +1,96 @@
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.RuleChain
|
||||
import util.*
|
||||
|
||||
import static org.hamcrest.Matchers.*
|
||||
import static org.junit.Assert.assertThat
|
||||
|
||||
class KubernetesDeployTest extends BasePiperTest {
|
||||
|
||||
private JenkinsReadJsonRule readJsonRule = new JenkinsReadJsonRule(this)
|
||||
private JenkinsShellCallRule shellCallRule = new JenkinsShellCallRule(this)
|
||||
private JenkinsStepRule stepRule = new JenkinsStepRule(this)
|
||||
private JenkinsWriteFileRule writeFileRule = new JenkinsWriteFileRule(this)
|
||||
private JenkinsDockerExecuteRule dockerExecuteRule = new JenkinsDockerExecuteRule(this)
|
||||
|
||||
private List withEnvArgs = []
|
||||
private List credentials = []
|
||||
|
||||
@Rule
|
||||
public RuleChain rules = Rules
|
||||
.getCommonRules(this)
|
||||
.around(new JenkinsReadYamlRule(this))
|
||||
.around(readJsonRule)
|
||||
.around(shellCallRule)
|
||||
.around(stepRule)
|
||||
.around(writeFileRule)
|
||||
.around(dockerExecuteRule)
|
||||
|
||||
@Before
|
||||
void init() {
|
||||
credentials = []
|
||||
helper.registerAllowedMethod("withEnv", [List.class, Closure.class], {arguments, closure ->
|
||||
arguments.each {arg ->
|
||||
withEnvArgs.add(arg.toString())
|
||||
}
|
||||
return closure()
|
||||
})
|
||||
|
||||
helper.registerAllowedMethod('file', [Map], { m -> return m })
|
||||
helper.registerAllowedMethod('string', [Map], { m -> return m })
|
||||
helper.registerAllowedMethod('usernamePassword', [Map], { m -> return m })
|
||||
helper.registerAllowedMethod('withCredentials', [List, Closure], { l, c ->
|
||||
l.each {m ->
|
||||
credentials.add(m)
|
||||
if (m.credentialsId == 'kubeConfig') {
|
||||
binding.setProperty('PIPER_kubeConfig', 'myKubeConfig')
|
||||
} else if (m.credentialsId == 'kubeToken') {
|
||||
binding.setProperty('PIPER_kubeToken','myKubeToken')
|
||||
} else if (m.credentialsId == 'dockerCredentials') {
|
||||
binding.setProperty('PIPER_containerRegistryUser', 'registryUser')
|
||||
binding.setProperty('PIPER_containerRegistryPassword', '********')
|
||||
}
|
||||
}
|
||||
try {
|
||||
c()
|
||||
} finally {
|
||||
binding.setProperty('PIPER_kubeConfig', null)
|
||||
binding.setProperty('PIPER_kubeToken', null)
|
||||
binding.setProperty('PIPER_containerRegistryUser', null)
|
||||
binding.setProperty('PIPER_containerRegistryPassword', null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Test
|
||||
void testKubernetesDeployAllCreds() {
|
||||
shellCallRule.setReturnValue('./piper getConfig --contextConfig --stepMetadata \'metadata/kubernetesdeploy.yaml\'', '{"kubeConfigFileCredentialsId":"kubeConfig", "kubeTokenCredentialsId":"kubeToken", "dockerCredentialsId":"dockerCredentials", "dockerImage":"my.Registry/K8S:latest"}')
|
||||
|
||||
stepRule.step.kubernetesDeploy(
|
||||
juStabUtils: utils,
|
||||
testParam: "This is test content",
|
||||
script: nullScript
|
||||
)
|
||||
// asserts
|
||||
assertThat(writeFileRule.files['metadata/kubernetesdeploy.yaml'], containsString('name: kubernetesDeploy'))
|
||||
assertThat(withEnvArgs[0], allOf(startsWith('PIPER_parametersJSON'), containsString('"testParam":"This is test content"')))
|
||||
assertThat(shellCallRule.shell[1], is('./piper kubernetesDeploy'))
|
||||
assertThat(credentials.size(), is(3))
|
||||
|
||||
assertThat(dockerExecuteRule.dockerParams.dockerImage, is('my.Registry/K8S:latest'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testKubernetesDeploySomeCreds() {
|
||||
shellCallRule.setReturnValue('./piper getConfig --contextConfig --stepMetadata \'metadata/kubernetesdeploy.yaml\'', '{"kubeTokenCredentialsId":"kubeToken", "dockerCredentialsId":"dockerCredentials"}')
|
||||
stepRule.step.kubernetesDeploy(
|
||||
juStabUtils: utils,
|
||||
script: nullScript
|
||||
)
|
||||
// asserts
|
||||
assertThat(shellCallRule.shell[1], is('./piper kubernetesDeploy'))
|
||||
assertThat(credentials.size(), is(2))
|
||||
}
|
||||
}
|
48
vars/kubernetesDeploy.groovy
Normal file
48
vars/kubernetesDeploy.groovy
Normal file
@ -0,0 +1,48 @@
|
||||
import com.sap.piper.PiperGoUtils
|
||||
import com.sap.piper.Utils
|
||||
import groovy.transform.Field
|
||||
|
||||
import static com.sap.piper.Prerequisites.checkScript
|
||||
|
||||
@Field String STEP_NAME = getClass().getName()
|
||||
@Field String METADATA_FILE = 'metadata/kubernetesdeploy.yaml'
|
||||
|
||||
void call(Map parameters = [:]) {
|
||||
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
|
||||
|
||||
def script = checkScript(this, parameters) ?: this
|
||||
|
||||
def utils = parameters.juStabUtils ?: new Utils()
|
||||
parameters.juStabUtils = null
|
||||
|
||||
new PiperGoUtils(this, utils).unstashPiperBin()
|
||||
utils.unstash('pipelineConfigAndTests')
|
||||
script.commonPipelineEnvironment.writeToDisk(script)
|
||||
|
||||
writeFile(file: METADATA_FILE, text: libraryResource(METADATA_FILE))
|
||||
|
||||
withEnv([
|
||||
"PIPER_parametersJSON=${groovy.json.JsonOutput.toJson(parameters)}",
|
||||
]) {
|
||||
// get context configuration
|
||||
Map config = readJSON (text: sh(returnStdout: true, script: "./piper getConfig --contextConfig --stepMetadata '${METADATA_FILE}'"))
|
||||
echo "Config: ${config}"
|
||||
|
||||
dockerExecute(
|
||||
script: script,
|
||||
dockerImage: config.dockerImage,
|
||||
dockerWorkspace: config.dockerWorkspace,
|
||||
) {
|
||||
def creds = []
|
||||
if (config.kubeConfigFileCredentialsId) creds.add(file(credentialsId: config.kubeConfigFileCredentialsId, variable: 'PIPER_kubeConfig'))
|
||||
if (config.kubeTokenCredentialsId) creds.add(string(credentialsId: config.kubeTokenCredentialsId, variable: 'PIPER_kubeToken'))
|
||||
if (config.dockerCredentialsId) creds.add(usernamePassword(credentialsId: config.dockerCredentialsId, passwordVariable: 'PIPER_containerRegistryPassword', usernameVariable: 'PIPER_containerRegistryUser'))
|
||||
|
||||
// execute step
|
||||
withCredentials(creds) {
|
||||
sh "./piper kubernetesDeploy"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user