Skip to content

Commit

Permalink
[installer]: add config validation endpoint and run on render
Browse files Browse the repository at this point in the history
This includes some refactoring of the render command as the config validation and render commands share
some of the same principles.
  • Loading branch information
Simon Emms committed Oct 19, 2021
1 parent 7623c5f commit f43c35b
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 50 deletions.
40 changes: 26 additions & 14 deletions installer/cmd/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ import (
)

var renderOpts struct {
ConfigFN string
Namespace string
ConfigFN string
Namespace string
ValidateConfigDisabled bool
}

//go:embed versions.yaml
Expand All @@ -40,29 +41,24 @@ A config file is required which can be generated with the init command.`,
# Install Gitpod into a non-default namespace.
gitpod-installer render --config config.yaml --namespace gitpod | kubectl apply -f -`,
RunE: func(cmd *cobra.Command, args []string) error {
cfgFN, _ := cmd.PersistentFlags().GetString("config")

rawCfg, cfgVersion, err := config.Load(cfgFN)
if err != nil {
return fmt.Errorf("error loading config: %w", err)
}
if cfgVersion != config.CurrentVersion {
return fmt.Errorf("config version is mismatch: expected %s, got %s", config.CurrentVersion, cfgVersion)
}
cfg := rawCfg.(*configv1.Config)
_, cfgVersion, cfg, err := loadConfig(renderOpts.ConfigFN)

var versionMF versions.Manifest
err = yaml.Unmarshal(versionManifest, &versionMF)
if err != nil {
return err
}

namespace, _ := cmd.PersistentFlags().GetString("namespace")
if !renderOpts.ValidateConfigDisabled {
if err = runConfigValidation(cfgVersion, cfg); err != nil {
return err
}
}

ctx := &common.RenderContext{
Config: *cfg,
VersionManifest: versionMF,
Namespace: namespace,
Namespace: renderOpts.Namespace,
}

var renderable common.RenderFunc
Expand Down Expand Up @@ -108,9 +104,25 @@ A config file is required which can be generated with the init command.`,
},
}

func loadConfig(cfgFN string) (rawCfg interface{}, cfgVersion string, cfg *configv1.Config, err error) {
rawCfg, cfgVersion, err = config.Load(cfgFN)
if err != nil {
err = fmt.Errorf("error loading config: %w", err)
return
}
if cfgVersion != config.CurrentVersion {
err = fmt.Errorf("config version is mismatch: expected %s, got %s", config.CurrentVersion, cfgVersion)
return
}
cfg = rawCfg.(*configv1.Config)

return rawCfg, cfgVersion, cfg, err
}

func init() {
rootCmd.AddCommand(renderCmd)

renderCmd.PersistentFlags().StringVarP(&renderOpts.ConfigFN, "config", "c", os.Getenv("GITPOD_INSTALLER_CONFIG"), "path to the config file")
renderCmd.PersistentFlags().StringVarP(&renderOpts.Namespace, "namespace", "n", "default", "namespace to deploy to")
renderCmd.Flags().BoolVar(&renderOpts.ValidateConfigDisabled, "no-validation", false, "if set, the config will not be validated before running")
}
74 changes: 74 additions & 0 deletions installer/cmd/validate_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License-AGPL.txt in the project root for license information.

package cmd

import (
"fmt"
"github.com/gitpod-io/gitpod/installer/pkg/config"
"github.com/spf13/cobra"
"os"
"strings"
)

var validateConfigOpts struct {
Config string
}

// validateConfigCmd represents the cluster command
var validateConfigCmd = &cobra.Command{
Use: "config",
Short: "Validate the deployment configuration",
RunE: func(cmd *cobra.Command, args []string) error {
_, cfgVersion, cfg, err := loadConfig(validateConfigOpts.Config)

if err = runConfigValidation(cfgVersion, cfg); err != nil {
return err
}

fmt.Println("Gitpod installer configuration valid")

return nil
},
}

// runConfigValidation this will run the validation and print any validation errors
// It's silent if everything is fine
func runConfigValidation(version string, cfg interface{}) error {
apiVersion, err := config.LoadConfigVersion(version)
if err != nil {
return err
}

validationErrs, err := config.Validate(apiVersion, cfg)
if err != nil {
return err
}

if len(validationErrs) > 0 {
for _, v := range validationErrs {
switch v.Tag() {
case "required":
fmt.Println(fmt.Sprintf("Field '%s' is required", v.Namespace()))
case "required_if", "required_unless", "required_with":
tag := strings.Replace(v.Tag(), "_", " ", -1)
fmt.Println(fmt.Sprintf("Field '%s' is %s '%s'", v.Namespace(), tag, v.Param()))
case "startswith":
fmt.Println(fmt.Sprintf("Field '%s' must start with '%s'", v.Namespace(), v.Param()))
default:
// General error message
fmt.Println(fmt.Sprintf("Field '%s' failed %s validation", v.Namespace(), v.Tag()))
}
}
return fmt.Errorf("configuration invalid")
}

return nil
}

func init() {
validateCmd.AddCommand(validateConfigCmd)

validateCmd.PersistentFlags().StringVarP(&validateConfigOpts.Config, "config", "c", os.Getenv("GITPOD_INSTALLER_CONFIG"), "path to the config file")
}
8 changes: 3 additions & 5 deletions installer/example-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@ apiVersion: v1
blockNewUsers:
enabled: false
certificate:
kind: ""
name: "https-certificates"
kind: secret
name: https-certificates
containerRegistry:
inCluster: true
database:
inCluster: true
domain: gitpod.example.com
imagePullSecrets: null
jaegerOperator:
InCluster: true
inCluster: true
kind: Full
messageQueue:
InCluster: true
metadata:
region: ""
objectStorage:
Expand Down
4 changes: 4 additions & 0 deletions installer/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/gitpod-io/gitpod/ws-manager/api v0.0.0-00010101000000-000000000000
github.com/gitpod-io/gitpod/ws-proxy v0.0.0-00010101000000-000000000000
github.com/gitpod-io/gitpod/ws-scheduler v0.0.0-00010101000000-000000000000
github.com/go-playground/validator/v10 v10.9.0
github.com/google/go-cmp v0.5.6
github.com/jetstack/cert-manager v1.4.4
github.com/spf13/cobra v1.2.1
Expand Down Expand Up @@ -93,6 +94,8 @@ require (
github.com/go-openapi/jsonreference v0.19.5 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/godbus/dbus/v5 v5.0.4 // indirect
github.com/gogo/googleapis v1.4.0 // indirect
Expand Down Expand Up @@ -123,6 +126,7 @@ require (
github.com/klauspost/cpuid v1.3.1 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/lib/pq v1.10.3 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.7.6 // indirect
Expand Down
18 changes: 17 additions & 1 deletion installer/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,14 @@ github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/
github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE=
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPgjj221I1QHci2A=
github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
Expand Down Expand Up @@ -749,8 +757,9 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
Expand All @@ -760,6 +769,8 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
Expand Down Expand Up @@ -965,6 +976,7 @@ github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoU
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down Expand Up @@ -1025,6 +1037,9 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rubenv/sql-migrate v0.0.0-20210614095031-55d5740dbbcc h1:BD7uZqkN8CpjJtN/tScAKiccBikU4dlqe/gNrkRaPY4=
Expand Down Expand Up @@ -1467,6 +1482,7 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
Expand Down
19 changes: 16 additions & 3 deletions installer/pkg/config/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package config

import (
"fmt"
"github.com/go-playground/validator/v10"
"io/ioutil"

"sigs.k8s.io/yaml"
Expand Down Expand Up @@ -38,6 +39,9 @@ type ConfigVersion interface {
// Defaults fills in the defaults for this version.
// obj is expected to be the return value of Factory()
Defaults(obj interface{}) error

// LoadValidationFuncs loads the custom validation functions
LoadValidationFuncs(*validator.Validate) error
}

// AddVersion adds a new version.
Expand All @@ -55,6 +59,15 @@ var (

var versions map[string]ConfigVersion

func LoadConfigVersion(version string) (ConfigVersion, error) {
v, ok := versions[version]
if !ok {
return nil, fmt.Errorf("unsupprted API version: %s", version)
}

return v, nil
}

func Load(fn string) (cfg interface{}, version string, err error) {
fc, err := ioutil.ReadFile(fn)
if err != nil {
Expand All @@ -68,11 +81,11 @@ func Load(fn string) (cfg interface{}, version string, err error) {
return
}

v, ok := versions[vs.APIVersion]
if !ok {
err = fmt.Errorf("unsupprted API version: %s", vs.APIVersion)
v, err := LoadConfigVersion(vs.APIVersion)
if err != nil {
return
}

cfg = v.Factory()
version = vs.APIVersion
err = yaml.Unmarshal(fc, cfg)
Expand Down
Loading

0 comments on commit f43c35b

Please sign in to comment.