Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions cmd/template_gitcredential.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package cmd

import (
"fmt"
"net/url"
"os"

"github.com/spf13/cobra"
generator "github.com/uselagoon/build-deploy-tool/internal/generator"
"github.com/uselagoon/build-deploy-tool/internal/helpers"
"github.com/uselagoon/build-deploy-tool/internal/lagoon"
)

var templateGitCredential = &cobra.Command{
Use: "git-credential",
Aliases: []string{"gitcred", "gc"},
Short: "Create a git credential file",
RunE: func(cmd *cobra.Command, args []string) error {
credFile, err := cmd.Flags().GetString("credential-file")
if err != nil {
return fmt.Errorf("error reading credential-file flag: %v", err)
}
gitURL, err := TemplateGitCredential(credFile)
if err != nil {
return err
}
fmt.Println(gitURL)
return nil
},
}

// TemplateGitCredential creates a git credential secret if required
func TemplateGitCredential(file string) (string, error) {
// get the source repository from the build pod environment variables
sourceRepository := helpers.GetEnv("SOURCE_REPOSITORY", "", false)

// because this runs before anything has been checked out in the repo, we have to query variables directly
// and handle any merging that is usually done in generator
variables := generator.GetLagoonEnvVars()

// parse the repository into a url struct so the username and password can be injected into http/https based urls
u, err := url.Parse(sourceRepository)
if err != nil {
return "", fmt.Errorf("unable to parse provided gitUrl")
}
cred := url.URL{}
// if this is a http or https based url, it may need a username and password
if helpers.Contains([]string{"http", "https"}, u.Scheme) {
// since the provided source repository could be public or private, check for the `LAGOON_GIT_HTTPS_X`
// variables, ignore errors for these lookups as they're in most cases going to be "not found"
username, _ := lagoon.GetLagoonVariable("LAGOON_GIT_HTTPS_USERNAME", []string{"build"}, variables)
password, _ := lagoon.GetLagoonVariable("LAGOON_GIT_HTTPS_PASSWORD", []string{"build"}, variables)
if username != nil && password == nil {
return "", fmt.Errorf("LAGOON_GIT_HTTPS_USERNAME was provided, but not LAGOON_GIT_HTTPS_PASSWORD")
}
if username == nil && password != nil {
return "", fmt.Errorf("LAGOON_GIT_HTTPS_PASSWORD was provided, but not LAGOON_GIT_HTTPS_USERNAME")
}
// if both are found, set the user auth into the url
if username != nil && password != nil {
u.User = url.UserPassword(username.Value, password.Value)
cred.Scheme = u.Scheme
cred.Host = u.Host
cred.User = u.User
err := os.WriteFile(file, []byte(cred.String()), 0644)
if err != nil {
return "", err
}
// return the new url only if it was modified with a username and password
return "store", nil
}
}
// otherwise return whatever was provided to the build
return "", nil
}

func init() {
templateCmd.AddCommand(templateGitCredential)
templateGitCredential.Flags().String("credential-file", "", "The file to store the credential in")
}
142 changes: 142 additions & 0 deletions cmd/template_gitcredential_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package cmd

import (
"fmt"
"os"
"reflect"
"testing"

"github.com/andreyvit/diff"
"github.com/uselagoon/build-deploy-tool/internal/helpers"

// changes the testing to source from root so paths to test resources must be defined from repo root
_ "github.com/uselagoon/build-deploy-tool/internal/testing"
)

func TestTemplateGitCredential(t *testing.T) {
tests := []struct {
name string
vars []helpers.EnvironmentVariable
want string
resultFilename string
testResultfilename string
wantFile bool
wantErr bool
}{
{
name: "test1 check if variables are defined",
vars: []helpers.EnvironmentVariable{
{
Name: "SOURCE_REPOSITORY",
Value: "https://example.com/lagoon-demo.git",
},
{
Name: "LAGOON_ENVIRONMENT_VARIABLES",
Value: `[{"name":"LAGOON_GIT_HTTPS_USERNAME","scope":"build","value":"user1"},{"name":"LAGOON_GIT_HTTPS_PASSWORD","scope":"build","value":"somep@ssword"}]`,
},
},
resultFilename: "test1",
testResultfilename: "internal/testdata/git-credentials/test1",
wantFile: true,
want: "store",
},
{
name: "test2 check if variable are defined (no username)",
vars: []helpers.EnvironmentVariable{
{
Name: "SOURCE_REPOSITORY",
Value: "https://example.com/lagoon-demo.git",
},
{
Name: "LAGOON_ENVIRONMENT_VARIABLES",
Value: `[{"name":"LAGOON_GIT_HTTPS_PASSWORD","scope":"build","value":"somep@ssword"}]`,
},
},
wantErr: true,
want: "",
},
{
name: "test3 check if variable are defined (no password)",
vars: []helpers.EnvironmentVariable{
{
Name: "SOURCE_REPOSITORY",
Value: "https://example.com/lagoon-demo.git",
},
{
Name: "LAGOON_ENVIRONMENT_VARIABLES",
Value: `[{"name":"LAGOON_GIT_HTTPS_USERNAME","scope":"build","value":"user1"}]`,
},
},
wantErr: true,
want: "",
},
{
name: "test4 no username or password",
vars: []helpers.EnvironmentVariable{
{
Name: "SOURCE_REPOSITORY",
Value: "https://example.com/lagoon-demo.git",
},
{
Name: "LAGOON_ENVIRONMENT_VARIABLES",
Value: `[]`,
},
},
want: "",
},
{
name: "test5 ssh pass through",
vars: []helpers.EnvironmentVariable{
{
Name: "SOURCE_REPOSITORY",
Value: "ssh://git@example.com/lagoon-demo.git",
},
{
Name: "LAGOON_ENVIRONMENT_VARIABLES",
Value: `[]`,
},
},
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for _, envVar := range tt.vars {
err := os.Setenv(envVar.Name, envVar.Value)
if err != nil {
t.Errorf("%v", err)
}
}
tempResults := "testoutput"
err := os.MkdirAll(tempResults, 0755)
if err != nil {
t.Errorf("couldn't create directory %v: %v", tempResults, err)
}
defer os.RemoveAll(tempResults)
got, err := TemplateGitCredential(fmt.Sprintf("%s/%s", tempResults, tt.resultFilename))
if (err != nil) != tt.wantErr {
t.Errorf("TemplateGitCredential() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("TemplateGitCredential() = %v, want %v", got, tt.want)
}
if tt.wantFile {
f1, err := os.ReadFile(fmt.Sprintf("%s/%s", tempResults, tt.resultFilename))
if err != nil {
t.Errorf("couldn't read file %v: %v", tempResults, err)
}
r1, err := os.ReadFile(tt.testResultfilename)
if err != nil {
t.Errorf("couldn't read file %v: %v", tt.wantFile, err)
}
if !reflect.DeepEqual(f1, r1) {
t.Errorf("TemplateGitCredential() = \n%v", diff.LineDiff(string(r1), string(f1)))
}
}
t.Cleanup(func() {
helpers.UnsetEnvVars(tt.vars)
})
})
}
}
12 changes: 2 additions & 10 deletions internal/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,6 @@ func NewGenerator(
// if k8upv2 (k8up.io/v1) version is specified by remote, set that here
buildValues.Backup.K8upVersion = "v2"
}
// get the project and environment variables
projectVariables := helpers.GetEnv("LAGOON_PROJECT_VARIABLES", generator.ProjectVariables, generator.Debug)
environmentVariables := helpers.GetEnv("LAGOON_ENVIRONMENT_VARIABLES", generator.EnvironmentVariables, generator.Debug)

// read the .lagoon.yml file and the LAGOON_YAML_OVERRIDE if set
if err := LoadAndUnmarshalLagoonYml(generator.LagoonYAML, generator.LagoonYAMLOverride, "LAGOON_YAML_OVERRIDE", lYAML, projectName, generator.Debug); err != nil {
Expand Down Expand Up @@ -269,14 +266,9 @@ func NewGenerator(
buildValues.DynamicDBaaSSecrets = strings.Split(dynamicDBaaSSecrets, ",")
}

// unmarshal and then merge the two so there is only 1 set of variables to iterate over
projectVars := []lagoon.EnvironmentVariable{}
envVars := []lagoon.EnvironmentVariable{}
json.Unmarshal([]byte(projectVariables), &projectVars)
json.Unmarshal([]byte(environmentVariables), &envVars)

// get the project and environment variables
// set the environment variables to all the known merged variables so far
buildValues.EnvironmentVariables = lagoon.MergeVariables(projectVars, envVars)
buildValues.EnvironmentVariables = GetLagoonEnvVars()

// if the core version is provided from the API, set the buildvalues LagoonVersion to this instead
lagoonCoreVersion, _ := lagoon.GetLagoonVariable("LAGOON_SYSTEM_CORE_VERSION", []string{"internal_system"}, buildValues.EnvironmentVariables)
Expand Down
15 changes: 15 additions & 0 deletions internal/generator/helpers_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"github.com/spf13/cobra"
"github.com/uselagoon/build-deploy-tool/internal/dbaasclient"
"github.com/uselagoon/build-deploy-tool/internal/helpers"
"github.com/uselagoon/build-deploy-tool/internal/lagoon"
"k8s.io/apimachinery/pkg/api/resource"
)
Expand Down Expand Up @@ -314,3 +315,17 @@ func determineRefreshImage(serviceName, imageName string, envVars []lagoon.Envir
}
return parsed, errs
}

// GetLagoonEnvVars will get the project and environment variables from the build
// unmarshal them and then merge them down, where environment will override project
func GetLagoonEnvVars() []lagoon.EnvironmentVariable {
projectVariables := helpers.GetEnv("LAGOON_PROJECT_VARIABLES", "", false)
environmentVariables := helpers.GetEnv("LAGOON_ENVIRONMENT_VARIABLES", "", false)
// unmarshal and then merge the two so there is only 1 set of variables to iterate over
projectVars := []lagoon.EnvironmentVariable{}
envVars := []lagoon.EnvironmentVariable{}
_ = json.Unmarshal([]byte(projectVariables), &projectVars)
_ = json.Unmarshal([]byte(environmentVariables), &envVars)
// set the environment variables to all the known merged variables so far
return lagoon.MergeVariables(projectVars, envVars)
}
1 change: 1 addition & 0 deletions internal/testdata/git-credentials/test1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://user1:somep%40ssword@example.com
4 changes: 4 additions & 0 deletions legacy/build-deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export NAMESPACE
export LAGOON_VERSION

echo -e "##############################################\nBEGIN Checkout Repository\n##############################################"
# check if a http/https url is defined, and if a username/password are supplied for it
if [ "$(build-deploy-tool template git-credential --credential-file /home/.git-credentials)" == "store" ]; then
git config --global credential.helper 'store --file /home/.git-credentials'
fi
if [ "$BUILD_TYPE" == "pullrequest" ]; then
/kubectl-build-deploy/scripts/git-checkout-pull-merge.sh "$SOURCE_REPOSITORY" "$PR_HEAD_SHA" "$PR_BASE_SHA"
else
Expand Down
Loading