mirror of
https://github.com/holos-run/holos.git
synced 2026-03-19 00:37:45 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67ef990c37 | ||
|
|
6bd54ab856 | ||
|
|
89a23a10fd | ||
|
|
5a939bb6fe |
50
cmd/holos/tests/v1alpha5/issues/issue366-cue-build-tags.txt
Normal file
50
cmd/holos/tests/v1alpha5/issues/issue366-cue-build-tags.txt
Normal file
@@ -0,0 +1,50 @@
|
||||
# https://github.com/holos-run/holos/issues/366
|
||||
# Build tags conditionally include CUE files.
|
||||
env HOME=$WORK
|
||||
|
||||
exec holos init platform v1alpha5 --force
|
||||
exec holos show platform
|
||||
cmp stdout want/empty.yaml
|
||||
|
||||
exec holos show platform -t foo
|
||||
cmp stdout want/foo.yaml
|
||||
|
||||
-- platform/empty.cue --
|
||||
@if(foo)
|
||||
package holos
|
||||
|
||||
Platform: Components: foo: _
|
||||
-- platform/metadata.cue --
|
||||
package holos
|
||||
|
||||
Platform: Components: [NAME=string]: {
|
||||
name: NAME
|
||||
path: "components/empty"
|
||||
labels: "app.holos.run/name": NAME
|
||||
annotations: "app.holos.run/description": "\(NAME) empty test case"
|
||||
}
|
||||
-- components/empty/empty.cue --
|
||||
package holos
|
||||
|
||||
Component: #Kubernetes & {}
|
||||
holos: Component.BuildPlan
|
||||
-- want/empty.yaml --
|
||||
apiVersion: v1alpha5
|
||||
kind: Platform
|
||||
metadata:
|
||||
name: default
|
||||
spec:
|
||||
components: []
|
||||
-- want/foo.yaml --
|
||||
apiVersion: v1alpha5
|
||||
kind: Platform
|
||||
metadata:
|
||||
name: default
|
||||
spec:
|
||||
components:
|
||||
- annotations:
|
||||
app.holos.run/description: foo empty test case
|
||||
labels:
|
||||
app.holos.run/name: foo
|
||||
name: foo
|
||||
path: components/empty
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
// PlatformOpts represents build options when processing the components in a
|
||||
// platform.
|
||||
type PlatformOpts struct {
|
||||
Fn BuildFunc
|
||||
Fn func(context.Context, int, holos.Component) error
|
||||
Selector holos.Selector
|
||||
Concurrency int
|
||||
InfoEnabled bool
|
||||
@@ -89,9 +89,6 @@ func (p *Platform) Build(ctx context.Context, opts PlatformOpts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// BuildFunc is executed concurrently when processing platform components.
|
||||
type BuildFunc func(context.Context, int, holos.Component) error
|
||||
|
||||
func LoadPlatform(i *Instance) (platform Platform, err error) {
|
||||
err = i.Discriminate(func(tm holos.TypeMeta) error {
|
||||
if tm.Kind != "Platform" {
|
||||
|
||||
@@ -16,6 +16,8 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const tagHelp = "set the value of a cue @tag field in the form key [ = value ]"
|
||||
|
||||
func New(cfg *holos.Config, feature holos.Flagger) *cobra.Command {
|
||||
cmd := command.New("render")
|
||||
cmd.Args = cobra.NoArgs
|
||||
@@ -26,7 +28,7 @@ func New(cfg *holos.Config, feature holos.Flagger) *cobra.Command {
|
||||
}
|
||||
|
||||
func newPlatform(cfg *holos.Config, feature holos.Flagger) *cobra.Command {
|
||||
cmd := command.New("platform DIRECTORY")
|
||||
cmd := command.New("platform")
|
||||
cmd.Args = cobra.MaximumNArgs(1)
|
||||
cmd.Example = "holos render platform"
|
||||
cmd.Short = "render an entire platform"
|
||||
@@ -38,13 +40,13 @@ func newPlatform(cfg *holos.Config, feature holos.Flagger) *cobra.Command {
|
||||
}
|
||||
|
||||
var concurrency int
|
||||
cmd.Flags().IntVar(&concurrency, "concurrency", min(runtime.NumCPU(), 8), "number of components to render concurrently")
|
||||
cmd.Flags().IntVar(&concurrency, "concurrency", runtime.NumCPU(), "number of components to render concurrently")
|
||||
var platform string
|
||||
cmd.Flags().StringVar(&platform, "platform", "./platform", "platform directory path")
|
||||
var selector holos.Selector
|
||||
cmd.Flags().VarP(&selector, "selector", "l", "label selector (e.g. label==string,label!=string)")
|
||||
tagMap := make(holos.TagMap)
|
||||
cmd.Flags().VarP(&tagMap, "inject", "t", "set the value of a cue @tag field from a key=value pair")
|
||||
cmd.Flags().VarP(&tagMap, "inject", "t", tagHelp)
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Root().Context()
|
||||
@@ -70,7 +72,7 @@ func newPlatform(cfg *holos.Config, feature holos.Flagger) *cobra.Command {
|
||||
"--log-format", cfg.LogConfig().Format(),
|
||||
}
|
||||
opts := builder.PlatformOpts{
|
||||
Fn: makePlatformRenderFunc(cmd.ErrOrStderr(), prefixArgs),
|
||||
Fn: makeComponentRenderFunc(cmd.ErrOrStderr(), prefixArgs, tagMap.Tags()),
|
||||
Selector: selector,
|
||||
Concurrency: concurrency,
|
||||
InfoEnabled: true,
|
||||
@@ -102,9 +104,9 @@ func newComponent(cfg *holos.Config, feature holos.Flagger) *cobra.Command {
|
||||
}
|
||||
|
||||
tagMap := make(holos.TagMap)
|
||||
cmd.Flags().VarP(&tagMap, "inject", "t", "set the value of a cue @tag field from a key=value pair")
|
||||
cmd.Flags().VarP(&tagMap, "inject", "t", tagHelp)
|
||||
var concurrency int
|
||||
cmd.Flags().IntVar(&concurrency, "concurrency", min(runtime.NumCPU(), 8), "number of concurrent build steps")
|
||||
cmd.Flags().IntVar(&concurrency, "concurrency", runtime.NumCPU(), "number of concurrent build steps")
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Root().Context()
|
||||
@@ -134,7 +136,7 @@ func newComponent(cfg *holos.Config, feature holos.Flagger) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func makePlatformRenderFunc(w io.Writer, prefixArgs []string) builder.BuildFunc {
|
||||
func makeComponentRenderFunc(w io.Writer, prefixArgs, cliTags []string) func(context.Context, int, holos.Component) error {
|
||||
return func(ctx context.Context, idx int, component holos.Component) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -147,6 +149,9 @@ func makePlatformRenderFunc(w io.Writer, prefixArgs []string) builder.BuildFunc
|
||||
args := make([]string, 0, 10+len(prefixArgs)+(len(tags)*2))
|
||||
args = append(args, prefixArgs...)
|
||||
args = append(args, "render", "component")
|
||||
for _, tag := range cliTags {
|
||||
args = append(args, "--inject", tag)
|
||||
}
|
||||
for _, tag := range tags {
|
||||
args = append(args, "--inject", tag)
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ func newShowBuildPlanCmd() (cmd *cobra.Command) {
|
||||
buildPlanOpts := holos.NewBuildOpts(path)
|
||||
buildPlanOpts.Stderr = cmd.ErrOrStderr()
|
||||
buildPlanOpts.Concurrency = concurrency
|
||||
buildPlanOpts.Tags = tagMap.Tags()
|
||||
|
||||
platformOpts := builder.PlatformOpts{
|
||||
Fn: makeBuildFunc(encoder, buildPlanOpts),
|
||||
@@ -110,7 +111,7 @@ func newShowBuildPlanCmd() (cmd *cobra.Command) {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func makeBuildFunc(encoder holos.OrderedEncoder, opts holos.BuildOpts) builder.BuildFunc {
|
||||
func makeBuildFunc(encoder holos.OrderedEncoder, opts holos.BuildOpts) func(context.Context, int, holos.Component) error {
|
||||
return func(ctx context.Context, idx int, component holos.Component) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -120,6 +121,7 @@ func makeBuildFunc(encoder holos.OrderedEncoder, opts holos.BuildOpts) builder.B
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
tags = append(tags, opts.Tags...)
|
||||
inst, err := builder.LoadInstance(component.Path(), tags)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
|
||||
@@ -40,13 +40,22 @@ func (i *StringSlice) Set(value string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TagMap represents a map of key values for CUE TagMap for flag parsing.
|
||||
type TagMap map[string]string
|
||||
// TagMap represents a map of key values for CUE TagMap for flag parsing. The
|
||||
// values are pointers to disambiguate between the case where a tag is a boolean
|
||||
// ("--inject foo") and the case where a tag has a string zero value ("--inject
|
||||
// foo="). Refer to the Tags field of [cue/load.Config]
|
||||
//
|
||||
// [cue/load.Config]: https://pkg.go.dev/cuelang.org/go@v0.10.1/cue/load#Config
|
||||
type TagMap map[string]*string
|
||||
|
||||
func (t TagMap) Tags() []string {
|
||||
parts := make([]string, 0, len(t))
|
||||
for k, v := range t {
|
||||
parts = append(parts, fmt.Sprintf("%s=%s", k, v))
|
||||
for tag, val := range t {
|
||||
if val == nil {
|
||||
parts = append(parts, tag)
|
||||
} else {
|
||||
parts = append(parts, fmt.Sprintf("%s=%s", tag, *val))
|
||||
}
|
||||
}
|
||||
return parts
|
||||
}
|
||||
@@ -60,10 +69,14 @@ func (t TagMap) String() string {
|
||||
// is not supported.
|
||||
func (t TagMap) Set(value string) error {
|
||||
parts := strings.SplitN(value, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
switch len(parts) {
|
||||
case 1:
|
||||
t[parts[0]] = nil
|
||||
case 2:
|
||||
t[parts[0]] = &parts[1]
|
||||
default:
|
||||
return errors.Format("invalid format, must be tag=value")
|
||||
}
|
||||
t[parts[0]] = parts[1]
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -307,6 +320,7 @@ type BuildOpts struct {
|
||||
Stderr io.Writer
|
||||
WriteTo string
|
||||
Path string
|
||||
Tags []string
|
||||
}
|
||||
|
||||
func NewBuildOpts(path string) BuildOpts {
|
||||
@@ -316,5 +330,6 @@ func NewBuildOpts(path string) BuildOpts {
|
||||
Stderr: os.Stderr,
|
||||
WriteTo: "deploy",
|
||||
Path: path,
|
||||
Tags: make([]string, 0, 10),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,10 +43,11 @@ func RunCmd(ctx context.Context, name string, args ...string) (result RunResult,
|
||||
cmd.Stdout = result.Stdout
|
||||
cmd.Stderr = result.Stderr
|
||||
log := logger.FromContext(ctx)
|
||||
log.DebugContext(ctx, "running: "+name, "name", name, "args", args)
|
||||
command := fmt.Sprintf("%s '%s'", name, strings.Join(args, "' '"))
|
||||
log.DebugContext(ctx, "running command: "+command, "name", name, "args", args)
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("could not run command: %s %s: %w", name, strings.Join(args, " "), err)
|
||||
err = fmt.Errorf("could not run command:\n\t%s\n\t%w", command, err)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
1
|
||||
2
|
||||
|
||||
Reference in New Issue
Block a user