| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- package stacks
- import (
- "fmt"
- "github.com/porter-dev/porter/api/server/shared/config"
- "github.com/porter-dev/porter/api/types"
- "github.com/porter-dev/porter/internal/helm/loader"
- "github.com/porter-dev/porter/internal/templater/utils"
- "github.com/stefanmcshane/helm/pkg/chart"
- "gopkg.in/yaml.v2"
- )
- type PorterStackYAML struct {
- Version *string `yaml:"version"`
- Build *Build `yaml:"build"`
- Env map[string]string `yaml:"env"`
- Apps map[string]*App `yaml:"apps"`
- Release *string `yaml:"release"`
- }
- type Build struct {
- Context *string `yaml:"context" validate:"dir"`
- Method *string `yaml:"method" validate:"required,oneof=pack docker registry"`
- Builder *string `yaml:"builder" validate:"required_if=Method pack"`
- Buildpacks []*string `yaml:"buildpacks"`
- Dockerfile *string `yaml:"dockerfile" validate:"required_if=Method docker"`
- Image *string `yaml:"image" validate:"required_if=Method registry"`
- }
- type App struct {
- Run *string `yaml:"run" validate:"required"`
- Config map[string]interface{} `yaml:"config"`
- Type *string `yaml:"type" validate:"required, oneof=web worker job"`
- }
- func parse(porterYaml []byte, imageInfo *types.ImageInfo, config *config.Config, projectID uint) (*chart.Chart, map[string]interface{}, error) {
- parsed := &PorterStackYAML{}
- err := yaml.Unmarshal(porterYaml, parsed)
- if err != nil {
- return nil, nil, fmt.Errorf("%s: %w", "error parsing porter.yaml", err)
- }
- values, err := buildStackValues(parsed, imageInfo)
- if err != nil {
- return nil, nil, fmt.Errorf("%s: %w", "error building values from porter.yaml", err)
- }
- convertedValues := convertMap(values)
- chart, err := buildStackChart(parsed, config, projectID)
- if err != nil {
- return nil, nil, fmt.Errorf("%s: %w", "error building chart from porter.yaml", err)
- }
- return chart, convertedValues.(map[string]interface{}), nil
- }
- func buildStackValues(parsed *PorterStackYAML, imageInfo *types.ImageInfo) (map[string]interface{}, error) {
- values := make(map[string]interface{})
- for name, app := range parsed.Apps {
- defaultValues := getDefaultValues(app, parsed.Env, imageInfo)
- helm_values := utils.CoalesceValues(defaultValues, app.Config)
- values[name] = helm_values
- }
- return values, nil
- }
- func getDefaultValues(app *App, env map[string]string, imageInfo *types.ImageInfo) map[string]interface{} {
- var defaultValues map[string]interface{}
- if *app.Type == "web" {
- defaultValues = map[string]interface{}{
- "ingress": map[string]interface{}{
- "enabled": false,
- },
- "container": map[string]interface{}{
- "command": *app.Run,
- "env": map[string]interface{}{
- "normal": CopyEnv(env),
- },
- },
- }
- } else {
- defaultValues = map[string]interface{}{
- "container": map[string]interface{}{
- "command": *app.Run,
- "env": map[string]interface{}{
- "normal": CopyEnv(env),
- },
- },
- }
- }
- if imageInfo != nil {
- defaultValues["image"] = map[string]interface{}{
- "repository": imageInfo.Repository,
- "tag": imageInfo.Tag,
- }
- }
- return defaultValues
- }
- func buildStackChart(parsed *PorterStackYAML, config *config.Config, projectID uint) (*chart.Chart, error) {
- deps := make([]*chart.Dependency, 0)
- for alias, app := range parsed.Apps {
- selectedRepo := "https://charts.getporter.dev"
- selectedVersion, err := getLatestTemplateVersion(*app.Type, config, projectID)
- if err != nil {
- return nil, err
- }
- deps = append(deps, &chart.Dependency{
- Name: *app.Type,
- Alias: alias,
- Version: selectedVersion,
- Repository: selectedRepo,
- })
- }
- chart, err := createChartFromDependencies(deps)
- if err != nil {
- return nil, err
- }
- return chart, nil
- }
- func createChartFromDependencies(deps []*chart.Dependency) (*chart.Chart, error) {
- metadata := &chart.Metadata{
- Name: "umbrella",
- Description: "Web application that is exposed to external traffic.",
- Version: "0.96.0",
- APIVersion: "v2",
- Home: "https://getporter.dev/",
- Icon: "https://user-images.githubusercontent.com/65516095/111255214-07d3da80-85ed-11eb-99e2-fddcbdb99bdb.png",
- Keywords: []string{
- "porter",
- "application",
- "service",
- "umbrella",
- },
- Type: "application",
- Dependencies: deps,
- }
- // create a new chart object with the metadata
- c := &chart.Chart{
- Metadata: metadata,
- }
- return c, nil
- }
- func getLatestTemplateVersion(templateName string, config *config.Config, projectID uint) (string, error) {
- repoIndex, err := loader.LoadRepoIndexPublic(config.ServerConf.DefaultApplicationHelmRepoURL)
- if err != nil {
- return "", fmt.Errorf("%s: %w", "unable to load porter chart repo", err)
- }
- templates := loader.RepoIndexToPorterChartList(repoIndex, config.ServerConf.DefaultApplicationHelmRepoURL)
- if err != nil {
- return "", fmt.Errorf("%s: %w", "unable to load porter chart list", err)
- }
- var version string
- // find the matching template name
- for _, template := range templates {
- if templateName == template.Name {
- version = template.Versions[0]
- break
- }
- }
- if version == "" {
- return "", fmt.Errorf("matching template version not found")
- }
- return version, nil
- }
- func convertMap(m interface{}) interface{} {
- switch m := m.(type) {
- case map[string]interface{}:
- for k, v := range m {
- m[k] = convertMap(v)
- }
- case map[interface{}]interface{}:
- result := map[string]interface{}{}
- for k, v := range m {
- result[k.(string)] = convertMap(v)
- }
- return result
- case []interface{}:
- for i, v := range m {
- m[i] = convertMap(v)
- }
- }
- return m
- }
- func CopyEnv(env map[string]string) map[string]string {
- envCopy := make(map[string]string)
- if env == nil {
- return envCopy
- }
- for k, v := range env {
- if k == "" || v == "" {
- continue
- }
- envCopy[k] = v
- }
- return envCopy
- }
|