diff --git a/.gitignore b/.gitignore index 92227d5..567fb67 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ +/_dist +/bin /kubecrt /vendor -/bin -/_dist diff --git a/parser/chart.go b/chart/parser.go similarity index 70% rename from parser/chart.go rename to chart/parser.go index 6b41b60..6327886 100644 --- a/parser/chart.go +++ b/chart/parser.go @@ -1,4 +1,4 @@ -package parser +package chart import ( "fmt" @@ -7,34 +7,39 @@ import ( "path/filepath" "strings" + "github.com/blendle/kubecrt/helm" + yaml "gopkg.in/yaml.v2" "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/downloader" "k8s.io/helm/pkg/engine" + "k8s.io/helm/pkg/getter" + "k8s.io/helm/pkg/helm/environment" "k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/timeconv" ) -// ParseCharts ... -func (cc *ChartsConfiguration) ParseCharts() ([]byte, error) { - var out []byte +// Chart ... +type Chart struct { + Version string `yaml:"version"` + Repo string `yaml:"repo"` + Values interface{} `yaml:"values"` + Location string +} + +// ParseChart ... +func (c *Chart) ParseChart(name, namespace string) ([]byte, error) { + s := strings.Split(c.Location, "/") - for _, c := range cc.Charts { - resources, err := c.ToResources(cc.Name, cc.Namespace) + if len(s) == 2 && c.Repo != "" { + err := helm.AddRepository(s[0], c.Repo) if err != nil { return nil, err } - - out = append(out, resources...) } - return out, nil -} - -// ToResources ... -func (c *Chart) ToResources(name, namespace string) ([]byte, error) { - d, err := yaml.Marshal(c.Config) + d, err := yaml.Marshal(c.Values) if err != nil { return nil, err } @@ -53,7 +58,7 @@ func (c *Chart) ToResources(name, namespace string) ([]byte, error) { return nil, err } - resources, err := chartToResources(c.Location, c.Version, name, namespace, tmpfile.Name()) + resources, err := c.compile(name, namespace, tmpfile.Name()) if err != nil { return nil, err } @@ -61,15 +66,15 @@ func (c *Chart) ToResources(name, namespace string) ([]byte, error) { return resources, nil } -func chartToResources(location, version, releaseName, namespace, values string) ([]byte, error) { +func (c *Chart) compile(releaseName, namespace, values string) ([]byte, error) { var output string - location, err := locateChartPath(location, version) + location, err := locateChartPath(c.Location, c.Version) if err != nil { return nil, err } - c, err := chartutil.Load(location) + cr, err := chartutil.Load(location) if err != nil { return nil, err } @@ -89,12 +94,12 @@ func chartToResources(location, version, releaseName, namespace, values string) renderer := engine.New() - vals, err := chartutil.ToRenderValues(c, config, options) + vals, err := chartutil.ToRenderValues(cr, config, options) if err != nil { return nil, err } - out, err := renderer.Render(c, vals) + out, err := renderer.Render(cr, vals) if err != nil { return nil, err } @@ -165,9 +170,16 @@ func locateChartPath(name, version string) (string, error) { return filepath.Abs(crepo) } + settings := environment.EnvSettings{ + Home: helmpath.Home(environment.DefaultHelmHome()), + } + + settings.PlugDirs = settings.Home.Plugins() + dl := downloader.ChartDownloader{ HelmHome: helmpath.Home(homepath), Out: os.Stdout, + Getters: getter.All(settings), } err := os.MkdirAll(filepath.Dir(crepo), 0755) @@ -175,6 +187,11 @@ func locateChartPath(name, version string) (string, error) { return "", fmt.Errorf("Failed to untar (mkdir): %s", err) } + version, err = helm.GetAcceptableVersion(name, version) + if err != nil { + return "", fmt.Errorf("Failed to find chart versions: %s", err) + } + filename, _, err := dl.DownloadTo(name, version, filepath.Dir(crepo)) if err != nil { return "", err diff --git a/chartsconfig/parser.go b/chartsconfig/parser.go new file mode 100644 index 0000000..5fc9bee --- /dev/null +++ b/chartsconfig/parser.go @@ -0,0 +1,104 @@ +package chartsconfig + +import ( + "errors" + + "github.com/Masterminds/semver" + "github.com/blendle/epp/epp" + "github.com/blendle/kubecrt/chart" + yaml "gopkg.in/yaml.v2" +) + +// ChartsConfiguration ... +type ChartsConfiguration struct { + APIVersion string `yaml:"apiVersion"` + Name string `yaml:"name"` + Namespace string `yaml:"namespace"` + ChartsMap []map[string]*chart.Chart `yaml:"charts"` + ChartsList []*chart.Chart +} + +// NewChartsConfiguration initialises a new ChartsConfiguration. +func NewChartsConfiguration(input []byte) (*ChartsConfiguration, error) { + m := &ChartsConfiguration{} + + out, err := parseEpp(input) + if err != nil { + return nil, err + } + + if err = yaml.Unmarshal(out, m); err != nil { + return nil, err + } + + for _, a := range m.ChartsMap { + for loc, c := range a { + c.Location = loc + m.ChartsList = append(m.ChartsList, c) + } + } + + return m, nil +} + +// ParseCharts loops through all charts, and returns the parsed resources. +func (cc *ChartsConfiguration) ParseCharts() ([]byte, error) { + var out []byte + + for _, c := range cc.ChartsList { + resources, err := c.ParseChart(cc.Name, cc.Namespace) + if err != nil { + return nil, err + } + + out = append(out, resources...) + } + + return out, nil +} + +// Validate makes sure the charts configuration is configured as expected. +func (cc *ChartsConfiguration) Validate() error { + if cc.APIVersion == "" { + return errors.New("Missing API version, please add \"apiVersion: v1\"") + } + + if cc.APIVersion != "v1" { + return errors.New("Unknown API version, please set apiVersion to \"v1\"") + } + + if cc.Name == "" { + return errors.New("Missing name, please add \"name: my-app-name\" or pass \"--name=my-app-name\"") + } + + if cc.Namespace == "" { + return errors.New("Missing namespace, please add \"namespace: my-namespace\" or pass \"--namespace=my-namespace\"") + } + + if len(cc.ChartsList) == 0 { + return errors.New("Missing charts, you need to define at least one chart") + } + + for _, c := range cc.ChartsList { + if c.Location == "" { + return errors.New("Invalid or missing chart name") + } + + if c.Version != "" { + if _, err := semver.NewConstraint(c.Version); err != nil { + return errors.New(c.Version + ": " + err.Error()) + } + } + } + + return nil +} + +func parseEpp(input []byte) ([]byte, error) { + out, err := epp.Parse(input) + if err != nil { + return nil, err + } + + return out, nil +} diff --git a/config/cli.go b/config/cli.go index 8f57b5a..4f2b672 100644 --- a/config/cli.go +++ b/config/cli.go @@ -94,11 +94,11 @@ charts: # repository, all other repositories require the "repo" configuration (see # below). - stable/factorio: - # config is a map of key/value pairs used when compiling the chart. This + # values is a map of key/value pairs used when compiling the chart. This # uses the same format as in regular chart "values.yaml" files. # # see: https://git.io/v9Tyr - config: + values: resources: requests: memory: 1024Mi @@ -108,14 +108,15 @@ charts: # using the "sprig" library. # # see: https://masterminds.github.io/sprig/ - name: {{ env "MY_SERVER_NAME" | default "hello world!" }} + name: > + {{ env "MY_SERVER_NAME" | default "hello world!" }} - stable/minecraft: # version is a semantic version constraint. # # see: https://github.com/Masterminds/semver#basic-comparisons version: ~> 0.1.0 - config: + values: minecraftServer: difficulty: hard @@ -123,7 +124,7 @@ charts: # repo is the location of a repositry, if other than "stable". This is # the URL you would normally add using "helm repo add NAME URL". repo: http://charts.opsgoodness.com - config: + values: sendAnalytics: false # For the above charts, see here for the default configurations: diff --git a/config/options.go b/config/options.go index 1e3e90c..ef44cd3 100644 --- a/config/options.go +++ b/config/options.go @@ -2,19 +2,20 @@ package config import "errors" -// CLIOptions ... +// CLIOptions contains all the options set through the CLI arguments type CLIOptions struct { - ChartsConfigPath string - ChartsConfigOptions *ChartsConfigOptions + ChartsConfigurationPath string + ChartsConfigurationOptions *ChartsConfigurationOptions } -// ChartsConfigOptions ... -type ChartsConfigOptions struct { +// ChartsConfigurationOptions contains the CLI options relevant for the charts +// configuration. +type ChartsConfigurationOptions struct { Name string Namespace string } -// NewCLIOptions ... +// NewCLIOptions takes CLI arguments, and returns a CLIOptions struct. func NewCLIOptions(cli map[string]interface{}) (*CLIOptions, error) { path, ok := cli["CHARTS_CONFIG"].(string) if !ok { @@ -25,8 +26,8 @@ func NewCLIOptions(cli map[string]interface{}) (*CLIOptions, error) { namespace, _ := cli["--namespace"].(string) c := &CLIOptions{ - ChartsConfigPath: path, - ChartsConfigOptions: &ChartsConfigOptions{ + ChartsConfigurationPath: path, + ChartsConfigurationOptions: &ChartsConfigurationOptions{ Name: name, Namespace: namespace, }, diff --git a/glide.lock b/glide.lock index 693babd..7962b95 100644 --- a/glide.lock +++ b/glide.lock @@ -1,22 +1,115 @@ -hash: 3ecc76a7b56e18b52eafd50fc6aa66ef35291b8b5f22ba9a7222f2e79813f442 -updated: 2017-04-24T00:56:51.039448011+02:00 +hash: 6ba5ec65469a702af408d736231297107f1cf8f3ac178ef7073d9450f0242d49 +updated: 2017-05-12T13:41:49.756814804+02:00 imports: +- name: cloud.google.com/go + version: 3b1ae45394a234c385be014e9a488f2bb6eef821 + subpackages: + - compute/metadata + - internal - name: github.com/aokoli/goutils version: 174f97f737d44ae9a2b687a1a2e2e6cc27c62783 -- name: github.com/blang/semver - version: 31b736133b98f26d5e078ec9eb591666edfd091f +- name: github.com/Azure/go-ansiterm + version: 70b2c90b260171e829f1ebd7c17f600c11858dbe + subpackages: + - winterm - name: github.com/blendle/epp version: 225bdbacaf52f2d42f2ceaefded96a8ddd08f8fe subpackages: - epp +- name: github.com/BurntSushi/toml + version: b26d9c308763d68093482582cea63d69be07a0f0 +- name: github.com/coreos/go-oidc + version: be73733bb8cc830d0205609b95d125215f8e9c70 + subpackages: + - http + - jose + - key + - oauth2 + - oidc +- name: github.com/coreos/pkg + version: fa29b1d70f0beaddd4c7021607cc3c3be8ce94b8 + subpackages: + - capnslog + - dlopen + - health + - httputil + - timeutil +- name: github.com/davecgh/go-spew + version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d + subpackages: + - spew +- name: github.com/dgrijalva/jwt-go + version: 01aeca54ebda6e0fbfafd0a524d234159c05ec20 +- name: github.com/docker/distribution + version: cd27f179f2c10c5d300e6d09025b538c475b0d51 + subpackages: + - digest + - reference +- name: github.com/docker/docker + version: b9f10c951893f9a00865890a5232e85d770c1087 + subpackages: + - pkg/jsonlog + - pkg/jsonmessage + - pkg/longpath + - pkg/mount + - pkg/stdcopy + - pkg/symlink + - pkg/system + - pkg/term + - pkg/term/windows +- name: github.com/docker/engine-api + version: dea108d3aa0c67d7162a3fd8aa65f38a430019fd + subpackages: + - client + - client/transport + - client/transport/cancellable + - types + - types/blkiodev + - types/container + - types/filters + - types/network + - types/reference + - types/registry + - types/strslice + - types/time + - types/versions +- name: github.com/docker/go-connections + version: f549a9393d05688dff0992ef3efd8bbe6c628aeb + subpackages: + - nat + - sockets + - tlsconfig +- name: github.com/docker/go-units + version: e30f1e79f3cd72542f2026ceec18d3bd67ab859c +- name: github.com/docker/spdystream + version: 449fdfce4d962303d702fec724ef0ad181c92528 + subpackages: + - spdy - name: github.com/docopt/docopt-go version: 784ddc588536785e7299f7272f39101f7faccc3f +- name: github.com/emicklei/go-restful + version: 09691a3b6378b740595c1002f40c34dd5f218a22 + subpackages: + - log + - swagger +- name: github.com/evanphx/json-patch + version: ba18e35c5c1b36ef6334cad706eb681153d2d379 +- name: github.com/exponent-io/jsonpath + version: d6023ce2651d8eafb5c75bb0c7167536102ec9f5 - name: github.com/facebookgo/symwalk version: 42004b9f322246749dd73ad71008b1f3160c0052 - name: github.com/flosch/pongo2 version: 1d0f0d3af150c4a65dfd424d742f7374819e7d29 - name: github.com/ghodss/yaml - version: 0ca9ea5df5451ffdf184b4428c902747c2c11cd7 + version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee +- name: github.com/go-openapi/jsonpointer + version: 46af16f9f7b149af66e5d1bd010e3574dc06de98 +- name: github.com/go-openapi/jsonreference + version: 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272 +- name: github.com/go-openapi/spec + version: 6aced65f8501fe1217321abf0749d354824ba2ff +- name: github.com/go-openapi/swag + version: 1d0bd113de87027671077d3c71eb3ac5d7dbba72 - name: github.com/gobwas/glob version: bea32b9cd2d6f55753d94a28e959b13f0244797a subpackages: @@ -27,35 +120,71 @@ imports: - syntax/lexer - util/runes - util/strings +- name: github.com/gogo/protobuf + version: c0656edd0d9eab7c66d1eb0c568f9039345796f7 + subpackages: + - proto + - sortkeys - name: github.com/golang/glog version: 44145f04b68cf362d9c4df2182967c2275eaefed +- name: github.com/golang/groupcache + version: 02826c3e79038b59d737d3b1c0a1d937f71a4433 + subpackages: + - lru - name: github.com/golang/protobuf - version: df1d3ca07d2d07bba352d5b73c4313b4e2a6203e + version: 2bba0603135d7d7f5cb73b2125beeda19c09f4ef subpackages: - proto - ptypes/any - ptypes/timestamp +- name: github.com/google/gofuzz + version: 44d81051d367757e1c7c6a5a86423ece9afcf63c +- name: github.com/howeyc/gopass + version: 3ca23474a7c7203e0a0a070fd33508f6efdb9b3d +- name: github.com/imdario/mergo + version: 6633656539c1639d9d78127b7d47c622b5d7b6dc +- name: github.com/inconshreveable/mousetrap + version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 +- name: github.com/jonboulle/clockwork + version: 72f9bd7c4e0c2a40055ab3d0f09654f730cce982 - name: github.com/juju/errors version: 6f54ff6318409d31ff16261533ce2c8381a4fd5d +- name: github.com/juju/ratelimit + version: 77ed1c8a01217656d2080ad51981f6e99adaa177 +- name: github.com/mailru/easyjson + version: d5b7844b561a7bc640052f1b935f7b800330d7e0 + subpackages: + - buffer + - jlexer + - jwriter - name: github.com/Masterminds/semver version: 3f0ab6d4ab4bed1c61caf056b63a6e62190c7801 - name: github.com/Masterminds/sprig - version: 23597e5f6ad0e4d590e71314bfd0251a4a3cf849 -- name: github.com/naoina/go-stringutil - version: 6b638e95a32d0c1131db0e7fe83775cbea4a0d0b -- name: github.com/naoina/toml - version: 751171607256bb66e64c9f0220c00662420c38e9 - subpackages: - - ast + version: a48f46e0125cf60347eda24fccf1b09f1a0d681f +- name: github.com/mitchellh/go-wordwrap + version: ad45545899c7b13c020ea92b2072220eefad42b8 +- name: github.com/pborman/uuid + version: ca53cad383cad2479bbba7f7a1a05797ec1386e4 +- name: github.com/PuerkitoBio/purell + version: 8a290539e2e8629dbc4e6bad948158f790ec31f4 +- name: github.com/PuerkitoBio/urlesc + version: 5bd2802263f21d8788851d5305584c82a5c75d7e - name: github.com/satori/go.uuid version: 879c5887cd475cd7864858769793b2ceb0d44feb +- name: github.com/Sirupsen/logrus + version: 51fe59aca108dc5680109e7b2051cbdcfa5a253c +- name: github.com/spf13/cobra + version: f62e98d28ab7ad31d707ba837a966378465c7b57 +- name: github.com/spf13/pflag + version: 9ff6c6923cfffbcd502984b8e0c80539a94968b7 +- name: github.com/ugorji/go + version: ded73eae5db7e7a0ef6f55aace87a2873c5d2b74 + subpackages: + - codec - name: golang.org/x/crypto - version: 1f22c0103821b9390939b6776727195525381532 + version: d172538b2cfce0c13cee31e647d0367aa8cd2486 subpackages: - - bcrypt - - blowfish - cast5 - - curve25519 - openpgp - openpgp/armor - openpgp/clearsign @@ -64,61 +193,379 @@ imports: - openpgp/packet - openpgp/s2k - pbkdf2 - - pkcs12 - - pkcs12/internal/rc2 - scrypt - - ssh - ssh/terminal +- name: golang.org/x/net + version: e90d6d0afc4c315a0d87a568ae68577cc15149a0 + subpackages: + - context + - context/ctxhttp + - http2 + - http2/hpack + - idna + - lex/httplex + - websocket +- name: golang.org/x/oauth2 + version: 3c3a985cb79f52a3190fbc056984415ca6763d01 + subpackages: + - google + - internal + - jws + - jwt +- name: golang.org/x/sys + version: 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9 + subpackages: + - unix +- name: golang.org/x/text + version: 2910a502d2bf9e43193af9d68ca516529614eed3 + subpackages: + - cases + - encoding + - encoding/internal + - encoding/internal/identifier + - encoding/unicode + - internal/tag + - internal/utf8internal + - language + - runes + - secure/bidirule + - secure/precis + - transform + - unicode/bidi + - unicode/norm + - width +- name: google.golang.org/appengine + version: 4f7eeb5305a4ba1966344836ba4af9996b7b4e05 + subpackages: + - internal + - internal/app_identity + - internal/base + - internal/datastore + - internal/log + - internal/modules + - internal/remote_api + - internal/urlfetch + - urlfetch +- name: gopkg.in/inf.v0 + version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 - name: gopkg.in/yaml.v2 version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b +- name: k8s.io/apimachinery + version: fbd6803372f831e58b86c78d07421637a64ad768 + subpackages: + - pkg/api/equality + - pkg/api/errors + - pkg/api/meta + - pkg/api/resource + - pkg/api/validation + - pkg/apimachinery + - pkg/apimachinery/announced + - pkg/apimachinery/registered + - pkg/apis/meta/v1 + - pkg/apis/meta/v1/unstructured + - pkg/apis/meta/v1/validation + - pkg/conversion + - pkg/conversion/queryparams + - pkg/fields + - pkg/labels + - pkg/openapi + - pkg/runtime + - pkg/runtime/schema + - pkg/runtime/serializer + - pkg/runtime/serializer/json + - pkg/runtime/serializer/protobuf + - pkg/runtime/serializer/recognizer + - pkg/runtime/serializer/streaming + - pkg/runtime/serializer/versioning + - pkg/selection + - pkg/types + - pkg/util/diff + - pkg/util/errors + - pkg/util/framer + - pkg/util/httpstream + - pkg/util/httpstream/spdy + - pkg/util/intstr + - pkg/util/json + - pkg/util/mergepatch + - pkg/util/net + - pkg/util/rand + - pkg/util/runtime + - pkg/util/sets + - pkg/util/strategicpatch + - pkg/util/uuid + - pkg/util/validation + - pkg/util/validation/field + - pkg/util/wait + - pkg/util/yaml + - pkg/version + - pkg/watch + - third_party/forked/golang/json + - third_party/forked/golang/netutil + - third_party/forked/golang/reflect +- name: k8s.io/apiserver + version: 2308857ad3b8b18abf74ff734853973eda9da94d + subpackages: + - pkg/authentication/authenticator + - pkg/authentication/serviceaccount + - pkg/authentication/user + - pkg/features + - pkg/server/httplog + - pkg/util/feature + - pkg/util/flag + - pkg/util/wsstream +- name: k8s.io/client-go + version: 5b0e11b577b35539f05523c47e94ed96a17f992b + subpackages: + - discovery + - dynamic + - kubernetes + - kubernetes/scheme + - kubernetes/typed/apps/v1beta1 + - kubernetes/typed/authentication/v1 + - kubernetes/typed/authentication/v1beta1 + - kubernetes/typed/authorization/v1 + - kubernetes/typed/authorization/v1beta1 + - kubernetes/typed/autoscaling/v1 + - kubernetes/typed/autoscaling/v2alpha1 + - kubernetes/typed/batch/v1 + - kubernetes/typed/batch/v2alpha1 + - kubernetes/typed/certificates/v1beta1 + - kubernetes/typed/core/v1 + - kubernetes/typed/extensions/v1beta1 + - kubernetes/typed/policy/v1beta1 + - kubernetes/typed/rbac/v1alpha1 + - kubernetes/typed/rbac/v1beta1 + - kubernetes/typed/settings/v1alpha1 + - kubernetes/typed/storage/v1 + - kubernetes/typed/storage/v1beta1 + - pkg/api + - pkg/api/helper + - pkg/api/install + - pkg/api/v1 + - pkg/apis/apps + - pkg/apis/apps/install + - pkg/apis/apps/v1beta1 + - pkg/apis/authentication + - pkg/apis/authentication/install + - pkg/apis/authentication/v1 + - pkg/apis/authentication/v1beta1 + - pkg/apis/authorization + - pkg/apis/authorization/install + - pkg/apis/authorization/v1 + - pkg/apis/authorization/v1beta1 + - pkg/apis/autoscaling + - pkg/apis/autoscaling/install + - pkg/apis/autoscaling/v1 + - pkg/apis/autoscaling/v2alpha1 + - pkg/apis/batch + - pkg/apis/batch/install + - pkg/apis/batch/v1 + - pkg/apis/batch/v2alpha1 + - pkg/apis/certificates + - pkg/apis/certificates/install + - pkg/apis/certificates/v1beta1 + - pkg/apis/extensions + - pkg/apis/extensions/install + - pkg/apis/extensions/v1beta1 + - pkg/apis/policy + - pkg/apis/policy/install + - pkg/apis/policy/v1beta1 + - pkg/apis/rbac + - pkg/apis/rbac/install + - pkg/apis/rbac/v1alpha1 + - pkg/apis/rbac/v1beta1 + - pkg/apis/settings + - pkg/apis/settings/install + - pkg/apis/settings/v1alpha1 + - pkg/apis/storage + - pkg/apis/storage/install + - pkg/apis/storage/v1 + - pkg/apis/storage/v1beta1 + - pkg/util + - pkg/util/parsers + - pkg/version + - plugin/pkg/client/auth + - plugin/pkg/client/auth/gcp + - plugin/pkg/client/auth/oidc + - rest + - rest/watch + - third_party/forked/golang/template + - tools/auth + - tools/cache + - tools/clientcmd + - tools/clientcmd/api + - tools/clientcmd/api/latest + - tools/clientcmd/api/v1 + - tools/metrics + - tools/portforward + - tools/record + - transport + - util/cert + - util/clock + - util/flowcontrol + - util/homedir + - util/integer + - util/jsonpath - name: k8s.io/helm - version: 32562a3040bb5ca690339b9840b6f60f8ce25da4 + version: 46d9ea82e2c925186e1fc620a8320ce1314cbb02 subpackages: - pkg/chartutil - pkg/downloader - pkg/engine + - pkg/getter - pkg/getter/defaultgetters + - pkg/helm/environment - pkg/helm/helmpath - pkg/ignore + - pkg/kube + - pkg/plugin - pkg/proto/hapi/chart + - pkg/proto/hapi/release - pkg/proto/hapi/version - pkg/provenance + - pkg/releaseutil - pkg/repo - pkg/resolver + - pkg/storage + - pkg/storage/driver + - pkg/tiller/environment - pkg/timeconv - pkg/tlsutil - pkg/urlutil - name: k8s.io/kubernetes - version: ea8f6637b639246faa14a8d5c6f864100fcb77a9 + version: 0480917b552be33e2dba47386e51decb1a211df6 subpackages: + - federation/apis/federation + - federation/apis/federation/install + - federation/apis/federation/v1beta1 + - federation/client/clientset_generated/federation_internalclientset + - federation/client/clientset_generated/federation_internalclientset/scheme + - federation/client/clientset_generated/federation_internalclientset/typed/autoscaling/internalversion + - federation/client/clientset_generated/federation_internalclientset/typed/batch/internalversion + - federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion + - federation/client/clientset_generated/federation_internalclientset/typed/extensions/internalversion + - federation/client/clientset_generated/federation_internalclientset/typed/federation/internalversion - pkg/api - - pkg/api/errors - - pkg/api/meta - - pkg/api/unversioned + - pkg/api/annotations + - pkg/api/events + - pkg/api/install + - pkg/api/pod + - pkg/api/service + - pkg/api/util - pkg/api/v1 + - pkg/api/validation + - pkg/apis/apps + - pkg/apis/apps/install - pkg/apis/apps/v1beta1 + - pkg/apis/authentication + - pkg/apis/authentication/install + - pkg/apis/authentication/v1 + - pkg/apis/authentication/v1beta1 + - pkg/apis/authorization + - pkg/apis/authorization/install + - pkg/apis/authorization/v1 + - pkg/apis/authorization/v1beta1 + - pkg/apis/autoscaling + - pkg/apis/autoscaling/install + - pkg/apis/autoscaling/v1 + - pkg/apis/autoscaling/v2alpha1 - pkg/apis/batch + - pkg/apis/batch/install - pkg/apis/batch/v1 + - pkg/apis/batch/v2alpha1 + - pkg/apis/certificates + - pkg/apis/certificates/install + - pkg/apis/certificates/v1beta1 + - pkg/apis/componentconfig + - pkg/apis/componentconfig/install + - pkg/apis/componentconfig/v1alpha1 - pkg/apis/extensions + - pkg/apis/extensions/install - pkg/apis/extensions/v1beta1 + - pkg/apis/policy + - pkg/apis/policy/install + - pkg/apis/policy/v1beta1 + - pkg/apis/rbac + - pkg/apis/rbac/install + - pkg/apis/rbac/v1alpha1 + - pkg/apis/rbac/v1beta1 + - pkg/apis/settings + - pkg/apis/settings/install + - pkg/apis/settings/v1alpha1 + - pkg/apis/storage + - pkg/apis/storage/install + - pkg/apis/storage/util + - pkg/apis/storage/v1 + - pkg/apis/storage/v1beta1 + - pkg/capabilities + - pkg/client/clientset_generated/clientset + - pkg/client/clientset_generated/clientset/scheme + - pkg/client/clientset_generated/clientset/typed/apps/v1beta1 + - pkg/client/clientset_generated/clientset/typed/authentication/v1 + - pkg/client/clientset_generated/clientset/typed/authentication/v1beta1 + - pkg/client/clientset_generated/clientset/typed/authorization/v1 + - pkg/client/clientset_generated/clientset/typed/authorization/v1beta1 + - pkg/client/clientset_generated/clientset/typed/autoscaling/v1 + - pkg/client/clientset_generated/clientset/typed/autoscaling/v2alpha1 + - pkg/client/clientset_generated/clientset/typed/batch/v1 + - pkg/client/clientset_generated/clientset/typed/batch/v2alpha1 + - pkg/client/clientset_generated/clientset/typed/certificates/v1beta1 + - pkg/client/clientset_generated/clientset/typed/core/v1 + - pkg/client/clientset_generated/clientset/typed/extensions/v1beta1 + - pkg/client/clientset_generated/clientset/typed/policy/v1beta1 + - pkg/client/clientset_generated/clientset/typed/rbac/v1alpha1 + - pkg/client/clientset_generated/clientset/typed/rbac/v1beta1 + - pkg/client/clientset_generated/clientset/typed/settings/v1alpha1 + - pkg/client/clientset_generated/clientset/typed/storage/v1 + - pkg/client/clientset_generated/clientset/typed/storage/v1beta1 - pkg/client/clientset_generated/internalclientset + - pkg/client/clientset_generated/internalclientset/scheme + - pkg/client/clientset_generated/internalclientset/typed/apps/internalversion + - pkg/client/clientset_generated/internalclientset/typed/authentication/internalversion + - pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion + - pkg/client/clientset_generated/internalclientset/typed/autoscaling/internalversion + - pkg/client/clientset_generated/internalclientset/typed/batch/internalversion + - pkg/client/clientset_generated/internalclientset/typed/certificates/internalversion - pkg/client/clientset_generated/internalclientset/typed/core/internalversion - pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion - - pkg/client/restclient - - pkg/client/typed/discovery + - pkg/client/clientset_generated/internalclientset/typed/policy/internalversion + - pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion + - pkg/client/clientset_generated/internalclientset/typed/settings/internalversion + - pkg/client/clientset_generated/internalclientset/typed/storage/internalversion + - pkg/client/listers/core/v1 + - pkg/client/listers/extensions/v1beta1 + - pkg/client/retry - pkg/client/unversioned - - pkg/client/unversioned/clientcmd - - pkg/client/unversioned/portforward - pkg/client/unversioned/remotecommand - - pkg/fields + - pkg/controller + - pkg/controller/deployment/util + - pkg/credentialprovider + - pkg/features + - pkg/fieldpath - pkg/kubectl - pkg/kubectl/cmd/util - pkg/kubectl/resource - - pkg/labels - - pkg/runtime - - pkg/util/intstr - - pkg/util/strategicpatch - - pkg/util/wait + - pkg/kubelet/qos + - pkg/kubelet/server/remotecommand + - pkg/kubelet/types + - pkg/master/ports + - pkg/printers + - pkg/printers/internalversion + - pkg/security/apparmor + - pkg/serviceaccount + - pkg/util + - pkg/util/exec + - pkg/util/hash + - pkg/util/interrupt + - pkg/util/labels + - pkg/util/net/sets + - pkg/util/node + - pkg/util/parsers + - pkg/util/slice + - pkg/util/term - pkg/version - - pkg/watch +- name: vbom.ml/util + version: db5cfe13f5cc80a4990d98e2e1b0707a4d1a5394 + subpackages: + - sortorder testImports: [] diff --git a/glide.yaml b/glide.yaml index 84dc819..8728438 100644 --- a/glide.yaml +++ b/glide.yaml @@ -7,8 +7,13 @@ import: - package: github.com/docopt/docopt-go version: ^0.6.2 - package: gopkg.in/yaml.v2 +- package: k8s.io/client-go +- package: k8s.io/apimachinery +- package: k8s.io/apiserver +- package: k8s.io/kubernetes + version: ~1.6.0 - package: k8s.io/helm - version: ^2.3.1 + version: ^2.4.1 subpackages: - pkg/chartutil - pkg/downloader @@ -16,3 +21,4 @@ import: - pkg/getter/defaultgetters - pkg/proto/hapi/chart - pkg/timeconv + - pkg/getter diff --git a/helm/helm.go b/helm/helm.go new file mode 100644 index 0000000..c7e3e49 --- /dev/null +++ b/helm/helm.go @@ -0,0 +1,13 @@ +package helm + +import ( + "k8s.io/helm/pkg/helm/environment" + "k8s.io/helm/pkg/helm/helmpath" +) + +var settings environment.EnvSettings + +func init() { + settings.Home = helmpath.Home(environment.DefaultHelmHome()) + settings.PlugDirs = settings.Home.Plugins() +} diff --git a/helm/init.go b/helm/init.go new file mode 100644 index 0000000..380fff4 --- /dev/null +++ b/helm/init.go @@ -0,0 +1,54 @@ +package helm + +import ( + "fmt" + "os" + + "k8s.io/helm/pkg/helm/helmpath" + "k8s.io/helm/pkg/repo" +) + +// Init makes sure the Helm home path exists and the required subfolders. +func Init() error { + if err := ensureDirectories(settings.Home); err != nil { + return err + } + + if err := ensureRepoFileFormat(settings.Home.RepositoryFile()); err != nil { + return err + } + + return nil +} + +func ensureDirectories(home helmpath.Home) error { + configDirectories := []string{ + home.String(), + home.Repository(), + home.Cache(), + home.Plugins(), + } + + for _, p := range configDirectories { + if fi, err := os.Stat(p); err != nil { + if err := os.MkdirAll(p, 0755); err != nil { + return fmt.Errorf("Could not create %s: %s", p, err) + } + } else if !fi.IsDir() { + return fmt.Errorf("%s must be a directory", p) + } + } + + return nil +} + +func ensureRepoFileFormat(file string) error { + r, err := repo.LoadRepositoriesFile(file) + if err == repo.ErrRepoOutOfDate { + if err := r.WriteFile(file, 0644); err != nil { + return err + } + } + + return nil +} diff --git a/helm/repo_add.go b/helm/repo_add.go new file mode 100644 index 0000000..09370c3 --- /dev/null +++ b/helm/repo_add.go @@ -0,0 +1,41 @@ +package helm + +import ( + "fmt" + + "k8s.io/helm/pkg/getter" + "k8s.io/helm/pkg/repo" +) + +// AddRepository adds a new repository to the Helm index. +func AddRepository(name, url string) error { + home := settings.Home + + f, err := repo.LoadRepositoriesFile(home.RepositoryFile()) + if err != nil { + return err + } + + cif := home.CacheIndex(name) + c := repo.Entry{ + Name: name, + Cache: cif, + URL: url, + CertFile: "", + KeyFile: "", + CAFile: "", + } + + r, err := repo.NewChartRepository(&c, getter.All(settings)) + if err != nil { + return err + } + + if err := r.DownloadIndexFile(home.Cache()); err != nil { + return fmt.Errorf("Looks like %q is not a valid chart repository or cannot be reached: %s", url, err.Error()) + } + + f.Update(&c) + + return f.WriteFile(home.RepositoryFile(), 0644) +} diff --git a/helm/version.go b/helm/version.go new file mode 100644 index 0000000..5d32997 --- /dev/null +++ b/helm/version.go @@ -0,0 +1,73 @@ +package helm + +import ( + "fmt" + + "github.com/Masterminds/semver" + + "k8s.io/helm/cmd/helm/search" + "k8s.io/helm/pkg/repo" +) + +// GetAcceptableVersion accepts a SemVer constraint, and finds the best matching +// chart version. +func GetAcceptableVersion(name, constraint string) (string, error) { + index, err := buildIndex() + if err != nil { + return "", err + } + + var res []*search.Result + res, err = index.Search(name, 10, false) + if err != nil { + return "", err + } + + search.SortScore(res) + + if constraint != "" { + res, err = applyConstraint(constraint, res) + if err != nil { + return "", err + } + } + + return res[0].Chart.Version, nil +} + +func buildIndex() (*search.Index, error) { + rf, err := repo.LoadRepositoriesFile(settings.Home.RepositoryFile()) + if err != nil { + return nil, err + } + + i := search.NewIndex() + for _, re := range rf.Repositories { + n := re.Name + f := settings.Home.CacheIndex(n) + ind, err := repo.LoadIndexFile(f) + if err != nil { + continue + } + + i.AddRepo(n, ind, true) + } + return i, nil +} + +func applyConstraint(version string, res []*search.Result) ([]*search.Result, error) { + constraint, err := semver.NewConstraint(version) + if err != nil { + return res, fmt.Errorf("an invalid version/constraint format: %s", err) + } + + data := res[:0] + for _, r := range res { + v, err := semver.NewVersion(r.Chart.Version) + if err != nil || constraint.Check(v) { + data = append(data, r) + } + } + + return data, nil +} diff --git a/main.go b/main.go index 7d8b3d1..0b5a0f3 100644 --- a/main.go +++ b/main.go @@ -5,30 +5,57 @@ import ( "io/ioutil" "os" + "github.com/blendle/kubecrt/chartsconfig" "github.com/blendle/kubecrt/config" - "github.com/blendle/kubecrt/parser" + "github.com/blendle/kubecrt/helm" ) func main() { cli := config.CLI() + opts, err := config.NewCLIOptions(cli) if err != nil { fmt.Fprintf(os.Stderr, "kubecrt arguments error: \n\n%s\n", err) os.Exit(1) } - cfg, err := readInput(opts.ChartsConfigPath) + cfg, err := readInput(opts.ChartsConfigurationPath) if err != nil { fmt.Fprintf(os.Stderr, "charts config IO error: \n\n%s\n", err) os.Exit(1) } - cc, err := parser.ParseConfig(cfg, opts.ChartsConfigOptions) + if err = helm.Init(); err != nil { + fmt.Fprintf(os.Stderr, "error initialising helm: \n\n%s\n", err) + os.Exit(1) + } + + if err = helm.AddRepository("stable", "https://kubernetes-charts.storage.googleapis.com"); err != nil { + fmt.Fprintf(os.Stderr, "error adding repository: \n\n%s\n", err) + os.Exit(1) + } + + cc, err := chartsconfig.NewChartsConfiguration(cfg) if err != nil { fmt.Fprintf(os.Stderr, "charts config parsing error: \n\n%s\n", err) os.Exit(1) } + name := opts.ChartsConfigurationOptions.Name + if name != "" { + cc.Name = name + } + + namespace := opts.ChartsConfigurationOptions.Namespace + if namespace != "" { + cc.Name = namespace + } + + if err = cc.Validate(); err != nil { + fmt.Fprintf(os.Stderr, "charts validation error: \n\n%s\n", err) + os.Exit(1) + } + out, err := cc.ParseCharts() if err != nil { fmt.Fprintf(os.Stderr, "chart parsing error: %s\n", err) @@ -40,8 +67,7 @@ func main() { return } - err = ioutil.WriteFile(cli["--output"].(string), out, 0644) - if err != nil { + if err = ioutil.WriteFile(cli["--output"].(string), out, 0644); err != nil { fmt.Fprintf(os.Stderr, "output IO error: %s\n", err) os.Exit(1) } diff --git a/parser/config.go b/parser/config.go deleted file mode 100644 index 910fcb5..0000000 --- a/parser/config.go +++ /dev/null @@ -1,142 +0,0 @@ -package parser - -import ( - "errors" - "strings" - - "github.com/Masterminds/semver" - "github.com/blendle/epp/epp" - "github.com/blendle/kubecrt/config" - yaml "gopkg.in/yaml.v2" -) - -// ChartsConfiguration ... -type ChartsConfiguration struct { - APIVersion string `yaml:"apiVersion"` - Charts []*Chart - Name string `yaml:"name"` - Namespace string `yaml:"namespace"` -} - -// Chart ... -type Chart struct { - Location string - Version string - Config interface{} -} - -// ParseConfig ... -func ParseConfig(input []byte, opts *config.ChartsConfigOptions) (*ChartsConfiguration, error) { - out, err := parseEpp(input) - if err != nil { - return nil, err - } - - m := make(map[string]interface{}) - - err = yaml.Unmarshal(out, m) - if err != nil { - return nil, err - } - - c := NewChartsConfiguration(m, opts) - - err = validateConfig(c) - if err != nil { - return nil, err - } - - return c, nil -} - -// NewChartsConfiguration ... -func NewChartsConfiguration(m map[string]interface{}, opts *config.ChartsConfigOptions) *ChartsConfiguration { - var apiVersion string - var charts map[interface{}]interface{} - - apiVersion, _ = m["apiVersion"].(string) - - namespace := opts.Namespace - if namespace == "" { - namespace, _ = m["namespace"].(string) - } - - name := opts.Name - if name == "" { - name, _ = m["name"].(string) - } - - cc := &ChartsConfiguration{ - APIVersion: apiVersion, - Name: name, - Namespace: namespace, - } - - charts, _ = m["charts"].(map[interface{}]interface{}) - for key, config := range charts { - var version, location string - - s, _ := key.(string) - p := strings.Split(s, ":") - location = p[0] - if len(p) > 1 { - version, p = p[len(p)-1], p[:len(p)-1] - location = strings.Join(p, ":") - } - - c := &Chart{ - Location: location, - Version: version, - Config: config, - } - - cc.Charts = append(cc.Charts, c) - } - - return cc -} - -func parseEpp(input []byte) ([]byte, error) { - out, err := epp.Parse(input) - if err != nil { - return nil, err - } - - return out, nil -} - -func validateConfig(cc *ChartsConfiguration) error { - if cc.APIVersion == "" { - return errors.New("Missing API version, please add \"apiVersion: v1\"") - } - - if cc.APIVersion != "v1" { - return errors.New("Unknown API version, please set apiVersion to \"v1\"") - } - - if cc.Name == "" { - return errors.New("Missing name, please add \"name: my-app-name\" or pass \"--name=my-app-name\"") - } - - if cc.Namespace == "" { - return errors.New("Missing namespace, please add \"namespace: my-namespace\" or pass \"--namespace=my-namespace\"") - } - - if len(cc.Charts) == 0 { - return errors.New("Missing charts, you need to define at least one chart") - } - - for _, c := range cc.Charts { - if c.Location == "" { - return errors.New("Invalid or missing chart name") - } - - if c.Version != "" { - if _, err := semver.NewVersion(c.Version); err != nil { - return errors.New(c.Version + ": " + err.Error()) - } - } - } - - return nil -} diff --git a/script/bootstrap b/script/bootstrap new file mode 100755 index 0000000..520a38d --- /dev/null +++ b/script/bootstrap @@ -0,0 +1,13 @@ +#!/bin/sh + +# script/bootstrap: bootstrap application for first usage + +set -e + +cd "$(dirname "$0")/.." + +glide install --force --strip-vendor --skip-test + +rm -rf ./vendor/k8s.io/{apiserver,apimachinery,client-go} + +cp -r ./vendor/k8s.io/kubernetes/staging/src/k8s.io/{apiserver,apimachinery,client-go} ./vendor/k8s.io