From 5391b93468df5386b796484259db8e3647d1c343 Mon Sep 17 00:00:00 2001 From: Andrew Edstrom Date: Wed, 15 Jan 2020 16:22:09 -0800 Subject: [PATCH] Provide uaa.yml via ConfigMap in k8s rather than having it be on the classpath of the compiled artifact. [#169718758] Signed-off-by: Andrew Edstrom Signed-off-by: Andrew Wittrock --- k8s/Makefile | 1 + k8s/go.mod | 1 + k8s/templates/config.yml | 10 ++++ k8s/templates/deployment.yml | 13 +++-- k8s/templates/uaa.lib.yml | 69 +++++++++++++++++++++++++++ k8s/templates/values/values.yml | 5 ++ k8s/test/config_map_matcher_test.go | 50 +++++++++++++++++++ k8s/test/config_map_test.go | 63 ++++++++++++++++++++++++ k8s/test/deployment_matcher_test.go | 2 +- k8s/test/k8s_suite_test.go | 2 +- k8s/test/produce_yaml_matcher_test.go | 4 +- k8s/test/uaa_config_structs_test.go | 45 +++++++++++++++++ k8s/test/uaa_yml_matcher_test.go | 53 ++++++++++++++++++++ 13 files changed, 311 insertions(+), 7 deletions(-) create mode 100644 k8s/templates/config.yml create mode 100644 k8s/templates/uaa.lib.yml create mode 100644 k8s/test/config_map_matcher_test.go create mode 100644 k8s/test/config_map_test.go create mode 100644 k8s/test/uaa_config_structs_test.go create mode 100644 k8s/test/uaa_yml_matcher_test.go diff --git a/k8s/Makefile b/k8s/Makefile index 34cbaded903..aa48c9dd858 100644 --- a/k8s/Makefile +++ b/k8s/Makefile @@ -12,5 +12,6 @@ format: test: go test -count=1 ./... +.PHONY: render render: @ytt -f templates diff --git a/k8s/go.mod b/k8s/go.mod index 39656603b09..47df9cc8383 100644 --- a/k8s/go.mod +++ b/k8s/go.mod @@ -5,6 +5,7 @@ go 1.13 require ( github.com/onsi/ginkgo v1.11.0 github.com/onsi/gomega v1.8.1 + gopkg.in/yaml.v2 v2.2.4 k8s.io/api v0.17.1 k8s.io/client-go v11.0.0+incompatible ) diff --git a/k8s/templates/config.yml b/k8s/templates/config.yml new file mode 100644 index 00000000000..2129e975528 --- /dev/null +++ b/k8s/templates/config.yml @@ -0,0 +1,10 @@ +#@ load("@ytt:yaml", "yaml") +#@ load("uaa.lib.yml", "config") +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: uaa-config +data: + uaa.yml: #@ yaml.encode(config()) + diff --git a/k8s/templates/deployment.yml b/k8s/templates/deployment.yml index 47cdefdcbb2..a5d8dffee18 100644 --- a/k8s/templates/deployment.yml +++ b/k8s/templates/deployment.yml @@ -22,7 +22,14 @@ spec: - containerPort: 8080 protocol: TCP env: - - name: LOGIN_CONFIG_URL - value: "classpath:required_configuration.yml" - name: spring_profiles - value: "default,hsqldb" \ No newline at end of file + value: "default,hsqldb" + - name: UAA_CONFIG_PATH + value: /etc/config + volumeMounts: + - name: uaa-config + mountPath: /etc/config + volumes: + - name: uaa-config + configMap: + name: uaa-config \ No newline at end of file diff --git a/k8s/templates/uaa.lib.yml b/k8s/templates/uaa.lib.yml new file mode 100644 index 00000000000..73bdfd87dae --- /dev/null +++ b/k8s/templates/uaa.lib.yml @@ -0,0 +1,69 @@ +#@ load("@ytt:data", "data") +#@ def config(): +--- +issuer: + uri: http://localhost:8080/uaa + +encryption: + active_key_label: CHANGE-THIS-KEY + encryption_keys: + - label: CHANGE-THIS-KEY + passphrase: CHANGEME + +login: + serviceProviderKey: | + -----BEGIN RSA PRIVATE KEY----- + MIICXQIBAAKBgQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5 + L39WqS9u0hnA+O7MCA/KlrAR4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vA + fpOwznoD66DDCnQVpbCjtDYWX+x6imxn8HCYxhMol6ZnTbSsFW6VZjFMjQIDAQAB + AoGAVOj2Yvuigi6wJD99AO2fgF64sYCm/BKkX3dFEw0vxTPIh58kiRP554Xt5ges + 7ZCqL9QpqrChUikO4kJ+nB8Uq2AvaZHbpCEUmbip06IlgdA440o0r0CPo1mgNxGu + lhiWRN43Lruzfh9qKPhleg2dvyFGQxy5Gk6KW/t8IS4x4r0CQQD/dceBA+Ndj3Xp + ubHfxqNz4GTOxndc/AXAowPGpge2zpgIc7f50t8OHhG6XhsfJ0wyQEEvodDhZPYX + kKBnXNHzAkEAyCA76vAwuxqAd3MObhiebniAU3SnPf2u4fdL1EOm92dyFs1JxyyL + gu/DsjPjx6tRtn4YAalxCzmAMXFSb1qHfwJBAM3qx3z0gGKbUEWtPHcP7BNsrnWK + vw6By7VC8bk/ffpaP2yYspS66Le9fzbFwoDzMVVUO/dELVZyBnhqSRHoXQcCQQCe + A2WL8S5o7Vn19rC0GVgu3ZJlUrwiZEVLQdlrticFPXaFrn3Md82ICww3jmURaKHS + N+l4lnMda79eSp3OMmq9AkA0p79BvYsLshUJJnvbk76pCjR28PK4dV1gSDUEqQMB + qy45ptdwJLqLJCeNoR0JUcDNIRhOCuOPND7pcMtX6hI/ + -----END RSA PRIVATE KEY----- + serviceProviderKeyPassword: password + serviceProviderCertificate: | + -----BEGIN CERTIFICATE----- + MIIDSTCCArKgAwIBAgIBADANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJhdzEO + MAwGA1UECBMFYXJ1YmExDjAMBgNVBAoTBWFydWJhMQ4wDAYDVQQHEwVhcnViYTEO + MAwGA1UECxMFYXJ1YmExDjAMBgNVBAMTBWFydWJhMR0wGwYJKoZIhvcNAQkBFg5h + cnViYUBhcnViYS5hcjAeFw0xNTExMjAyMjI2MjdaFw0xNjExMTkyMjI2MjdaMHwx + CzAJBgNVBAYTAmF3MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAM + BgNVBAcTBWFydWJhMQ4wDAYDVQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAb + BgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyMIGfMA0GCSqGSIb3DQEBAQUAA4GN + ADCBiQKBgQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5L39W + qS9u0hnA+O7MCA/KlrAR4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vAfpOw + znoD66DDCnQVpbCjtDYWX+x6imxn8HCYxhMol6ZnTbSsFW6VZjFMjQIDAQABo4Ha + MIHXMB0GA1UdDgQWBBTx0lDzjH/iOBnOSQaSEWQLx1syGDCBpwYDVR0jBIGfMIGc + gBTx0lDzjH/iOBnOSQaSEWQLx1syGKGBgKR+MHwxCzAJBgNVBAYTAmF3MQ4wDAYD + VQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYD + VQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAbBgkqhkiG9w0BCQEWDmFydWJh + QGFydWJhLmFyggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAYvBJ + 0HOZbbHClXmGUjGs+GS+xC1FO/am2suCSYqNB9dyMXfOWiJ1+TLJk+o/YZt8vuxC + KdcZYgl4l/L6PxJ982SRhc83ZW2dkAZI4M0/Ud3oePe84k8jm3A7EvH5wi5hvCkK + RpuRBwn3Ei+jCRouxTbzKPsuCVB+1sNyxMTXzf0= + -----END CERTIFICATE----- + +#! The secret that an external login server will use to authenticate to the uaa using the id `login` +LOGIN_SECRET: loginsecret + +jwt: + token: + signing-key: tokenKey + +database: + maxactive: 100 + maxidle: 10 + minidle: 0 + removeabandoned: false + logabandoned: true + abandonedtimeout: 300 + username: #@ data.values.database.username + password: #@ data.values.database.password +#@ end diff --git a/k8s/templates/values/values.yml b/k8s/templates/values/values.yml index e69de29bb2d..9de67cb5eaa 100644 --- a/k8s/templates/values/values.yml +++ b/k8s/templates/values/values.yml @@ -0,0 +1,5 @@ +#@data/values +--- +database: + username: uaa + password: password diff --git a/k8s/test/config_map_matcher_test.go b/k8s/test/config_map_matcher_test.go new file mode 100644 index 00000000000..8ef75eb34a5 --- /dev/null +++ b/k8s/test/config_map_matcher_test.go @@ -0,0 +1,50 @@ +package k8s_test + +import ( + "fmt" + "github.com/onsi/gomega/format" + "github.com/onsi/gomega/types" + v1 "k8s.io/api/core/v1" +) + +type DataMatcherConfig func(*UaaYmlMatcher) + +type ConfigMapMatcher struct { + dataMatcher *UaaYmlMatcher + + executed types.GomegaMatcher +} + + +func RepresentingConfigMap() *ConfigMapMatcher { + return &ConfigMapMatcher{NewUaaYmlMatcher(), nil} +} + +func (matcher *ConfigMapMatcher) WithDataMatching(config DataMatcherConfig) *ConfigMapMatcher { + config(matcher.dataMatcher) + + return matcher +} + +func (matcher *ConfigMapMatcher) Match(actual interface{}) (success bool, err error) { + configMap, ok := actual.(*v1.ConfigMap) + if !ok { + return false, fmt.Errorf("Expected a ConfigMap. Got\n%s", format.Object(actual, 1)) + } + + matcher.executed = matcher.dataMatcher + pass, err := matcher.dataMatcher.Match(configMap.Data) + if !pass || err != nil { + return pass, err + } + + return true, nil +} + +func (matcher *ConfigMapMatcher) FailureMessage(actual interface{}) (message string) { + return matcher.executed.FailureMessage(actual) +} + +func (matcher *ConfigMapMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return matcher.executed.NegatedFailureMessage(actual) +} diff --git a/k8s/test/config_map_test.go b/k8s/test/config_map_test.go new file mode 100644 index 00000000000..0e1975f308d --- /dev/null +++ b/k8s/test/config_map_test.go @@ -0,0 +1,63 @@ +package k8s_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gstruct" + + "path/filepath" +) + +var _ = Describe("Uaa ConfigMap", func() { + var configPath, uaaLibPath, valuesPath string + var database Database + + BeforeEach(func() { + configPath = pathToTemplate("config.yml") + uaaLibPath = pathToTemplate("uaa.lib.yml") + valuesPath = pathToTemplate(filepath.Join("values", "values.yml")) + database = Database{Username: "uaa", Password: "password"} + }) + + It("Renders a config map with default values", func() { + ctx := NewRenderingContext(configPath, uaaLibPath, valuesPath) + + Expect(ctx).To( + ProduceYAML( + RepresentingConfigMap().WithDataMatching(func(uaaYml *UaaYmlMatcher) { + uaaYml.WithFields(Fields{ + "LoginSecret": Equal("loginsecret"), + "Issuer": Equal(Issuer{Uri: "http://localhost:8080/uaa"}), + "Database": MatchFields(IgnoreExtras, Fields{ + "Username": Equal(database.Username), + "Password": Equal(database.Password), + }), + }) + }), + )) + }) + + It("Can renders a config map with overriden values", func() { + database.Username = "database-username" + database.Password = "database-password" + + ctx := NewRenderingContext(configPath, uaaLibPath, valuesPath).WithData(map[string]string{ + "database.username": database.Username, + "database.password": database.Password, + }) + + Expect(ctx).To( + ProduceYAML( + RepresentingConfigMap().WithDataMatching(func(uaaYml *UaaYmlMatcher) { + uaaYml.WithFields(Fields{ + "LoginSecret": Equal("loginsecret"), + "Issuer": Equal(Issuer{Uri: "http://localhost:8080/uaa"}), + "Database": MatchFields(IgnoreExtras, Fields{ + "Username": Equal(database.Username), + "Password": Equal(database.Password), + }), + }) + }), + )) + }) +}) diff --git a/k8s/test/deployment_matcher_test.go b/k8s/test/deployment_matcher_test.go index e2675ca74f7..68c55951e36 100644 --- a/k8s/test/deployment_matcher_test.go +++ b/k8s/test/deployment_matcher_test.go @@ -31,7 +31,7 @@ func (matcher *DeploymentMatcher) Match(actual interface{}) (bool, error) { return false, fmt.Errorf("Expected a deployment. Got\n%s", format.Object(actual, 1)) } - matcher.executed = matcher.pod + matcher.executed = matcher.pod // so we can have good pod-specific failure messages if pass, err := matcher.pod.Match(deployment.Spec.Template); !pass || err != nil { return pass, err } diff --git a/k8s/test/k8s_suite_test.go b/k8s/test/k8s_suite_test.go index abe0d6f42a9..1482136c1f0 100644 --- a/k8s/test/k8s_suite_test.go +++ b/k8s/test/k8s_suite_test.go @@ -15,7 +15,7 @@ var templateBasePath string func init() { _, filename, _, ok := runtime.Caller(0) if !ok { - panic("Could not initialize k8s_test package: can't find location of this file") + panic("Could not initiaize k8s_test package: can't find location of this file") } relative := filepath.Join(filepath.Dir(filename), "..", "templates") diff --git a/k8s/test/produce_yaml_matcher_test.go b/k8s/test/produce_yaml_matcher_test.go index 79d31fccfd8..f1b3ba88034 100644 --- a/k8s/test/produce_yaml_matcher_test.go +++ b/k8s/test/produce_yaml_matcher_test.go @@ -56,7 +56,7 @@ func (matcher *ProduceYAMLMatcher) Match(actual interface{}) (bool, error) { func (matcher *ProduceYAMLMatcher) FailureMessage(actual interface{}) string { msg := fmt.Sprintf( - "There is a problem with this YAML:\n\n%s\n\n%s", + "FailureMessage: There is a problem with this YAML:\n\n%s\n\n%s", matcher.rendered, matcher.matcher.FailureMessage(actual), ) @@ -65,7 +65,7 @@ func (matcher *ProduceYAMLMatcher) FailureMessage(actual interface{}) string { func (matcher *ProduceYAMLMatcher) NegatedFailureMessage(actual interface{}) string { msg := fmt.Sprintf( - "There is a problem with this YAML:\n\n%s\n\n%s", + "NegatedFailureMessage: There is a problem with this YAML:\n\n%s\n\n%s", matcher.rendered, matcher.matcher.NegatedFailureMessage(actual), ) diff --git a/k8s/test/uaa_config_structs_test.go b/k8s/test/uaa_config_structs_test.go new file mode 100644 index 00000000000..1b3b3997739 --- /dev/null +++ b/k8s/test/uaa_config_structs_test.go @@ -0,0 +1,45 @@ +package k8s_test + +type UaaConfig struct { + Issuer Issuer `yaml:"issuer"` + Encryption Encryption `yaml:"encryption"` + Login Login `yaml:"login"` + LoginSecret string `yaml:"LOGIN_SECRET"` + Jwt Jwt `yaml:"jwt"` + Database Database `yaml:"database"` +} + +type Issuer struct { + Uri string `yaml:"uri"` +} + +type Encryption struct { + ActiveKeyLabel string `yaml:"active_key_label"` + EncryptionKeys []struct { + Label string `yaml:"label"` + Passphrase string `yaml:"passphrase"` + } `yaml:"encryption_keys"` +} + +type Jwt struct { + Token struct { + SigningKey string `yaml:"signing-key"` + } `yaml:"token"` +} + +type Login struct { + ServiceProviderKey string `yaml:"serviceProviderKey"` + ServiceProviderKeyPassword string `yaml:"serviceProviderKeyPassword"` + ServiceProviderCertificate string `yaml:"serviceProviderKeyCertificate"` +} + +type Database struct { + MaxActive int `yaml:"maxactive"` + MaxIdle int `yaml:"maxidle"` + MinIdle int `yaml:"minidle"` + RemoveAbandoned bool `yaml:"removeabandoned"` + LogAbandoned bool `yaml:"logabandoned"` + AbandonedTimeout int `yaml:"abandonedtimeout"` + Username string `yaml:"username"` + Password string `yaml:"password"` +} diff --git a/k8s/test/uaa_yml_matcher_test.go b/k8s/test/uaa_yml_matcher_test.go new file mode 100644 index 00000000000..2149300f4b4 --- /dev/null +++ b/k8s/test/uaa_yml_matcher_test.go @@ -0,0 +1,53 @@ +package k8s_test + +import ( + "github.com/onsi/gomega/gstruct" + "github.com/onsi/gomega/types" + + "gopkg.in/yaml.v2" +) + +const UaaYmlConfigKey = "uaa.yml" + +type UaaYmlMatcher struct { + fields map[string]types.GomegaMatcher + configFields gstruct.Fields + executed types.GomegaMatcher +} + +func NewUaaYmlMatcher() *UaaYmlMatcher { + return &UaaYmlMatcher{ + fields: map[string]types.GomegaMatcher{}, + configFields: gstruct.Fields{}, + executed: nil, + } +} + +func (matcher *UaaYmlMatcher) WithFields(fields gstruct.Fields) *UaaYmlMatcher { + matcher.configFields = fields + return matcher +} + +func (matcher *UaaYmlMatcher) Match(actual interface{}) (success bool, err error) { + configMapData, ok := actual.(map[string]string) + if !ok { + panic("expected a map[string]string") + } + + uaaYml := UaaConfig{} + err = yaml.Unmarshal([]byte(configMapData[UaaYmlConfigKey]), &uaaYml) + if err != nil { + panic("Failed to unmarshal") + } + + matcher.executed = gstruct.MatchFields(gstruct.IgnoreExtras, matcher.configFields) + return matcher.executed.Match(uaaYml) +} + +func (matcher *UaaYmlMatcher) FailureMessage(actual interface{}) (message string) { + return matcher.executed.FailureMessage(actual) +} + +func (matcher *UaaYmlMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return matcher.executed.NegatedFailureMessage(actual) +}