Skip to content

Commit

Permalink
feat: add support for organization environment variables
Browse files Browse the repository at this point in the history
  • Loading branch information
rocketeerbkw committed Dec 20, 2024
1 parent e9f8dc4 commit 6a762f3
Show file tree
Hide file tree
Showing 10 changed files with 338 additions and 167 deletions.
9 changes: 8 additions & 1 deletion cmd/config_fastly.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ func FastlyConfigGeneration(debug bool, domain string) (lagoon.Fastly, error) {
if err != nil {
return lagoon.Fastly{}, fmt.Errorf("error reading fastly-service-id flag: %v", err)
}
organizationVariables, err := rootCmd.PersistentFlags().GetString("organization-variables")
if err != nil {
return lagoon.Fastly{}, fmt.Errorf("error reading organization-variables flag: %v", err)
}
projectVariables, err := rootCmd.PersistentFlags().GetString("project-variables")
if err != nil {
return lagoon.Fastly{}, fmt.Errorf("error reading project-variables flag: %v", err)
Expand All @@ -53,15 +57,18 @@ func FastlyConfigGeneration(debug bool, domain string) (lagoon.Fastly, error) {
fastlyServiceID = helpers.GetEnv("ROUTE_FASTLY_SERVICE_ID", fastlyServiceID, debug)

// get the project and environment variables
organizationVariables = helpers.GetEnv("LAGOON_ORGANIZATION_VARIABLES", organizationVariables, debug)
projectVariables = helpers.GetEnv("LAGOON_PROJECT_VARIABLES", projectVariables, debug)
environmentVariables = helpers.GetEnv("LAGOON_ENVIRONMENT_VARIABLES", environmentVariables, debug)

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

// generate the fastly configuration from the provided flags/variables
f := &lagoon.Fastly{}
Expand Down
20 changes: 20 additions & 0 deletions cmd/identify_feature_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,26 @@ func TestIdentifyFeatureFlag(t *testing.T) {
},
want: "enabled",
},
{
name: "test7 check if flag is defined in lagoon organization variables",
varName: "ROOTLESS_WORKLOAD",
args: testdata.GetSeedData(
testdata.TestData{
ProjectName: "example-project",
EnvironmentName: "main",
Branch: "main",
LagoonYAML: "internal/testdata/node/lagoon.yml",
OrganizationVariables: []lagoon.EnvironmentVariable{
{
Name: "LAGOON_FEATURE_FLAG_ROOTLESS_WORKLOAD",
Value: "enabled",
Scope: "build",
},
},
}, true),
templatePath: "testoutput",
want: "enabled",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
2 changes: 2 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ func init() {
"The pullrequest base branch")
rootCmd.PersistentFlags().StringP("lagoon-version", "L", "",
"The lagoon version")
rootCmd.PersistentFlags().StringP("organization-variables", "", "",
"The JSON payload for organization scope variables")
rootCmd.PersistentFlags().StringP("project-variables", "", "",
"The JSON payload for project scope variables")
rootCmd.PersistentFlags().StringP("environment-variables", "", "",
Expand Down
1 change: 1 addition & 0 deletions docs/buildrequirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ These are variables that are injected into a build pod by `remote-controller`, s
* `PROMOTION_SOURCE_ENVIRONMENT` contains the source environment name if this is a promotion type build

#### Environment Variables
* `LAGOON_ORGANIZATION_VARIABLES` contains any organization specific environment variables
* `LAGOON_PROJECT_VARIABLES` contains any project specific environment variables
* `LAGOON_ENVIRONMENT_VARIABLES` contains any environment specific environment variables

Expand Down
9 changes: 5 additions & 4 deletions internal/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type GeneratorInput struct {
EnvironmentType string
ActiveEnvironment string
StandbyEnvironment string
OrganizationVariables string
ProjectVariables string
EnvironmentVariables string
BuildType string
Expand Down Expand Up @@ -139,6 +140,7 @@ func NewGenerator(
buildValues.Backup.K8upVersion = helpers.GetEnv("K8UP_VERSION", generator.BackupConfiguration.K8upVersion, generator.Debug)

// get the project and environment variables
organizationVariables := helpers.GetEnv("LAGOON_ORGANIZATION_VARIABLES", generator.OrganizationVariables, generator.Debug)
projectVariables := helpers.GetEnv("LAGOON_PROJECT_VARIABLES", generator.ProjectVariables, generator.Debug)
environmentVariables := helpers.GetEnv("LAGOON_ENVIRONMENT_VARIABLES", generator.EnvironmentVariables, generator.Debug)

Expand Down Expand Up @@ -245,16 +247,15 @@ func NewGenerator(
}

// unmarshal and then merge the two so there is only 1 set of variables to iterate over
orgVars := []lagoon.EnvironmentVariable{}
projectVars := []lagoon.EnvironmentVariable{}
envVars := []lagoon.EnvironmentVariable{}
json.Unmarshal([]byte(organizationVariables), &orgVars)
json.Unmarshal([]byte(projectVariables), &projectVars)
json.Unmarshal([]byte(environmentVariables), &envVars)
mergedVariables := lagoon.MergeVariables(projectVars, envVars)
// collect a bunch of the default LAGOON_X based build variables that are injected into `lagoon-env` and make them available
configVars := collectBuildVariables(buildValues)
// add the calculated build runtime variables into the existing variable slice
// this will later be used to add `runtime|global` scope into the `lagoon-env` configmap
buildValues.EnvironmentVariables = lagoon.MergeVariables(mergedVariables, configVars)
buildValues.EnvironmentVariables = lagoon.MergeVariables(orgVars, projectVars, envVars, configVars)

// 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
1 change: 1 addition & 0 deletions internal/helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ func UnsetEnvVars(localVars []EnvironmentVariable) {
"ACTIVE_ENVIRONMENT",
"STANDBY_ENVIRONMENT",
"LAGOON_FASTLY_NOCACHE_SERVICE_ID",
"LAGOON_ORGANIZATION_VARIABLES",
"LAGOON_PROJECT_VARIABLES",
"LAGOON_ENVIRONMENT_VARIABLES",
"LAGOON_VERSION",
Expand Down
72 changes: 40 additions & 32 deletions internal/lagoon/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package lagoon

import (
"fmt"
"slices"

"github.com/uselagoon/build-deploy-tool/internal/helpers"
)
Expand All @@ -14,44 +15,51 @@ type EnvironmentVariable struct {
}

// MergeVariables merges lagoon environment variables.
func MergeVariables(project, environment []EnvironmentVariable) []EnvironmentVariable {
allVars := []EnvironmentVariable{}
existsInEnvironment := false
// replace any variables from the project with ones from the environment
// this only modifies ones that exist in project
for _, pVar := range project {
add := EnvironmentVariable{}
for _, eVar := range environment {
// internal_system scoped variables are only added to the projects variabled during a build
// this make sure that any that may exist in the environment variables are not merged
// and also makes sure that internal_system variables are not replaced by other scopes
if eVar.Name == pVar.Name && pVar.Scope != "internal_system" && eVar.Scope != "internal_system" {
existsInEnvironment = true
add = eVar
}
func MergeVariables(organization, project, environment, config []EnvironmentVariable) []EnvironmentVariable {

// Helper function to compare environment variable names.
findByName := func(name string) func(EnvironmentVariable) bool {
return func(eVar EnvironmentVariable) bool { return eVar.Name == name }
}

// Start with config variables since they are most specific.
allVars := make([]EnvironmentVariable, len(config))
copy(allVars, config)

for _, eVar := range environment {
idx := slices.IndexFunc(allVars, findByName(eVar.Name))

// Append environment variables that are distinct.
if idx == -1 {
allVars = append(allVars, eVar)
}
if existsInEnvironment {
allVars = append(allVars, add)
existsInEnvironment = false
} else {
}

for _, pVar := range project {
idx := slices.IndexFunc(allVars, findByName(pVar.Name))

// Append project variables that are distinct.
if idx == -1 {
allVars = append(allVars, pVar)
continue
}
}
// add any that exist in the environment only to the final variables list
existsInProject := false
for _, eVar := range environment {
add := eVar
for _, aVar := range allVars {
if eVar.Name == aVar.Name {
existsInProject = true
}

// Overwrite environment variables if they are suppossed to be internally
// scoped.
if pVar.Scope == "internal_system" {
allVars[idx] = pVar
}
if existsInProject {
existsInProject = false
} else {
allVars = append(allVars, add)
}

for _, oVar := range organization {
idx := slices.IndexFunc(allVars, findByName(oVar.Name))

// Append organization variables that are distinct.
if idx == -1 {
allVars = append(allVars, oVar)
}
}

return allVars
}

Expand Down
Loading

0 comments on commit 6a762f3

Please sign in to comment.