Skip to content

Commit

Permalink
Add plural cd services template
Browse files Browse the repository at this point in the history
This will make it a much smoother experience to test .liquid or .tpl files as they are being developed.  Should be functionally equivalent to the process done in-agent.
  • Loading branch information
michaeljguarino committed May 4, 2024
1 parent 97979db commit 6dc7f8f
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 1 deletion.
63 changes: 63 additions & 0 deletions cmd/plural/cd_services.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"strings"

gqlclient "github.com/pluralsh/console-client-go"
"github.com/pluralsh/plural-cli/pkg/cd/template"
"github.com/pluralsh/plural-cli/pkg/console"
"github.com/pluralsh/plural-cli/pkg/utils"
"github.com/pluralsh/polly/containers"
Expand Down Expand Up @@ -95,6 +96,24 @@ func (p *Plural) cdServiceCommands() []cli.Command {
},
Usage: "describe cluster service",
},
{
Name: "template",
Action: p.handleTemplateService,
Usage: "Dry-runs templating a .liquid or .tpl file with either a full service as params or custom config",
Flags: []cli.Flag{
cli.StringFlag{
Name: "service",
Usage: "specify the service you want to use as context while templating"},
cli.StringFlag{
Name: "configuration",
Usage: "hand-coded configuration for templating (useful if you want to test before creating a service)",
},
cli.StringFlag{
Name: "file",
Usage: "The .liquid or .tpl file you want to attempt to template.",
},
},
},
{
Name: "delete",
ArgsUsage: "SERVICE_ID",
Expand Down Expand Up @@ -213,6 +232,50 @@ func (p *Plural) handleCreateClusterService(c *cli.Context) error {
})
}

func (p *Plural) handleTemplateService(c *cli.Context) error {
if err := p.InitConsoleClient(consoleToken, consoleURL); err != nil {
return err
}

printResult := func(out []byte) error {
fmt.Println()
fmt.Println(string(out))
return nil
}

if identifier := c.String("service"); identifier != "" {
serviceId, clusterName, serviceName, err := getServiceIdClusterNameServiceName(identifier)
if err != nil {
return err
}

existing, err := p.ConsoleClient.GetClusterService(serviceId, serviceName, clusterName)
if err != nil {
return err
}
if existing == nil {
return fmt.Errorf("Service %s does not exist", identifier)
}

res, err := template.RenderService(c.String("file"), existing)
if err != nil {
return err
}
return printResult(res)
}

bindings := map[string]interface{}{}
if err := utils.YamlFile(c.String("configuration"), &bindings); err != nil {
return err
}

res, err := template.RenderYaml(c.String("file"), bindings)
if err != nil {
return err
}
return printResult(res)
}

func (p *Plural) handleCloneClusterService(c *cli.Context) error {
if err := p.InitConsoleClient(consoleToken, consoleURL); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ require (
github.com/packethost/packngo v0.29.0
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
github.com/pluralsh/cluster-api-migration v0.2.15
github.com/pluralsh/console-client-go v0.5.3
github.com/pluralsh/console-client-go v0.5.5
github.com/pluralsh/gqlclient v1.11.0
github.com/pluralsh/plural-operator v0.5.5
github.com/pluralsh/polly v0.1.7
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1428,6 +1428,8 @@ github.com/pluralsh/console-client-go v0.1.17 h1:QMtnWdRvV13/sND/CFjFBUR8nyg3JJg
github.com/pluralsh/console-client-go v0.1.17/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo=
github.com/pluralsh/console-client-go v0.5.3 h1:RB4XtKlvh8+BM5o1o0h+W6zHculBGbL6q5lI/yRnqJE=
github.com/pluralsh/console-client-go v0.5.3/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo=
github.com/pluralsh/console-client-go v0.5.5 h1:SvpI1bCQTUMA1VAdSHH5ESxr6r9xLxkbySlzxOTBO40=
github.com/pluralsh/console-client-go v0.5.5/go.mod h1:eyCiLA44YbXiYyJh8303jk5JdPkt9McgCo5kBjk4lKo=
github.com/pluralsh/controller-reconcile-helper v0.0.4 h1:1o+7qYSyoeqKFjx+WgQTxDz4Q2VMpzprJIIKShxqG0E=
github.com/pluralsh/controller-reconcile-helper v0.0.4/go.mod h1:AfY0gtteD6veBjmB6jiRx/aR4yevEf6K0M13/pGan/s=
github.com/pluralsh/gqlclient v1.11.0 h1:FfXW7FiEJLHOfTAa7NxDb8jb3aMZNIpCAcG+bg8uHYA=
Expand Down
64 changes: 64 additions & 0 deletions pkg/cd/template/liquid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package template

import (
"strings"

"github.com/Masterminds/sprig/v3"
"github.com/osteele/liquid"
console "github.com/pluralsh/console-client-go"
)

var (
extensions = []string{".json", ".yaml", ".yml", ".yaml.liquid", ".yml.liquid", ".json.liquid"}

Check failure on line 12 in pkg/cd/template/liquid.go

View workflow job for this annotation

GitHub Actions / Lint

var `extensions` is unused (unused)
liquidEngine = liquid.NewEngine()
sprigFunctions = map[string]string{
"toJson": "to_json",
"fromJson": "from_json",
"b64enc": "b64enc",
"b64dec": "b64dec",
"semverCompare": "semver_compare",
"sha256sum": "sha26sum",
"quote": "quote",
"squote": "squote",
"replace": "replace",
"coalesce": "coalesce",
}
)

func init() {
fncs := sprig.TxtFuncMap()
for key, name := range sprigFunctions {
liquidEngine.RegisterFilter(name, fncs[key])
}
liquidEngine.RegisterFilter("indent", indent)
liquidEngine.RegisterFilter("nindent", nindent)
liquidEngine.RegisterFilter("replace", strings.ReplaceAll)

liquidEngine.RegisterFilter("default", dfault)
liquidEngine.RegisterFilter("ternary", ternary)
}

func renderLiquid(input []byte, bindings map[string]interface{}) ([]byte, error) {
return liquidEngine.ParseAndRender(input, bindings)
}

func clusterConfiguration(cluster *console.BaseClusterFragment) map[string]interface{} {
res := map[string]interface{}{
"ID": cluster.ID,
"Self": cluster.Self,
"Handle": cluster.Handle,
"Name": cluster.Name,
"Version": cluster.Version,
"CurrentVersion": cluster.CurrentVersion,
"KasUrl": cluster.KasURL,
"Metadata": cluster.Metadata,
}

for k, v := range res {
res[strings.ToLower(k)] = v
}
res["kasUrl"] = cluster.KasURL
res["currentVersion"] = cluster.CurrentVersion

return res
}
30 changes: 30 additions & 0 deletions pkg/cd/template/render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package template

import (
"os"
"strings"

console "github.com/pluralsh/console-client-go"
)

func RenderYaml(path string, bindings map[string]interface{}) ([]byte, error) {
content, err := os.ReadFile(path)
if err != nil {
return nil, err
}

if strings.HasPrefix(path, ".tpl") {
return renderTpl(content, bindings)
}

return renderLiquid(content, bindings)
}

func RenderService(path string, svc *console.ServiceDeploymentExtended) ([]byte, error) {
bindings := map[string]interface{}{
"Configuration": configMap(svc),
"Cluster": clusterConfiguration(svc.Cluster),
"Contexts": contexts(svc),
}
return RenderYaml(path, bindings)
}
19 changes: 19 additions & 0 deletions pkg/cd/template/tpl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package template

import (
"bytes"
"text/template"

"github.com/Masterminds/sprig/v3"
)

func renderTpl(input []byte, bindings map[string]interface{}) ([]byte, error) {
tpl, err := template.New("gotpl").Funcs(sprig.TxtFuncMap()).Parse(string(input))
if err != nil {
return nil, err
}

var buffer bytes.Buffer
err = tpl.Execute(&buffer, bindings)
return buffer.Bytes(), err
}
77 changes: 77 additions & 0 deletions pkg/cd/template/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package template

import (
"reflect"
"strings"

console "github.com/pluralsh/console-client-go"
)

func configMap(svc *console.ServiceDeploymentExtended) map[string]string {
res := map[string]string{}
for _, config := range svc.Configuration {
res[config.Name] = config.Value
}

return res
}

func contexts(svc *console.ServiceDeploymentExtended) map[string]map[string]interface{} {
res := map[string]map[string]interface{}{}
for _, context := range svc.Contexts {
res[context.Name] = context.Configuration
}
return res
}

func indent(v string, spaces int) string {
pad := strings.Repeat(" ", spaces)
return pad + strings.ReplaceAll(v, "\n", "\n"+pad)
}

func nindent(v string, spaces int) string {
return "\n" + indent(v, spaces)
}

func ternary(v bool, vt interface{}, vf interface{}) interface{} {
if v {
return vt
}

return vf
}

func dfault(v1, v2 interface{}) interface{} {
if empty(v1) {
return v2
}

return v1
}

func empty(given interface{}) bool {
g := reflect.ValueOf(given)
if !g.IsValid() {
return true
}

// Basically adapted from text/template.isTrue
switch g.Kind() {
default:
return g.IsNil()
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
return g.Len() == 0
case reflect.Bool:
return !g.Bool()
case reflect.Complex64, reflect.Complex128:
return g.Complex() == 0
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return g.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return g.Uint() == 0
case reflect.Float32, reflect.Float64:
return g.Float() == 0
case reflect.Struct:
return false
}
}

0 comments on commit 6dc7f8f

Please sign in to comment.