Skip to content

Commit 1cf2dc3

Browse files
authored
feat: Add --service option to plural cd services lua (#714)
* add service identifier parsing and retrieval functions * replace service identifier functions with a unified parsing approach * update service context handling to populate configuration map * rfactor Lua template handling and add bindings for service context * update Go modules to latest versions * refactor Lua template execution and enhance binding handling * add unit tests for Lua template execution and binding handling * refactor Lua template tests to use 'any' type for value maps * add unit tests for Lua bindings with service identifier and context path * add service flag values for Lua service configuration * update golangci-lint to version 9.2.0 and set latest version for linting * optimize slice initialization for better performance * update Go version from 1.25.8 to 1.26.1 in Dockerfile
1 parent 92d7afb commit 1cf2dc3

17 files changed

Lines changed: 902 additions & 292 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ jobs:
3131
with:
3232
go-version-file: go.mod
3333
- name: golangci-lint
34-
uses: golangci/golangci-lint-action@v8.0.0
34+
uses: golangci/golangci-lint-action@v9.2.0
3535
with:
36-
version: v2.4.0
36+
version: latest
3737
skip-cache: true
3838

3939
contract-validation:

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ FROM ubuntu:22.10 AS user
33
# Create a nonroot user for final image
44
RUN useradd -u 10001 nonroot
55

6-
FROM golang:1.25.8-alpine3.22 AS builder
6+
FROM golang:1.26.1-alpine3.22 AS builder
77

88
WORKDIR /workspace
99

@@ -31,7 +31,7 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} \
3131
-X \"github.com/pluralsh/plural-cli/pkg/common.Date=${APP_DATE}\"" \
3232
-o plural ./cmd/plural
3333

34-
FROM golang:1.25.8-alpine3.22 AS final
34+
FROM golang:1.26.1-alpine3.22 AS final
3535

3636
WORKDIR /
3737

cmd/command/cd/cd_services.go

Lines changed: 49 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,14 @@ package cd
22

33
import (
44
"fmt"
5-
iofs "io/fs"
6-
"os"
75
"path/filepath"
8-
"sort"
96
"strings"
107

118
"github.com/pluralsh/console/go/polly/fs"
129
"github.com/pluralsh/plural-cli/pkg/common"
13-
lua "github.com/yuin/gopher-lua"
1410

1511
gqlclient "github.com/pluralsh/console/go/client"
1612
"github.com/pluralsh/console/go/polly/containers"
17-
"github.com/pluralsh/console/go/polly/luautils"
1813
"github.com/pluralsh/plural-cli/pkg/cd/template"
1914
"github.com/pluralsh/plural-cli/pkg/console"
2015
"github.com/pluralsh/plural-cli/pkg/utils"
@@ -139,6 +134,10 @@ func (p *Plural) cdServiceCommands() []cli.Command {
139134
Name: "context",
140135
Usage: "A yaml context file to imitate the internal service template context",
141136
},
137+
cli.StringFlag{
138+
Name: "service",
139+
Usage: "The service which context to use. Use @{cluster-handle}/{service-name} format.",
140+
},
142141
cli.StringFlag{
143142
Name: "dir",
144143
Usage: "The directory to run the lua script from, defaults to the current working directory",
@@ -287,7 +286,7 @@ func (p *Plural) handleTemplateService(c *cli.Context) error {
287286
}
288287

289288
if identifier := c.String("service"); identifier != "" {
290-
serviceId, clusterName, serviceName, err := getServiceIdClusterNameServiceName(identifier)
289+
serviceId, clusterName, serviceName, err := parseServiceIdentifier(identifier)
291290
if err != nil {
292291
return err
293292
}
@@ -319,126 +318,6 @@ func (p *Plural) handleTemplateService(c *cli.Context) error {
319318
return printResult(res)
320319
}
321320

322-
func (p *Plural) handleLuaTemplate(c *cli.Context) error {
323-
if err := p.InitConsoleClient(consoleToken, consoleURL); err != nil {
324-
return err
325-
}
326-
327-
luaFile := c.String("lua-file")
328-
luaDir := c.String("lua-dir")
329-
context := c.String("context")
330-
dir := c.String("dir")
331-
if dir == "" {
332-
dir = "."
333-
}
334-
335-
if luaFile == "" {
336-
return fmt.Errorf("expected --lua-file flag")
337-
}
338-
339-
luaStr, err := utils.ReadFile(luaFile)
340-
if err != nil {
341-
return err
342-
}
343-
344-
if luaDir != "" {
345-
luaFiles, err := luaFolder(luaDir)
346-
if err != nil {
347-
return err
348-
}
349-
350-
luaStr = luaFiles + "\n\n" + luaStr
351-
}
352-
353-
ctx := map[string]interface{}{}
354-
if context != "" {
355-
if err := utils.YamlFile(context, &ctx); err != nil {
356-
return err
357-
}
358-
}
359-
360-
values := map[interface{}]interface{}{}
361-
valuesFiles := []string{}
362-
363-
dir, err = filepath.Abs(dir)
364-
if err != nil {
365-
return err
366-
}
367-
L := luautils.NewLuaState(dir)
368-
defer L.Close()
369-
370-
// Register global values and valuesFiles in Lua
371-
valuesTable := L.NewTable()
372-
L.SetGlobal("values", valuesTable)
373-
374-
valuesFilesTable := L.NewTable()
375-
L.SetGlobal("valuesFiles", valuesFilesTable)
376-
L.SetGlobal("cluster", luautils.GoValueToLuaValue(L, ctx["cluster"]))
377-
L.SetGlobal("configuration", luautils.GoValueToLuaValue(L, ctx["configuration"]))
378-
L.SetGlobal("contexts", luautils.GoValueToLuaValue(L, ctx["contexts"]))
379-
L.SetGlobal("imports", luautils.GoValueToLuaValue(L, ctx["imports"]))
380-
381-
if err := L.DoString(luaStr); err != nil {
382-
return err
383-
}
384-
385-
if err := luautils.MapLua(L.GetGlobal("values").(*lua.LTable), &values); err != nil {
386-
return err
387-
}
388-
389-
if err := luautils.MapLua(L.GetGlobal("valuesFiles").(*lua.LTable), &valuesFiles); err != nil {
390-
return err
391-
}
392-
393-
result := map[string]interface{}{
394-
"values": luautils.SanitizeValue(values),
395-
"valuesFiles": valuesFiles,
396-
}
397-
398-
utils.Highlight("Final lua output:\n\n")
399-
utils.NewYAMLPrinter(result).PrettyPrint()
400-
return nil
401-
}
402-
403-
func luaFolder(folder string) (string, error) {
404-
luaFiles := make([]string, 0)
405-
if err := filepath.WalkDir(folder, func(path string, info iofs.DirEntry, err error) error {
406-
if err != nil {
407-
return err
408-
}
409-
if info.IsDir() {
410-
return nil
411-
}
412-
413-
if strings.HasSuffix(info.Name(), ".lua") {
414-
luaPath, err := filepath.Rel(folder, path)
415-
if err != nil {
416-
return err
417-
}
418-
luaFiles = append(luaFiles, luaPath)
419-
}
420-
421-
return nil
422-
}); err != nil {
423-
return "", fmt.Errorf("failed to walk lua folder %s: %w", folder, err)
424-
}
425-
426-
sort.Slice(luaFiles, func(i, j int) bool {
427-
return luaFiles[i] < luaFiles[j]
428-
})
429-
430-
luaFileContents := make([]string, 0)
431-
for _, file := range luaFiles {
432-
luaContents, err := os.ReadFile(file)
433-
if err != nil {
434-
return "", fmt.Errorf("failed to read lua file %s: %w", file, err)
435-
}
436-
luaFileContents = append(luaFileContents, string(luaContents))
437-
}
438-
439-
return strings.Join(luaFileContents, "\n\n"), nil
440-
}
441-
442321
func (p *Plural) handleCloneClusterService(c *cli.Context) error {
443322
if err := p.InitConsoleClient(consoleToken, consoleURL); err != nil {
444323
return err
@@ -452,7 +331,7 @@ func (p *Plural) handleCloneClusterService(c *cli.Context) error {
452331
return fmt.Errorf("could not find cluster %s", c.Args().Get(0))
453332
}
454333

455-
serviceId, clusterName, serviceName, err := getServiceIdClusterNameServiceName(c.Args().Get(1))
334+
serviceId, clusterName, serviceName, err := parseServiceIdentifier(c.Args().Get(1))
456335
if err != nil {
457336
return err
458337
}
@@ -498,7 +377,7 @@ func (p *Plural) handleUpdateClusterService(c *cli.Context) error {
498377
return err
499378
}
500379
contextBindings := containers.NewSet[string]()
501-
serviceId, clusterName, serviceName, err := getServiceIdClusterNameServiceName(c.Args().Get(0))
380+
serviceId, clusterName, serviceName, err := parseServiceIdentifier(c.Args().Get(0))
502381
if err != nil {
503382
return err
504383
}
@@ -615,7 +494,7 @@ func (p *Plural) handleDescribeClusterService(c *cli.Context) error {
615494
return err
616495
}
617496

618-
serviceId, clusterName, serviceName, err := getServiceIdClusterNameServiceName(c.Args().Get(0))
497+
serviceId, clusterName, serviceName, err := parseServiceIdentifier(c.Args().Get(0))
619498
if err != nil {
620499
return err
621500
}
@@ -651,7 +530,7 @@ func (p *Plural) handleDeleteClusterService(c *cli.Context) error {
651530
if err := p.InitConsoleClient(consoleToken, consoleURL); err != nil {
652531
return err
653532
}
654-
serviceId, clusterName, serviceName, err := getServiceIdClusterNameServiceName(c.Args().Get(0))
533+
serviceId, clusterName, serviceName, err := parseServiceIdentifier(c.Args().Get(0))
655534
if err != nil {
656535
return err
657536
}
@@ -677,7 +556,7 @@ func (p *Plural) handleKickClusterService(c *cli.Context) error {
677556
if err := p.InitConsoleClient(consoleToken, consoleURL); err != nil {
678557
return err
679558
}
680-
serviceId, clusterName, serviceName, err := getServiceIdClusterNameServiceName(c.Args().Get(0))
559+
serviceId, clusterName, serviceName, err := parseServiceIdentifier(c.Args().Get(0))
681560
if err != nil {
682561
return err
683562
}
@@ -697,7 +576,7 @@ func (p *Plural) handleKickClusterService(c *cli.Context) error {
697576
}
698577

699578
func (p *Plural) handleTarballClusterService(c *cli.Context) error {
700-
serviceId, clusterName, serviceName, err := getServiceIdClusterNameServiceName(c.Args().Get(0))
579+
serviceId, clusterName, serviceName, err := parseServiceIdentifier(c.Args().Get(0))
701580
if err != nil {
702581
return fmt.Errorf("could not parse args: %w", err)
703582
}
@@ -744,30 +623,55 @@ type ServiceDeploymentAttributesConfiguration struct {
744623
Configuration []*gqlclient.ConfigAttributes
745624
}
746625

747-
func getServiceIdClusterNameServiceName(input string) (serviceId, clusterName, serviceName *string, err error) {
748-
if strings.HasPrefix(input, "@") {
749-
i := strings.Trim(input, "@")
626+
func validateFlag(ctx *cli.Context, name string, defaultVal string) (string, error) {
627+
res := ctx.String(name)
628+
if res == "" {
629+
if defaultVal == "" {
630+
return "", fmt.Errorf("expected --%s flag", name)
631+
}
632+
res = defaultVal
633+
}
634+
635+
return res, nil
636+
}
637+
638+
// parseServiceIdentifier parses the given identifier and returns the service id, cluster name, and service name.
639+
// If the identifier is in the format @{cluster-handle}/{service-name}, the cluster name and service name are returned.
640+
// Otherwise, the service id is returned.
641+
func parseServiceIdentifier(id string) (serviceId, clusterName, serviceName *string, err error) {
642+
if strings.HasPrefix(id, "@") {
643+
i := strings.Trim(id, "@")
750644
split := strings.Split(i, "/")
751645
if len(split) != 2 {
752-
err = fmt.Errorf("expected format @{cluster-handle}/{serviceName}")
646+
err = fmt.Errorf("expected format @{cluster-handle}/{service-name} or {service-id}, got %s", id)
753647
return
754648
}
755649
clusterName = &split[0]
756650
serviceName = &split[1]
757651
} else {
758-
serviceId = &input
652+
serviceId = &id
759653
}
654+
760655
return
761656
}
762657

763-
func validateFlag(ctx *cli.Context, name string, defaultVal string) (string, error) {
764-
res := ctx.String(name)
765-
if res == "" {
766-
if defaultVal == "" {
767-
return "", fmt.Errorf("expected --%s flag", name)
768-
}
769-
res = defaultVal
658+
// getService returns the service deployment for the given identifier.
659+
// Identifier should be in the format of @{cluster-handle}/{service-name} or {service-id}.
660+
// If the identifier is empty, it will return nil.
661+
func getService(c console.ConsoleClient, id string) (*gqlclient.ServiceDeploymentExtended, error) {
662+
if id == "" {
663+
return nil, nil
770664
}
771665

772-
return res, nil
666+
serviceId, clusterName, serviceName, err := parseServiceIdentifier(id)
667+
if err != nil {
668+
return nil, fmt.Errorf("could not parse identifier: %w", err)
669+
}
670+
671+
service, err := c.GetClusterService(serviceId, serviceName, clusterName)
672+
if err != nil {
673+
return nil, fmt.Errorf("could not get service deployment: %w", err)
674+
}
675+
676+
return service, lo.Ternary(service == nil, fmt.Errorf("could not find service deployment for %s", id), nil)
773677
}

0 commit comments

Comments
 (0)