github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/tests/integration-tests/rollout_test.go (about) 1 /*This file is part of kuberpult. 2 3 Kuberpult is free software: you can redistribute it and/or modify 4 it under the terms of the Expat(MIT) License as published by 5 the Free Software Foundation. 6 7 Kuberpult is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 MIT License for more details. 11 12 You should have received a copy of the MIT License 13 along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>. 14 15 Copyright 2023 freiheit.com*/ 16 17 package integration_tests 18 19 import ( 20 "bytes" 21 "io" 22 "os/exec" 23 "strings" 24 "testing" 25 26 "github.com/google/go-cmp/cmp" 27 "gopkg.in/yaml.v3" 28 ) 29 30 type simplifiedArgoApp struct { 31 Metadata struct { 32 Annotations map[string]string `yaml:"annotations"` 33 } `yaml:"metadata"` 34 Status struct { 35 OperationState struct { 36 Phase string `yaml:"phase"` 37 } `yaml:"operationState"` 38 } `yaml:"status"` 39 } 40 41 type simplifiedConfigMapMeta struct { 42 Name string `yaml:"name"` 43 Namespace string `yaml:"namespace"` 44 } 45 46 type simplifiedConfigMap struct { 47 Metadata simplifiedConfigMapMeta `yaml:"metadata"` 48 Data map[string]string `yaml:"data"` 49 Kind string `yaml:"kind"` 50 ApiVersion string `yaml:"apiVersion"` 51 } 52 53 func TestArgoRolloutWork(t *testing.T) { 54 testCases := []struct { 55 name string 56 app string 57 }{ 58 { 59 name: "it can sync manifests into the cluster", 60 app: "rollout-" + appSuffix, 61 }, 62 } 63 for _, tc := range testCases { 64 tc := tc 65 t.Run(tc.name, func(t *testing.T) { 66 appName := "development-" + tc.app 67 expectedConfig := simplifiedConfigMap{ 68 ApiVersion: "v1", 69 Kind: "ConfigMap", 70 Metadata: simplifiedConfigMapMeta{ 71 Name: tc.app, 72 Namespace: "development", 73 }, 74 Data: map[string]string{ 75 "key": tc.app, 76 }, 77 } 78 data, err := yaml.Marshal(expectedConfig) 79 if err != nil { 80 t.Fatal(err) 81 } 82 // Release a new app that we start completely fresh 83 releaseApp(t, tc.app, map[string]string{ 84 "development": string(data), 85 }) 86 // We have to sync the root app once because we have created a new app 87 runArgo(t, "app", "sync", "root") 88 // The sync may already be in progress, therefore we wait here for pending operations to finish 89 runArgo(t, "app", "wait", appName, "--operation") 90 runArgo(t, "app", "sync", appName) 91 _, appData := runArgo(t, "app", "get", appName, "-o", "yaml") 92 var app simplifiedArgoApp 93 err = yaml.Unmarshal(appData, &app) 94 if err != nil { 95 t.Fatal(err) 96 } 97 appAnnotation := app.Metadata.Annotations["com.freiheit.kuberpult/application"] 98 if appAnnotation != tc.app { 99 t.Errorf("wrong value for annotation \"com.freiheit.kuberpult/application\": expected %q but got %q", tc.app, appAnnotation) 100 } 101 if app.Status.OperationState.Phase != "Succeeded" { 102 t.Errorf("wrong value for operation state phase, expected %q got %q", "Succeeded", app.Status.OperationState.Phase) 103 } 104 _, manifestData := runArgo(t, "app", "manifests", appName) 105 var actualConfig simplifiedConfigMap 106 err = yaml.Unmarshal(manifestData, &actualConfig) 107 if err != nil { 108 t.Fatal(err) 109 } 110 d := cmp.Diff(expectedConfig, actualConfig) 111 if d != "" { 112 t.Errorf("unexpected diff between config maps: %s", d) 113 } 114 }) 115 } 116 } 117 118 func runArgo(t *testing.T, args ...string) (*exec.Cmd, []byte) { 119 var out, stderr bytes.Buffer 120 args = append([]string{"--port-forward"}, args...) 121 cmd := exec.Command("argocd", args...) 122 cmd.Stdout = &out 123 cmd.Stderr = &stderr 124 err := cmd.Run() 125 if err != nil { 126 t.Fatalf("argocd %q command exited with code %d\nerr: %s\nstderr: %s", strings.Join(args, " "), cmd.ProcessState.ExitCode(), err, stderr.String()) 127 } 128 if cmd.ProcessState.ExitCode() != 0 { 129 t.Fatalf("argocd %q command exited with code %d\nstderr: %s", strings.Join(args, " "), cmd.ProcessState.ExitCode(), stderr.String()) 130 } 131 return cmd, out.Bytes() 132 } 133 134 func releaseApp(t *testing.T, application string, manifests map[string]string) { 135 values := map[string]io.Reader{ 136 "application": strings.NewReader(application), 137 } 138 files := map[string]io.Reader{} 139 for env, data := range manifests { 140 files["manifests["+env+"]"] = strings.NewReader(data) 141 files["signatures["+env+"]"] = strings.NewReader(CalcSignature(t, data)) 142 } 143 actualStatusCode, body, err := callRelease(values, files) 144 if err != nil { 145 t.Fatal(err) 146 } 147 if actualStatusCode > 299 { 148 t.Fatalf("bad status code: %d, body: %s", actualStatusCode, body) 149 } 150 }