Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ require (
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c
github.com/olekukonko/tablewriter v1.1.4
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/pluralsh/console/go/client v1.76.4
github.com/pluralsh/console/go/client v1.76.5
github.com/pluralsh/console/go/polly v1.0.0
github.com/pluralsh/gqlclient v1.12.2
github.com/pluralsh/plural-operator v0.6.0
Expand Down Expand Up @@ -349,5 +349,3 @@ require (
sigs.k8s.io/kustomize/api v0.21.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.21.1 // indirect
)

replace github.com/pluralsh/console/go/client => github.com/pluralsh/console/go/client v0.0.0-20260706120905-5f6ff115eb71
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -608,8 +608,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pluralsh/console/go/client v0.0.0-20260706120905-5f6ff115eb71 h1:ZDQvTGLJEAqLmiZTffaLNp3Yc4GHRYrpxbKMppz+Z0s=
github.com/pluralsh/console/go/client v0.0.0-20260706120905-5f6ff115eb71/go.mod h1:+dJuFu2ruNb8u4Aaor8NPneOiK2ndrwGBsbsFxbQfDU=
github.com/pluralsh/console/go/client v1.76.5 h1:0DjfQTphB3pDLXVdK4Ty9/EsJvrm8kC12pqB9unvePM=
github.com/pluralsh/console/go/client v1.76.5/go.mod h1:+dJuFu2ruNb8u4Aaor8NPneOiK2ndrwGBsbsFxbQfDU=
github.com/pluralsh/console/go/controller v0.0.0-20260706120905-5f6ff115eb71 h1:d00SB5O8he1ls7YmUpI10J9FiqXKU+k8EgI0gKqRdSo=
github.com/pluralsh/console/go/controller v0.0.0-20260706120905-5f6ff115eb71/go.mod h1:7eM3lngWsj4OUrQsuN2ZTT5UgV7c5yi+PZF8Cer3K+M=
github.com/pluralsh/console/go/polly v1.0.0 h1:NRg8CMITGllEJ08oYidNuuG/gJGDdDuga8TM+gdIcFA=
Expand Down
32 changes: 25 additions & 7 deletions pkg/client/plural.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os/exec"
"strings"

consoleclient "github.com/pluralsh/console/go/client"
"github.com/pluralsh/console/go/polly/algorithms"
"github.com/samber/lo"
apierrors "k8s.io/apimachinery/pkg/api/errors"
Expand All @@ -15,18 +16,15 @@ import (

"github.com/urfave/cli"

"github.com/pluralsh/plural-cli/pkg/common"
"github.com/pluralsh/plural-cli/pkg/scm"
"github.com/pluralsh/plural-cli/pkg/wkspace"

"sigs.k8s.io/yaml"

"github.com/pluralsh/plural-cli/pkg/api"
"github.com/pluralsh/plural-cli/pkg/common"
"github.com/pluralsh/plural-cli/pkg/config"
"github.com/pluralsh/plural-cli/pkg/console"
"github.com/pluralsh/plural-cli/pkg/kubernetes"
"github.com/pluralsh/plural-cli/pkg/manifest"
"github.com/pluralsh/plural-cli/pkg/scm"
"github.com/pluralsh/plural-cli/pkg/utils"
"github.com/pluralsh/plural-cli/pkg/wkspace"
)

type Plural struct {
Expand Down Expand Up @@ -243,7 +241,13 @@ func (p *Plural) DoInstallOperator(url, token, values, chart_loc, clusterId stri
utils.Highlight("Using Agent v%s\n", version)

if settings.AgentHelmValues != nil {
if err := yaml.Unmarshal([]byte(*settings.AgentHelmValues), &globalVals); err != nil {
cluster, err := p.fetchClusterForAgentHelmValues(settings, clusterId)
if err != nil {
return fmt.Errorf("fetching cluster for agent helm values templating: %w", err)
}

globalVals, err = console.ResolveAgentHelmValues(settings, cluster)
if err != nil {
return err
}
} else {
Expand Down Expand Up @@ -285,6 +289,20 @@ func (p *Plural) ReinstallOperator(c *cli.Context, id, handle *string, chart_loc
return p.DoInstallOperator(url, deployToken, c.String("values"), chart_loc, clusterId)
}

func (p *Plural) fetchClusterForAgentHelmValues(
settings *consoleclient.DeploymentSettingsFragment,
clusterId string,
) (*consoleclient.ClusterFragment, error) {
if settings == nil || !lo.FromPtr(settings.AgentHelmValuesTemplateable) {
return nil, nil
}
if clusterId == "" {
return nil, fmt.Errorf("cluster id is required to render agent helm values")
}

return p.ConsoleClient.GetCluster(lo.ToPtr(clusterId), nil)
}

func displayWarning(err error) {
var outMsg string
var ee *exec.ExitError
Expand Down
89 changes: 89 additions & 0 deletions pkg/console/agent_helm_values.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package console

import (
"fmt"
"strings"

gqlclient "github.com/pluralsh/console/go/client"
"github.com/pluralsh/console/go/polly/template"
"github.com/samber/lo"
"sigs.k8s.io/yaml"
)

func ResolveAgentHelmValues(
settings *gqlclient.DeploymentSettingsFragment,
cluster *gqlclient.ClusterFragment,
) (map[string]interface{}, error) {
if settings == nil || settings.AgentHelmValues == nil {
return map[string]interface{}{}, nil
}

raw := *settings.AgentHelmValues
rendered := raw
if lo.FromPtr(settings.AgentHelmValuesTemplateable) {
Comment thread
michaeljguarino marked this conversation as resolved.
if cluster == nil {
return nil, fmt.Errorf("cluster context is required to render agent helm values")
}
bindings := agentHelmValuesBindings(cluster)
out, err := template.RenderLiquid([]byte(raw), bindings)
if err != nil {
return nil, fmt.Errorf("rendering agent helm values: %w", err)
}
rendered = string(out)
}

globalVals := map[string]interface{}{}
if err := yaml.Unmarshal([]byte(rendered), &globalVals); err != nil {
return nil, err
}

return globalVals, nil
}

func agentHelmValuesBindings(cluster *gqlclient.ClusterFragment) map[string]interface{} {
return map[string]interface{}{
"cluster": clusterBindings(cluster),
}
}

func clusterBindings(cluster *gqlclient.ClusterFragment) map[string]interface{} {
if cluster == nil {
return 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,
"Distro": cluster.Distro,
"Tags": clusterTagsMap(cluster.Tags),
}

lowercase := make(map[string]interface{}, len(res))
for k, v := range res {
lowercase[strings.ToLower(k)] = v
}
for k, v := range lowercase {
res[k] = v
}
res["kasUrl"] = cluster.KasURL
res["currentVersion"] = cluster.CurrentVersion
Comment thread
zreigz marked this conversation as resolved.

return res
}

func clusterTagsMap(tags []*gqlclient.ClusterTags) map[string]string {
res := map[string]string{}
for _, tag := range tags {
if tag == nil {
continue
}
res[tag.Name] = tag.Value
}
return res
}
97 changes: 97 additions & 0 deletions pkg/console/agent_helm_values_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package console_test

import (
"testing"

gqlclient "github.com/pluralsh/console/go/client"
"github.com/pluralsh/plural-cli/pkg/console"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestResolveAgentHelmValuesWithoutTemplating(t *testing.T) {
t.Parallel()

settings := &gqlclient.DeploymentSettingsFragment{
AgentHelmValues: lo.ToPtr("foo: bar\n"),
}

vals, err := console.ResolveAgentHelmValues(settings, nil)
require.NoError(t, err)
assert.Equal(t, "bar", vals["foo"])
}

func TestResolveAgentHelmValuesWithTemplating(t *testing.T) {
t.Parallel()

settings := &gqlclient.DeploymentSettingsFragment{
AgentHelmValues: lo.ToPtr("cluster:\n name: {{ cluster.name }}\n"),
AgentHelmValuesTemplateable: lo.ToPtr(true),
}
cluster := &gqlclient.ClusterFragment{
ID: "cluster-id",
Name: "test",
}

vals, err := console.ResolveAgentHelmValues(settings, cluster)
require.NoError(t, err)

clusterVals, ok := vals["cluster"].(map[string]interface{})
require.True(t, ok)
assert.Equal(t, "test", clusterVals["name"])
}

func TestResolveAgentHelmValuesRequiresClusterWhenTemplating(t *testing.T) {
t.Parallel()

settings := &gqlclient.DeploymentSettingsFragment{
AgentHelmValues: lo.ToPtr("cluster:\n id: {{ cluster.id }}\n"),
AgentHelmValuesTemplateable: lo.ToPtr(true),
}

_, err := console.ResolveAgentHelmValues(settings, nil)
require.Error(t, err)
assert.Contains(t, err.Error(), "cluster context is required")
}

func TestResolveAgentHelmValuesRendersClusterMetadataAndTags(t *testing.T) {
t.Parallel()

settings := &gqlclient.DeploymentSettingsFragment{
AgentHelmValues: lo.ToPtr(`name: {{ cluster.name }}
region: {{ cluster.metadata.region }}
env: {{ cluster.tags.env }}
id: {{ cluster.id }}
kas: {{ cluster.kasUrl }}
`),
AgentHelmValuesTemplateable: lo.ToPtr(true),
}
cluster := &gqlclient.ClusterFragment{
ID: "cluster-1",
Name: "prod-cluster",
KasURL: lo.ToPtr("https://kas.example.com"),
Metadata: map[string]any{
"region": "eu-central-1",
},
Tags: []*gqlclient.ClusterTags{
{Name: "env", Value: "production"},
},
}

vals, err := console.ResolveAgentHelmValues(settings, cluster)
require.NoError(t, err)
assert.Equal(t, "prod-cluster", vals["name"])
assert.Equal(t, "eu-central-1", vals["region"])
assert.Equal(t, "production", vals["env"])
assert.Equal(t, "cluster-1", vals["id"])
assert.Equal(t, "https://kas.example.com", vals["kas"])
}

func TestResolveAgentHelmValuesNilSettings(t *testing.T) {
t.Parallel()

vals, err := console.ResolveAgentHelmValues(nil, nil)
require.NoError(t, err)
assert.Empty(t, vals)
}
1 change: 1 addition & 0 deletions pkg/console/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type ConsoleClient interface {
CreateNotificationSinks(attr consoleclient.NotificationSinkAttributes) (*consoleclient.NotificationSinkFragment, error)
UpdateDeploymentSettings(attr consoleclient.DeploymentSettingsAttributes) (*consoleclient.UpdateDeploymentSettings, error)
GetGlobalSettings() (*consoleclient.DeploymentSettingsFragment, error)
GetGlobalSettingsMinimal() (*consoleclient.DeploymentSettingsFragment, error)
GetAgentRun(id string) (*consoleclient.AgentRunMinimalFragment, error)
ListAgentRuns(first int64) ([]*consoleclient.AgentRunMinimalFragment, error)
ListStackRuns(stackID string) (*consoleclient.ListStackRuns, error)
Expand Down
28 changes: 25 additions & 3 deletions pkg/console/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,32 @@ func (c *consoleClient) UpdateDeploymentSettings(attr gqlclient.DeploymentSettin
func (c *consoleClient) GetGlobalSettings() (*gqlclient.DeploymentSettingsFragment, error) {
resp, err := c.client.GetDeploymentSettings(c.ctx)
if err != nil {
return nil, api.GetErrorResponse(err, "GetDeploymentSettings")
return c.GetGlobalSettingsMinimal()
}
if resp == nil {
return nil, fmt.Errorf("returned GetDeploymentSettings object is nil")
if resp == nil || resp.DeploymentSettings == nil {
return c.GetGlobalSettingsMinimal()
}
return resp.DeploymentSettings, nil
}

func (c *consoleClient) GetGlobalSettingsMinimal() (*gqlclient.DeploymentSettingsFragment, error) {
resp, err := c.client.GetDeploymentSettingsMinimal(c.ctx)
if err != nil {
return nil, api.GetErrorResponse(err, "GetDeploymentSettingsMinimal")
}
if resp == nil {
return nil, fmt.Errorf("returned GetDeploymentSettingsMinimal object is nil")
}
return toDeploymentSettingsFragment(resp.DeploymentSettings), nil
}

func toDeploymentSettingsFragment(minimal *gqlclient.DeploymentSettingsMinimalFragment) *gqlclient.DeploymentSettingsFragment {
if minimal == nil {
return nil
}

return &gqlclient.DeploymentSettingsFragment{
AgentHelmValues: minimal.AgentHelmValues,
AgentVsn: minimal.AgentVsn,
}
}
30 changes: 30 additions & 0 deletions pkg/test/mocks/ConsoleClient.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions pkg/test/mocks/Kube.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading