Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: backport podpconfig to pr-379 #394

Closed
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
9 changes: 9 additions & 0 deletions cmd/template_backups.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ func BackupTemplateGeneration(g generator.GeneratorInput,
helpers.WriteTemplateFile(fmt.Sprintf("%s/%s.yaml", savedTemplates, "k8up-lagoon-backup-schedule"), templateYAML)
}

// generate the backup schedule templates
templateYAML, err = backuptemplate.GenerateBackupPodConfig(*lagoonBuild.BuildValues)
if err != nil {
return fmt.Errorf("couldn't generate template: %v", err)
}
if len(templateYAML) > 0 {
helpers.WriteTemplateFile(fmt.Sprintf("%s/%s.yaml", savedTemplates, "k8up-rootless-workload-podconfig"), templateYAML)
}

// generate any prebackuppod templates
templateYAML, err = backuptemplate.GeneratePreBackupPod(*lagoonBuild.BuildValues)
if err != nil {
Expand Down
25 changes: 23 additions & 2 deletions cmd/template_backups_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"reflect"
"testing"

"github.com/andreyvit/diff"
"github.com/uselagoon/build-deploy-tool/internal/dbaasclient"
"github.com/uselagoon/build-deploy-tool/internal/helpers"
"github.com/uselagoon/build-deploy-tool/internal/lagoon"
Expand Down Expand Up @@ -240,6 +241,27 @@ func TestBackupTemplateGeneration(t *testing.T) {
templatePath: "testdata/output",
want: "internal/testdata/node/backup-templates/backup-8",
},
{
name: "test-generic-backup-rootless-workloads",
args: testdata.GetSeedData(
testdata.TestData{
ProjectName: "example-project",
EnvironmentName: "main",
Branch: "main",
EnvironmentType: "production",
LagoonYAML: "internal/testdata/node/lagoon.yml",
K8UPVersion: "v2",
ProjectVariables: []lagoon.EnvironmentVariable{
{
Name: "LAGOON_FEATURE_FLAG_ROOTLESS_WORKLOAD",
Value: "enabled",
Scope: "build",
},
},
}, true),
templatePath: "testoutput",
want: "internal/testdata/node/backup-templates/test-generic-backup-rootless-workloads",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -307,8 +329,7 @@ func TestBackupTemplateGeneration(t *testing.T) {
t.Errorf("couldn't read file %v: %v", tt.want, err)
}
if !reflect.DeepEqual(f1, r1) {
fmt.Println(string(f1))
t.Errorf("resulting templates do not match")
t.Errorf("TemplateLagoonServices() = \n%v", diff.LineDiff(string(r1), string(f1)))
}
}
}
Expand Down
141 changes: 141 additions & 0 deletions internal/templating/backups/template_podconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package backups

import (
"fmt"

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

k8upv1 "github.com/k8up-io/k8up/v2/api/v1"
corev1 "k8s.io/api/core/v1"
apivalidation "k8s.io/apimachinery/pkg/api/validation"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metavalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"

"sigs.k8s.io/yaml"
)

func GenerateBackupPodConfig(
lValues generator.BuildValues,
) ([]byte, error) {
// generate the template spec

var result []byte
separator := []byte("---\n")

// create the podconfig
if lValues.BackupsEnabled {
switch lValues.Backup.K8upVersion {
case "v2":
if lValues.PodSecurityContext.RunAsUser != 0 {
podConfig := &k8upv1.PodConfig{
TypeMeta: metav1.TypeMeta{
Kind: "PodConfig",
APIVersion: k8upv1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "k8up-rootless-workload-podconfig",
},
Spec: k8upv1.PodConfigSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
SecurityContext: &corev1.PodSecurityContext{
RunAsUser: helpers.Int64Ptr(lValues.PodSecurityContext.RunAsUser),
RunAsGroup: helpers.Int64Ptr(lValues.PodSecurityContext.RunAsGroup),
FSGroup: helpers.Int64Ptr(lValues.PodSecurityContext.FsGroup),
},
},
},
},
}
if lValues.PodSecurityContext.OnRootMismatch {
fsGroupChangePolicy := corev1.FSGroupChangeOnRootMismatch
if podConfig.Spec.Template.Spec.SecurityContext != nil {
podConfig.Spec.Template.Spec.SecurityContext.FSGroupChangePolicy = &fsGroupChangePolicy
} else {
podConfig.Spec.Template.Spec.SecurityContext = &corev1.PodSecurityContext{
FSGroupChangePolicy: &fsGroupChangePolicy,
}
}
}
// add the default labels
podConfig.ObjectMeta.Labels = map[string]string{
"app.kubernetes.io/name": "k8up-podconfig",
"app.kubernetes.io/instance": "k8up-rootless-workload-podconfig",
"app.kubernetes.io/managed-by": "build-deploy-tool",
"lagoon.sh/template": fmt.Sprintf("%s-%s", "k8up-podconfig", "0.1.0"),
"lagoon.sh/service": "k8up-rootless-workload-podconfig",
"lagoon.sh/service-type": "k8up-podconfig",
"lagoon.sh/project": lValues.Project,
"lagoon.sh/environment": lValues.Environment,
"lagoon.sh/environmentType": lValues.EnvironmentType,
"lagoon.sh/buildType": lValues.BuildType,
}

// add the default annotations
podConfig.ObjectMeta.Annotations = map[string]string{
"lagoon.sh/version": lValues.LagoonVersion,
}

// add any additional labels
additionalLabels := map[string]string{}
additionalAnnotations := map[string]string{}
if lValues.BuildType == "branch" {
additionalAnnotations["lagoon.sh/branch"] = lValues.Branch
} else if lValues.BuildType == "pullrequest" {
additionalAnnotations["lagoon.sh/prNumber"] = lValues.PRNumber
additionalAnnotations["lagoon.sh/prHeadBranch"] = lValues.PRHeadBranch
additionalAnnotations["lagoon.sh/prBaseBranch"] = lValues.PRBaseBranch

}
for key, value := range additionalLabels {
podConfig.ObjectMeta.Labels[key] = value
}
// add any additional annotations
for key, value := range additionalAnnotations {
podConfig.ObjectMeta.Annotations[key] = value
}
// validate any annotations
if err := apivalidation.ValidateAnnotations(podConfig.ObjectMeta.Annotations, nil); err != nil {
if len(err) != 0 {
return nil, fmt.Errorf("the annotations for %s are not valid: %v", "k8up-rootless-workload-podconfig", err)
}
}
// validate any labels
if err := metavalidation.ValidateLabels(podConfig.ObjectMeta.Labels, nil); err != nil {
if len(err) != 0 {
return nil, fmt.Errorf("the labels for %s are not valid: %v", "k8up-rootless-workload-podconfig", err)
}
}

// check length of labels
err := helpers.CheckLabelLength(podConfig.ObjectMeta.Labels)
if err != nil {
return nil, err
}
// @TODO: we should review this in the future when we stop doing `kubectl apply` in the builds :)
// marshal the resulting ingress
podconfigBytes, err := yaml.Marshal(podConfig)
if err != nil {
return nil, err
}
podconfigBytes, _ = CleanupPodConfigYAML(podconfigBytes)
// add the seperator to the template so that it can be `kubectl apply` in bulk as part
// of the current build process
result = append(separator[:], podconfigBytes[:]...)
}
}
}
return result, nil
}

// helper function to remove data from the yaml spec so that kubectl will apply without validation errors
// this is only needed because we use kubectl in builds for now
func CleanupPodConfigYAML(a []byte) ([]byte, error) {
tmpMap := map[string]interface{}{}
yaml.Unmarshal(a, &tmpMap)
delete(tmpMap["spec"].(map[string]interface{})["template"].(map[string]interface{}), "metadata")
delete(tmpMap["spec"].(map[string]interface{})["template"].(map[string]interface{})["spec"].(map[string]interface{}), "containers")
b, _ := yaml.Marshal(tmpMap)
return b, nil
}
133 changes: 133 additions & 0 deletions internal/templating/backups/template_podconfig_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package backups

import (
"os"
"reflect"
"testing"
"time"

"github.com/andreyvit/diff"
"github.com/uselagoon/build-deploy-tool/internal/dbaasclient"
"github.com/uselagoon/build-deploy-tool/internal/generator"
)

func TestGenerateBackupPodConfig(t *testing.T) {
type args struct {
lValues generator.BuildValues
}
tests := []struct {
name string
description string
args args
want string
wantErr bool
wantEmpty bool
}{
{
name: "test-k8up-v1-rootless",
description: "this will generate a podconfig if the environment is configured for rootless workloads",
args: args{
lValues: generator.BuildValues{
Project: "example-project",
Environment: "environment",
EnvironmentType: "production",
Namespace: "myexample-project-environment",
BuildType: "branch",
LagoonVersion: "v2.x.x",
Kubernetes: "generator.local",
Branch: "environment",
BackupsEnabled: true,
Backup: generator.BackupConfiguration{
K8upVersion: "v2",
},
FeatureFlags: map[string]bool{
"rootlessworkloads": true,
},
PodSecurityContext: generator.PodSecurityContext{
RunAsGroup: 0,
RunAsUser: 10000,
FsGroup: 10001,
},
},
},
want: "test-resources/test-k8up-v1-rootless.yaml",
},
{
name: "test-k8up-v1-rootless-onrootmismatch",
description: "this will generate a podconfig if the environment is configured for rootless workloads",
args: args{
lValues: generator.BuildValues{
Project: "example-project",
Environment: "environment",
EnvironmentType: "production",
Namespace: "myexample-project-environment",
BuildType: "branch",
LagoonVersion: "v2.x.x",
Kubernetes: "generator.local",
Branch: "environment",
BackupsEnabled: true,
Backup: generator.BackupConfiguration{
K8upVersion: "v2",
},
FeatureFlags: map[string]bool{
"rootlessworkloads": true,
},
PodSecurityContext: generator.PodSecurityContext{
RunAsGroup: 0,
RunAsUser: 10000,
FsGroup: 10001,
OnRootMismatch: true,
},
},
},
want: "test-resources/test-k8up-v1-rootless-onrootmismatch.yaml",
},
{
name: "test-k8up-v1-root",
description: "this will not generate a podconfig if the environment is not configured for rootless workloads",
args: args{
lValues: generator.BuildValues{
Project: "example-project",
Environment: "environment",
EnvironmentType: "production",
Namespace: "myexample-project-environment",
BuildType: "branch",
LagoonVersion: "v2.x.x",
Kubernetes: "generator.local",
Branch: "environment",
BackupsEnabled: true,
Backup: generator.BackupConfiguration{
K8upVersion: "v2",
},
},
},
wantEmpty: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// add dbaasclient overrides for tests
tt.args.lValues.DBaaSClient = dbaasclient.NewClient(dbaasclient.Client{
RetryMax: 5,
RetryWaitMin: time.Duration(10) * time.Millisecond,
RetryWaitMax: time.Duration(50) * time.Millisecond,
})
got, err := GenerateBackupPodConfig(tt.args.lValues)
if err != nil {
t.Errorf("couldn't generate template %v: %v", tt.want, err)
}
if tt.wantEmpty && len(got) > 0 {
t.Errorf("wanted empty, but got data:\n%v", string(got))
}
if !tt.wantEmpty {
r1, err := os.ReadFile(tt.want)
if err != nil {
t.Errorf("couldn't read file %v: %v", tt.want, err)
}
if !reflect.DeepEqual(string(got), string(r1)) {
t.Errorf("GenerateBackupPodConfig() = \n%v", diff.LineDiff(string(r1), string(got)))
}
}
})
}
}
20 changes: 9 additions & 11 deletions internal/templating/backups/template_prebackuppod.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ func GeneratePreBackupPod(
return nil, err
}

pbpBytes, _ := RemoveYAML(prebackuppodBytes)
pbpBytes, _ := CleanupPreBackupPodYAML(prebackuppodBytes)
// add the seperator to the template so that it can be `kubectl apply` in bulk as part
// of the current build process
restoreResult := append(separator[:], pbpBytes[:]...)
Expand Down Expand Up @@ -238,7 +238,7 @@ func GeneratePreBackupPod(
if err != nil {
return nil, err
}
pbpBytes, _ := RemoveYAML(prebackuppodBytes)
pbpBytes, _ := CleanupPreBackupPodYAML(prebackuppodBytes)
// add the seperator to the template so that it can be `kubectl apply` in bulk as part
// of the current build process
restoreResult := append(separator[:], pbpBytes[:]...)
Expand All @@ -249,16 +249,14 @@ func GeneratePreBackupPod(
return result, nil
}

// helper function to remove the creationtimestamp from the prebackuppod pod spec so that kubectl will apply without validation errors
func RemoveYAML(a []byte) ([]byte, error) {
// helper function to remove data from the yaml spec so that kubectl will apply without validation errors
// this is only needed because we use kubectl in builds for now
func CleanupPreBackupPodYAML(a []byte) ([]byte, error) {
tmpMap := map[string]interface{}{}
yaml.Unmarshal(a, &tmpMap)
if _, ok := tmpMap["spec"].(map[string]interface{})["pod"].(map[string]interface{})["metadata"].(map[string]interface{})["creationTimestamp"]; ok {
delete(tmpMap["spec"].(map[string]interface{})["pod"].(map[string]interface{})["metadata"].(map[string]interface{}), "creationTimestamp")
b, _ := yaml.Marshal(tmpMap)
return b, nil
}
return a, nil
delete(tmpMap["spec"].(map[string]interface{})["pod"].(map[string]interface{})["metadata"].(map[string]interface{}), "creationTimestamp")
b, _ := yaml.Marshal(tmpMap)
return b, nil
}

var funcMap = template.FuncMap{
Expand All @@ -267,7 +265,7 @@ var funcMap = template.FuncMap{

// varfix just uppercases and replaces - with _ for variable names
func varFix(s string) string {
return fmt.Sprintf("%s", strings.ToUpper(strings.Replace(s, "-", "_", -1)))
return strings.ToUpper(strings.Replace(s, "-", "_", -1))
}

// this is just the first run at doing this, once the service template generator is introduced, this will need to be re-evaluated
Expand Down
5 changes: 5 additions & 0 deletions internal/templating/backups/template_schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ func GenerateBackupSchedule(
},
},
}
if lValues.PodSecurityContext.RunAsUser != 0 {
schedule.Spec.Backup.PodConfigRef = &corev1.LocalObjectReference{
Name: "k8up-rootless-workload-podconfig",
}
}
// add the default labels
schedule.ObjectMeta.Labels = map[string]string{
"app.kubernetes.io/name": "k8up-schedule",
Expand Down
Loading
Loading