Skip to content

Commit

Permalink
feat: add workspaces to Tekton pipeline for git repo creds and regist…
Browse files Browse the repository at this point in the history
…ry creds (#1045)

Also put relative paths in the Tekton pipeline when there is no git repo.
This makes the pipeline more usable.

Signed-off-by: Harikrishnan Balagopal <harikrishmenon@gmail.com>
  • Loading branch information
HarikrishnanBalagopal authored Jun 12, 2023
1 parent f3a166d commit 883539c
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 33 deletions.
10 changes: 10 additions & 0 deletions common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,16 @@ const (
ConfigTargetClusterTypeKey = ConfigTargetKey + d + "clustertype"
//ConfigImageRegistryKey represents image registry Key
ConfigImageRegistryKey = ConfigTargetKey + d + "imageregistry"
// ConfigCICDKey is for CICD related questions
ConfigCICDKey = ConfigTargetKey + d + "cicd"
// ConfigCICDTektonKey is for CICD Tekton pipelines
ConfigCICDTektonKey = ConfigCICDKey + d + "tekton"
// ConfigCICDTektonGitRepoSSHSecretNameKey is for Tekton git-clone ssh
ConfigCICDTektonGitRepoSSHSecretNameKey = ConfigCICDTektonKey + d + "gitreposshsecret"
// ConfigCICDTektonGitRepoBasicAuthSecretNameKey is for Tekton git-clone basic auth
ConfigCICDTektonGitRepoBasicAuthSecretNameKey = ConfigCICDTektonKey + d + "gitrepobasicauthsecret"
// ConfigCICDTektonRegistryPushSecretNameKey is for Tekton push image to registry credentials
ConfigCICDTektonRegistryPushSecretNameKey = ConfigCICDTektonKey + d + "registrypushsecret"
//ConfigTargetExistingVersionUpdate represents key which how to update versions
ConfigTargetExistingVersionUpdate = ConfigTargetKey + d + "existingversionupdate"
//ConfigImageRegistryURLKey represents image registry url Key
Expand Down
2 changes: 1 addition & 1 deletion common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -1266,7 +1266,7 @@ func GatherGitInfo(path string) (repoName, repoDir, repoHostName, repoURL, repoB
if err != nil {
return "", "", "", "", "", fmt.Errorf("failed to parse the remote host url '%s' . Error: %w", u, err)
}
logrus.Errorf("parsed normal case - giturl: %#v", giturl)
logrus.Debugf("parsed normal case - giturl: %#v", giturl)
repoHostName = giturl.Host
repoName = filepath.Base(giturl.Path)
repoName = strings.TrimSuffix(repoName, filepath.Ext(repoName))
Expand Down
17 changes: 16 additions & 1 deletion transformer/dockerfile/dockerfileparsertransformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,24 @@ func (t *DockerfileParser) getIRFromDockerfile(dockerfilepath, contextPath, imag
}
container.Build.ContainerBuildType = irtypes.DockerfileContainerBuildType
container.Build.ContextPath = contextPath
container.Build.Artifacts = map[irtypes.ContainerBuildArtifactTypeValue][]string{

t111 := map[irtypes.ContainerBuildArtifactTypeValue][]string{
irtypes.DockerfileContainerBuildArtifactTypeValue: {dockerfilepath},
}
currEnvOutputDir := t.Env.GetEnvironmentOutput()
logrus.Debugf("making Dockerfile paths relative to env output dir: '%s'", currEnvOutputDir)
if relDockerfilePath, err := filepath.Rel(currEnvOutputDir, dockerfilepath); err == nil {
t111[irtypes.RelDockerfileContainerBuildArtifactTypeValue] = []string{relDockerfilePath}
} else {
logrus.Errorf("failed to make the Dockerfile path '%s' relative to the env output dir '%s' . Error: %q", dockerfilepath, currEnvOutputDir, err)
}
if relDockerfileContextPath, err := filepath.Rel(currEnvOutputDir, contextPath); err == nil {
t111[irtypes.RelDockerfileContextContainerBuildArtifactTypeValue] = []string{relDockerfileContextPath}
} else {
logrus.Errorf("failed to make the Dockerfile context path '%s' relative to the env output dir '%s' . Error: %q", contextPath, currEnvOutputDir, err)
}
container.Build.Artifacts = t111

if len(container.ExposedPorts) == 0 {
logrus.Warnf("Unable to find ports in Dockerfile : %s. Using default port %d", dockerfilepath, common.DefaultServicePort)
container.AddExposedPort(common.DefaultServicePort)
Expand Down
105 changes: 91 additions & 14 deletions transformer/kubernetes/apiresource/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package apiresource
import (
"fmt"
"path/filepath"
"strings"

"github.com/konveyor/move2kube/common"
collecttypes "github.com/konveyor/move2kube/types/collection"
Expand All @@ -30,11 +31,15 @@ import (
)

const (
pipelineKind = "Pipeline"
defaultGitRepoBranch = "main"
gitRepoURLPlaceholder = "<TODO: insert git repo url>"
contextPathPlaceholder = "<TODO: insert path to the directory containing Dockerfile>"
dockerfilePathPlaceholder = "<TODO: insert path to the Dockerfile>"
pipelineKind = "Pipeline"
defaultGitRepoBranch = "main"
gitRepoURLPlaceholder = "<TODO: insert git repo url>"
contextPathPlaceholder = "<TODO: insert path to the directory containing Dockerfile>"
dockerfilePathPlaceholder = "<TODO: insert path to the Dockerfile>"
dirInsideGitRepoPlaceholder = "<TODO: fill this prefix starting from the root of the git repo>"
gitRepoSSHCredsWorkspace = "git-ssh-credentials"
gitRepoBasicAuthCredsWorkspace = "git-basic-auth-credentials"
registryCredsWorkspace = "registry-credentials"
)

// Pipeline handles all objects like a Tekton pipeline.
Expand Down Expand Up @@ -69,12 +74,21 @@ func (*Pipeline) createNewResource(irpipeline irtypes.Pipeline, ir irtypes.Enhan
{Name: "image-registry-url", Description: "registry-domain/namespace where the output image should be pushed.", Type: v1beta1.ParamTypeString},
}
pipeline.Spec.Workspaces = []v1beta1.PipelineWorkspaceDeclaration{
{Name: irpipeline.WorkspaceName, Description: "This workspace will receive the cloned git repo and be passed to the kaniko task for building the image."},
{
Name: irpipeline.WorkspaceName,
Description: "This workspace will receive the cloned git repo and be passed to the kaniko task for building the image.",
},
{
Name: registryCredsWorkspace,
Description: "This workspace provides the credentials (Docker config.json) for pushing images to the registry. See https://hub.tekton.dev/tekton/task/kaniko",
},
}
tasks := []v1beta1.PipelineTask{}
firstTask := true
prevTaskName := ""
containerIndex := 0
gitNeedsSSHCreds := false
gitNeedsBasicAuthCreds := false
for imageName, container := range ir.ContainerImages {
if container.Build.ContainerBuildType == "" {
continue
Expand Down Expand Up @@ -105,6 +119,19 @@ func (*Pipeline) createNewResource(irpipeline irtypes.Pipeline, ir irtypes.Enhan
{Name: "deleteExisting", Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "true"}},
},
}
if gitRepoURL == gitRepoURLPlaceholder || strings.HasPrefix(gitRepoURL, "git@") {
gitNeedsSSHCreds = true
cloneTask.Workspaces = append(
cloneTask.Workspaces,
v1beta1.WorkspacePipelineTaskBinding{Name: "ssh-directory", Workspace: gitRepoSSHCredsWorkspace},
)
} else if strings.HasPrefix(gitRepoURL, "https://") {
gitNeedsBasicAuthCreds = true
cloneTask.Workspaces = append(
cloneTask.Workspaces,
v1beta1.WorkspacePipelineTaskBinding{Name: "basic-auth", Workspace: gitRepoBasicAuthCredsWorkspace},
)
}
if !firstTask {
cloneTask.RunAfter = []string{prevTaskName}
}
Expand All @@ -113,21 +140,54 @@ func (*Pipeline) createNewResource(irpipeline irtypes.Pipeline, ir irtypes.Enhan
dockerfilePath := dockerfilePathPlaceholder
contextPath := contextPathPlaceholder
// If there is a git repo, set the correct context and dockerfile paths.
if repoDir != "" {
if repoDir == "" {
logrus.Debugf("no git repo found for directory '%s'", container.Build.ContextPath)
if len(container.Build.Artifacts) != 0 && len(container.Build.Artifacts[irtypes.DockerfileContainerBuildArtifactTypeValue]) != 0 {
relDFPath, err := filepath.Rel(repoDir, container.Build.Artifacts[irtypes.DockerfileContainerBuildArtifactTypeValue][0])
if err != nil {
logrus.Errorf(
"failed to make the Dockerfile path '%s' relative to the path '%s' . Error %q",
repoDir, container.Build.ContextPath, err,
t1DockerfilePath := container.Build.Artifacts[irtypes.DockerfileContainerBuildArtifactTypeValue][0]
if len(container.Build.Artifacts[irtypes.RelDockerfileContainerBuildArtifactTypeValue]) != 0 {
t1RelDockerfilePath := container.Build.Artifacts[irtypes.RelDockerfileContainerBuildArtifactTypeValue][0]
logrus.Debugf(
"found some Dockerfile paths: t1DockerfilePath: '%s' t1RelDockerfilePath: '%s'", t1DockerfilePath, t1RelDockerfilePath,
)
{
// remove "source/" from "source/<service name>/Dockerfile"
ps := strings.Split(t1RelDockerfilePath, "/")
if len(ps) >= 2 && ps[0] == common.DefaultSourceDir {
t1RelDockerfilePath = strings.Join(ps[1:], "/")
}
}
dockerfilePath = dirInsideGitRepoPlaceholder + "/" + t1RelDockerfilePath
}
if len(container.Build.Artifacts[irtypes.RelDockerfileContextContainerBuildArtifactTypeValue]) != 0 {
t1RelDockerfileContextPath := container.Build.Artifacts[irtypes.RelDockerfileContextContainerBuildArtifactTypeValue][0]
logrus.Debugf(
"found some Dockerfile context paths: container.Build.ContextPath: '%s' t1RelDockerfileContextPath: '%s'",
container.Build.ContextPath, t1RelDockerfileContextPath,
)
{
// remove "source/" from "source/<service name>"
ps := strings.Split(t1RelDockerfileContextPath, "/")
if len(ps) >= 2 && ps[0] == common.DefaultSourceDir {
t1RelDockerfileContextPath = strings.Join(ps[1:], "/")
}
}
contextPath = dirInsideGitRepoPlaceholder + "/" + t1RelDockerfileContextPath
}
}
} else {
if len(container.Build.Artifacts) != 0 && len(container.Build.Artifacts[irtypes.DockerfileContainerBuildArtifactTypeValue]) != 0 {
logrus.Debugf("found a repo directory: '%s'", repoDir)
t1DockerfilePath := container.Build.Artifacts[irtypes.DockerfileContainerBuildArtifactTypeValue][0]
relDFPath, err := filepath.Rel(repoDir, t1DockerfilePath)
if err != nil {
logrus.Errorf("failed to make the Dockerfile path '%s' relative to the repo directory '%s' . Error %q", t1DockerfilePath, repoDir, err)
} else {
dockerfilePath = relDFPath
}
}
relContextPath, err := filepath.Rel(repoDir, container.Build.ContextPath)
if err != nil {
logrus.Errorf("Failed to make the path %q relative to the path %q Error %q", repoDir, container.Build.ContextPath, err)
logrus.Errorf("failed to make the path '%s' relative to the repo directory '%s' Error %q", container.Build.ContextPath, repoDir, err)
} else {
if dockerfilePath == dockerfilePathPlaceholder {
dockerfilePath = filepath.Join(relContextPath, common.DefaultDockerfileName)
Expand All @@ -141,9 +201,10 @@ func (*Pipeline) createNewResource(irpipeline irtypes.Pipeline, ir irtypes.Enhan
buildPushTask := v1beta1.PipelineTask{
RunAfter: []string{cloneTaskName},
Name: buildPushTaskName,
TaskRef: &v1beta1.TaskRef{Name: "kaniko"},
TaskRef: &v1beta1.TaskRef{Name: "kaniko"}, // https://hub.tekton.dev/tekton/task/kaniko
Workspaces: []v1beta1.WorkspacePipelineTaskBinding{
{Name: "source", Workspace: irpipeline.WorkspaceName},
{Name: "dockerconfig", Workspace: registryCredsWorkspace},
},
Params: []v1beta1.Param{
{Name: "IMAGE", Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "$(params.image-registry-url)/" + imageName}},
Expand All @@ -164,6 +225,22 @@ func (*Pipeline) createNewResource(irpipeline irtypes.Pipeline, ir irtypes.Enhan
logrus.Errorf("Unknown containerization method: %v", container.Build.ContainerBuildType)
}
}
if gitNeedsSSHCreds {
pipeline.Spec.Workspaces = append(pipeline.Spec.Workspaces,
v1beta1.PipelineWorkspaceDeclaration{
Name: gitRepoSSHCredsWorkspace,
Description: "This workspace provides the credentials (ssh private key) for cloning the git repo. See https://hub.tekton.dev/tekton/task/git-clone",
},
)
}
if gitNeedsBasicAuthCreds {
pipeline.Spec.Workspaces = append(pipeline.Spec.Workspaces,
v1beta1.PipelineWorkspaceDeclaration{
Name: gitRepoBasicAuthCredsWorkspace,
Description: "This workspace provides the credentials (username and password) for cloning the git repo. See https://hub.tekton.dev/tekton/task/git-clone",
},
)
}
pipeline.Spec.Tasks = tasks
return pipeline
}
Expand Down
47 changes: 46 additions & 1 deletion transformer/kubernetes/apiresource/triggertemplate.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package apiresource

import (
"github.com/konveyor/move2kube/common"
"github.com/konveyor/move2kube/qaengine"
collecttypes "github.com/konveyor/move2kube/types/collection"
irtypes "github.com/konveyor/move2kube/types/ir"
"github.com/konveyor/move2kube/types/qaengine/commonqa"
Expand Down Expand Up @@ -87,7 +88,51 @@ func (*TriggerTemplate) createNewResource(tt irtypes.TriggerTemplate, ir irtypes
{Name: "image-registry-url", Value: v1beta1.ArrayOrString{Type: "string", StringVal: registryURL + "/" + registryNamespace}},
},
}

gitRepoSSHSecretName := qaengine.FetchStringAnswer(
common.ConfigCICDTektonGitRepoSSHSecretNameKey,
"Enter the name of an existing K8s secret that has ssh credentials for cloning the git repo",
[]string{"If this is not relevant to you then give an empty string to remove it from the YAML."},
"",
nil,
)
if gitRepoSSHSecretName != "" {
pipelineRun.Spec.Workspaces = append(pipelineRun.Spec.Workspaces, v1beta1.WorkspaceBinding{
Name: gitRepoSSHCredsWorkspace,
Secret: &corev1.SecretVolumeSource{
SecretName: gitRepoSSHSecretName,
},
})
}
gitRepoBasicAuthSecretName := qaengine.FetchStringAnswer(
common.ConfigCICDTektonGitRepoBasicAuthSecretNameKey,
"Enter the name of an existing K8s secret that has username and password for cloning the git repo",
[]string{"If this is not relevant to you then give an empty string to remove it from the YAML."},
"",
nil,
)
if gitRepoBasicAuthSecretName != "" {
pipelineRun.Spec.Workspaces = append(pipelineRun.Spec.Workspaces, v1beta1.WorkspaceBinding{
Name: gitRepoBasicAuthCredsWorkspace,
Secret: &corev1.SecretVolumeSource{
SecretName: gitRepoBasicAuthSecretName,
},
})
}
imageRegistryAuthSecretName := qaengine.FetchStringAnswer(
common.ConfigCICDTektonRegistryPushSecretNameKey,
"Enter the name of an existing K8s secret that has Docker config.json for pushing images to the registry",
[]string{"If this is not relevant to you then give an empty string to remove it from the YAML."},
"",
nil,
)
if imageRegistryAuthSecretName != "" {
pipelineRun.Spec.Workspaces = append(pipelineRun.Spec.Workspaces, v1beta1.WorkspaceBinding{
Name: registryCredsWorkspace,
Secret: &corev1.SecretVolumeSource{
SecretName: imageRegistryAuthSecretName,
},
})
}
// trigger template
triggerTemplate := new(triggersv1beta1.TriggerTemplate)
triggerTemplate.TypeMeta = metav1.TypeMeta{
Expand Down
14 changes: 11 additions & 3 deletions transformer/kubernetes/irpreprocessor/registrypreprocessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,24 @@ func (p registryPreProcessor) preprocess(ir irtypes.IR) (irtypes.IR, error) {
}
quesKey := fmt.Sprintf(common.ConfigImageRegistryLoginTypeKey, `"`+registry+`"`)
desc := fmt.Sprintf("[%s] What type of container registry login do you want to use?", registry)
hints := []string{"Docker login from config mode, will use the default config from your local machine."}
auth := qaengine.FetchSelectAnswer(quesKey, desc, hints, string(defaultOption), authOptions, nil)
auth := qaengine.FetchSelectAnswer(quesKey, desc, nil, string(defaultOption), authOptions, nil)
createPullSecret := false
switch registryLoginOption(auth) {
case noLogin:
regAuth.Auth = ""
delete(imagePullSecrets, registry)
case existingPullSecretLogin:
qaKey := fmt.Sprintf(common.ConfigImageRegistryPullSecretKey, `"`+registry+`"`)
ps := qaengine.FetchStringAnswer(qaKey, fmt.Sprintf("[%s] Enter the name of the pull secret : ", registry), []string{"The pull secret should exist in the namespace where you will be deploying the application."}, "", nil)
ps := qaengine.FetchStringAnswer(
qaKey,
fmt.Sprintf("[%s] Enter the name of the pull secret : ", registry),
[]string{"The pull secret should exist in the namespace where you will be deploying the application."},
"",
nil,
)
if ps == "" {
logrus.Errorf("the given pull secret name is empty. You will need to fix this in the YAMLs")
}
imagePullSecrets[registry] = ps
case usernamePasswordLogin:
createPullSecret = true
Expand Down
29 changes: 16 additions & 13 deletions types/ir/ir.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,14 @@ import (
"github.com/konveyor/move2kube/transformer/kubernetes/k8sschema"
transformertypes "github.com/konveyor/move2kube/types/transformer"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/strategicpatch"
core "k8s.io/kubernetes/pkg/apis/core"
networking "k8s.io/kubernetes/pkg/apis/networking"

corev1 "k8s.io/api/core/v1"
)

// IRArtifactType represents artifact type of IR
const IRArtifactType transformertypes.ArtifactType = "IR"

// IRConfigType represents config type of IR
const IRConfigType transformertypes.ConfigType = "IR"
// ContainerBuildTypeValue stores the container build type
type ContainerBuildTypeValue string

const (
// DockerfileContainerBuildType represents dockerfile container build type
Expand All @@ -48,11 +44,24 @@ const (
CNBContainerBuildTypeValue ContainerBuildTypeValue = "CNB"
)

// ContainerBuildArtifactTypeValue stores the container build artifact type
type ContainerBuildArtifactTypeValue string

const (
// DockerfileContainerBuildArtifactTypeValue represents dockerfile container build type artifact
DockerfileContainerBuildArtifactTypeValue ContainerBuildArtifactTypeValue = "Dockerfile"
// RelDockerfileContainerBuildArtifactTypeValue represents dockerfile container build type artifact
RelDockerfileContainerBuildArtifactTypeValue ContainerBuildArtifactTypeValue = "RelDockerfilePath"
// RelDockerfileContextContainerBuildArtifactTypeValue represents dockerfile container build type artifact
RelDockerfileContextContainerBuildArtifactTypeValue ContainerBuildArtifactTypeValue = "RelDockerfileContextPath"
)

// IRArtifactType represents artifact type of IR
const IRArtifactType transformertypes.ArtifactType = "IR"

// IRConfigType represents config type of IR
const IRConfigType transformertypes.ConfigType = "IR"

// IR is the intermediate representation filled by source transformers
type IR struct {
Name string
Expand Down Expand Up @@ -115,12 +124,6 @@ type ServiceToPodPortForwarding struct {
ServiceType core.ServiceType
}

// ContainerBuildTypeValue stores the container build type
type ContainerBuildTypeValue string

// ContainerBuildArtifactTypeValue stores the container build artifact type
type ContainerBuildArtifactTypeValue string

// ContainerImage defines images that need to be built or reused.
type ContainerImage struct {
ExposedPorts []int32 `yaml:"ports"`
Expand Down

0 comments on commit 883539c

Please sign in to comment.