| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- package parser
- import (
- "fmt"
- "github.com/porter-dev/porter/internal/helm"
- "github.com/porter-dev/porter/internal/models"
- "github.com/porter-dev/porter/internal/templater"
- "github.com/porter-dev/porter/internal/templater/utils"
- "helm.sh/helm/v3/pkg/chart"
- "helm.sh/helm/v3/pkg/release"
- "k8s.io/client-go/dynamic"
- "sigs.k8s.io/yaml"
- td "github.com/porter-dev/porter/internal/templater/dynamic"
- th "github.com/porter-dev/porter/internal/templater/helm"
- )
- // TODO -- handle all continue statements, errors should at least be logged if not
- // thrown
- type ClientConfigDefault struct {
- DynamicClient dynamic.Interface
- HelmAgent *helm.Agent
- HelmRelease *release.Release
- HelmChart *chart.Chart
- }
- func FormYAMLFromBytes(def *ClientConfigDefault, bytes []byte) (*models.FormYAML, error) {
- form, err := unqueriedFormYAMLFromBytes(bytes)
- if err != nil {
- return nil, err
- }
- lookup := formToLookupTable(def, form)
- // merge data from lookup
- data := make(map[string]interface{})
- for _, lookupVal := range lookup {
- queryRes, err := lookupVal.TemplateReader.Read()
- if err != nil {
- continue
- }
- for queryResKey, queryResVal := range queryRes {
- data[queryResKey] = queryResVal
- }
- }
- for i, tab := range form.Tabs {
- for j, section := range tab.Sections {
- for k, content := range section.Contents {
- key := fmt.Sprintf("tabs[%d].sections[%d].contents[%d]", i, j, k)
- if val, ok := data[key]; ok {
- content.Value = val
- }
- }
- }
- }
- return form, nil
- }
- // unqueriedFormYAMLFromBytes returns a FormYAML without values queries populated
- func unqueriedFormYAMLFromBytes(bytes []byte) (*models.FormYAML, error) {
- // parse bytes into object
- form := &models.FormYAML{}
- err := yaml.Unmarshal(bytes, form)
- if err != nil {
- return nil, err
- }
- // populate all context fields, with default set to helm/values with no config
- parent := &models.FormContext{
- Type: "helm/values",
- }
- for _, tab := range form.Tabs {
- if tab.Context == nil {
- tab.Context = parent
- }
- for _, section := range tab.Sections {
- if section.Context == nil {
- section.Context = tab.Context
- }
- for _, content := range section.Contents {
- if content.Context == nil {
- content.Context = section.Context
- }
- }
- }
- }
- return form, nil
- }
- type ContextConfig struct {
- FromType string // "live" or "declared"
- Capabilities []string // "read", "write"
- TemplateReader templater.TemplateReader
- TemplateWriter templater.TemplateWriter
- }
- // create map[*FormContext]*ContextConfig
- // assumes all contexts populated
- func formToLookupTable(def *ClientConfigDefault, form *models.FormYAML) map[*models.FormContext]*ContextConfig {
- lookup := make(map[*models.FormContext]*ContextConfig)
- for i, tab := range form.Tabs {
- for j, section := range tab.Sections {
- for k, content := range section.Contents {
- if content.Context == nil {
- continue
- }
- if _, ok := lookup[content.Context]; !ok {
- lookup[content.Context] = formContextToContextConfig(def, content.Context)
- }
- if content.Value != "" {
- // TODO -- case on whether value is proper query string, if not resolve it to a
- // proper query string
- query, err := utils.NewQuery(
- fmt.Sprintf("tabs[%d].sections[%d].contents[%d]", i, j, k),
- fmt.Sprintf("%v", content.Value),
- )
- if err != nil {
- continue
- }
- lookup[content.Context].TemplateReader.RegisterQuery(query)
- } else if content.Variable != "" {
- // if variable field set without value field set, make variable field into jsonpath
- // query
- query, err := utils.NewQuery(
- fmt.Sprintf("tabs[%d].sections[%d].contents[%d]", i, j, k),
- fmt.Sprintf("{ .%v }", content.Variable),
- )
- if err != nil {
- continue
- }
- lookup[content.Context].TemplateReader.RegisterQuery(query)
- }
- }
- }
- }
- return lookup
- }
- // TODO -- this needs to be able to construct new context configs based on
- // configuration for each context, but right now just uses the default config
- // for everything
- func formContextToContextConfig(def *ClientConfigDefault, context *models.FormContext) *ContextConfig {
- res := &ContextConfig{}
- switch context.Type {
- case "helm/values":
- res.FromType = "declared"
- res.Capabilities = []string{"read", "write"}
- res.TemplateReader = &th.ValuesTemplateReader{
- Release: def.HelmRelease,
- Chart: def.HelmChart,
- }
- relName := ""
- if def.HelmRelease != nil {
- relName = def.HelmRelease.Name
- }
- res.TemplateWriter = &th.ValuesTemplateWriter{
- Agent: def.HelmAgent,
- Chart: def.HelmChart,
- ReleaseName: relName,
- }
- case "helm/manifests":
- res.FromType = "live"
- res.Capabilities = []string{"read"}
- res.TemplateReader = &th.ManifestsTemplateReader{
- Release: def.HelmRelease,
- }
- case "cluster":
- res.FromType = "live"
- res.Capabilities = []string{"read"}
- // identify object based on passed config
- obj := &td.Object{
- Group: context.Config["Group"],
- Version: context.Config["Version"],
- Resource: context.Config["Resource"],
- Namespace: context.Config["Namespace"],
- Name: context.Config["Name"],
- }
- res.TemplateReader = td.NewDynamicTemplateReader(def.DynamicClient, obj)
- default:
- return nil
- }
- return res
- }
|