github.com/mgoltzsche/khelm@v1.0.1/cmd/khelm/fn_test.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "os" 7 "path" 8 "path/filepath" 9 "testing" 10 11 "github.com/mgoltzsche/khelm/internal/output" 12 "github.com/mgoltzsche/khelm/pkg/config" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 "sigs.k8s.io/kustomize/kyaml/yaml" 16 ) 17 18 func TestKptFnCommand(t *testing.T) { 19 dir, err := ioutil.TempDir("", "khelm-fn-test-") 20 require.NoError(t, err) 21 defer os.RemoveAll(dir) 22 os.Setenv("HELM_HOME", dir) 23 defer os.Unsetenv("HELM_HOME") 24 exampleDir := filepath.Join("..", "..", "example") 25 26 inputAnnotations := map[string]interface{}{} 27 inputItems := []map[string]interface{}{ 28 { 29 // should be preserved 30 "somekey": "somevalue", 31 }, 32 { 33 // should be filtered 34 "apiVersion": "v1", 35 "kind": "ConfigMap", 36 "metadata": map[string]interface{}{"annotations": inputAnnotations}, 37 }, 38 } 39 40 for _, c := range []struct { 41 name string 42 input kptFnConfig 43 mustContainObj int 44 mustContain []string 45 }{ 46 { 47 "chart path only", 48 kptFnConfig{ChartConfig: &config.ChartConfig{ 49 LoaderConfig: config.LoaderConfig{ 50 Chart: filepath.Join(exampleDir, "namespace"), 51 }, 52 }}, 53 3, []string{"myconfiga"}, 54 }, 55 { 56 "latest cluster scoped remote chart", 57 kptFnConfig{ChartConfig: &config.ChartConfig{ 58 LoaderConfig: config.LoaderConfig{ 59 Repository: "https://charts.jetstack.io", 60 Chart: "cert-manager", 61 }, 62 }}, 63 -1, []string{"acme.cert-manager.io"}, 64 }, 65 { 66 "remote chart with version", 67 kptFnConfig{ChartConfig: &config.ChartConfig{ 68 LoaderConfig: config.LoaderConfig{ 69 Repository: "https://charts.jetstack.io", 70 Chart: "cert-manager", 71 Version: "0.9.x", 72 }, 73 }}, 74 34, []string{"chart: cainjector-v0.9.1"}, 75 }, 76 { 77 "release name", 78 kptFnConfig{ChartConfig: &config.ChartConfig{ 79 LoaderConfig: config.LoaderConfig{ 80 Chart: filepath.Join(exampleDir, "release-name"), 81 }, 82 RendererConfig: config.RendererConfig{ 83 Name: "myrelease", 84 }, 85 }}, 86 1, []string{"myrelease-config"}, 87 }, 88 { 89 "valueFiles", 90 kptFnConfig{ChartConfig: &config.ChartConfig{ 91 LoaderConfig: config.LoaderConfig{ 92 Chart: filepath.Join(exampleDir, "values-inheritance", "chart"), 93 }, 94 RendererConfig: config.RendererConfig{ 95 ValueFiles: []string{filepath.Join(exampleDir, "values-inheritance", "values.yaml")}, 96 }}}, 97 1, []string{" valueoverwrite: overwritten by file"}, 98 }, 99 { 100 "values", 101 kptFnConfig{ChartConfig: &config.ChartConfig{ 102 LoaderConfig: config.LoaderConfig{ 103 Chart: filepath.Join(exampleDir, "values-inheritance", "chart"), 104 }, 105 RendererConfig: config.RendererConfig{ 106 Values: map[string]interface{}{ 107 "example": map[string]string{"overrideValue": "explicitly"}, 108 }, 109 }}}, 110 1, []string{" valueoverwrite: explicitly"}, 111 }, 112 { 113 "values override", 114 kptFnConfig{ChartConfig: &config.ChartConfig{ 115 LoaderConfig: config.LoaderConfig{ 116 Chart: filepath.Join(exampleDir, "values-inheritance", "chart"), 117 }, 118 RendererConfig: config.RendererConfig{ 119 ValueFiles: []string{filepath.Join(exampleDir, "values-inheritance", "values.yaml")}, 120 Values: map[string]interface{}{ 121 "example": map[string]string{"overrideValue": "explicitly"}, 122 }, 123 }}}, 124 1, []string{" valueoverwrite: explicitly"}, 125 }, 126 { 127 "apiversions", 128 kptFnConfig{ChartConfig: &config.ChartConfig{ 129 LoaderConfig: config.LoaderConfig{ 130 Chart: filepath.Join(exampleDir, "apiversions-condition", "chart"), 131 }, 132 RendererConfig: config.RendererConfig{ 133 APIVersions: []string{"myfancyapi/v1", ""}, 134 }}}, 135 1, []string{"fancycr"}, 136 }, 137 { 138 "kubeversion", 139 kptFnConfig{ChartConfig: &config.ChartConfig{ 140 LoaderConfig: config.LoaderConfig{ 141 Chart: filepath.Join(exampleDir, "release-name"), 142 }, 143 RendererConfig: config.RendererConfig{ 144 KubeVersion: "1.12", 145 }}}, 146 1, []string{"k8sVersion: v1.12.0"}, 147 }, 148 { 149 "expand-list", 150 kptFnConfig{ChartConfig: &config.ChartConfig{ 151 LoaderConfig: config.LoaderConfig{ 152 Chart: filepath.Join(exampleDir, "expand-list"), 153 }, 154 }}, 155 3, []string{"\n name: myserviceaccount2\n"}, 156 }, 157 { 158 "namespace", 159 kptFnConfig{ChartConfig: &config.ChartConfig{ 160 LoaderConfig: config.LoaderConfig{ 161 Chart: filepath.Join(exampleDir, "namespace"), 162 }, 163 RendererConfig: config.RendererConfig{ 164 Namespace: "mynamespace", 165 }, 166 }}, 167 3, []string{" namespace: mynamespace\n"}, 168 }, 169 { 170 "force namespace", 171 kptFnConfig{ChartConfig: &config.ChartConfig{ 172 LoaderConfig: config.LoaderConfig{ 173 Chart: filepath.Join(exampleDir, "namespace"), 174 }, 175 RendererConfig: config.RendererConfig{ 176 ForceNamespace: "forced-namespace", 177 }, 178 }}, 179 3, []string{" namespace: forced-namespace\n"}, 180 }, 181 { 182 "exclude", 183 kptFnConfig{ChartConfig: &config.ChartConfig{ 184 LoaderConfig: config.LoaderConfig{ 185 Chart: filepath.Join(exampleDir, "namespace"), 186 }, 187 RendererConfig: config.RendererConfig{ 188 Exclude: []config.ResourceSelector{ 189 { 190 APIVersion: "v1", 191 Kind: "ConfigMap", 192 Name: "myconfiga", 193 }, 194 }, 195 }, 196 }}, 197 2, []string{"myconfigb"}, 198 }, 199 { 200 "include", 201 kptFnConfig{ChartConfig: &config.ChartConfig{ 202 LoaderConfig: config.LoaderConfig{ 203 Chart: filepath.Join(exampleDir, "namespace"), 204 }, 205 RendererConfig: config.RendererConfig{ 206 Include: []config.ResourceSelector{ 207 { 208 APIVersion: "v1", 209 Kind: "ConfigMap", 210 }, 211 }, 212 Exclude: []config.ResourceSelector{ 213 { 214 APIVersion: "v1", 215 Kind: "ConfigMap", 216 Name: "myconfiga", 217 }, 218 }, 219 }, 220 }}, 221 1, []string{"myconfigb"}, 222 }, 223 { 224 "annotate output path", 225 kptFnConfig{ 226 ChartConfig: &config.ChartConfig{ 227 LoaderConfig: config.LoaderConfig{ 228 Chart: filepath.Join(exampleDir, "namespace"), 229 }, 230 }, 231 OutputPath: "my/output/manifest.yaml", 232 }, 233 3, []string{" annotations:\n config.kubernetes.io/index: 1\n config.kubernetes.io/path: my/output/manifest.yaml\n"}, 234 }, 235 { 236 "annotate output path when annotations empty", 237 kptFnConfig{ 238 ChartConfig: &config.ChartConfig{ 239 LoaderConfig: config.LoaderConfig{ 240 Chart: filepath.Join(exampleDir, "empty-annotations"), 241 }, 242 }, 243 OutputPath: "my/output/path/", 244 }, 245 3, []string{ 246 "\n config.kubernetes.io/path: my/output/path/kustomization.yaml\n", 247 "\n config.kubernetes.io/path: my/output/path/serviceaccount_sa1.yaml\n", 248 "\n config.kubernetes.io/path: my/output/path/serviceaccount_sa2.yaml\n", 249 " myannotation: should-be-preserved\n", 250 }, 251 }, 252 { 253 "output kustomization", 254 kptFnConfig{ 255 ChartConfig: &config.ChartConfig{ 256 LoaderConfig: config.LoaderConfig{ 257 Chart: filepath.Join(exampleDir, "namespace"), 258 }, 259 }, 260 OutputPath: "my/output/path/", 261 }, 262 4, []string{"resources:\n- configmap_myconfiga.yaml\n- configmap_myconfigb.yaml\n"}, 263 }, 264 } { 265 t.Run(c.name, func(t *testing.T) { 266 c.input.Debug = true 267 if c.input.Name == "" { 268 c.input.Name = "release-name" 269 } 270 outPath := c.input.OutputPath 271 if outPath == "" { 272 if output.IsDirectory(outPath) { 273 outPath = path.Join(outPath, "previously-generated.yaml") 274 } else { 275 outPath = "generated-manifest.yaml" 276 } 277 } 278 inputAnnotations[annotationPath] = outPath 279 b, err := yaml.Marshal(map[string]interface{}{ 280 "apiVersion": "config.kubernetes.io/v1alpha1", 281 "kind": "ResourceList", 282 "items": inputItems, 283 "functionConfig": map[string]interface{}{"data": c.input}, 284 }) 285 require.NoError(t, err) 286 var out bytes.Buffer 287 os.Args = []string{"khelmfn"} 288 err = Execute(bytes.NewReader(b), &out) 289 require.NoError(t, err) 290 result := validateYAML(t, out.Bytes(), 1) 291 items, _ := result["items"].([]interface{}) 292 out.Reset() 293 enc := yaml.NewEncoder(&out) 294 for _, item := range items { 295 err = enc.Encode(item) 296 require.NoError(t, err) 297 } 298 if c.mustContainObj >= 0 { 299 // assert n+1 resources because one provided input resource should be preserved, 300 // the 2nd input resource should be excluded since it was generated by this function during a previous run. 301 if !assert.Equal(t, c.mustContainObj+1, len(items), "amount of resources within output") { 302 t.Log("\n" + out.String()) 303 } 304 } 305 for _, mustContain := range c.mustContain { 306 require.Contains(t, out.String(), mustContain, "output of %#v", c.input) 307 } 308 }) 309 } 310 }