Skip to content

Commit

Permalink
Add optional support for specifying digests for template locators
Browse files Browse the repository at this point in the history
Instead of `base: template.yaml` the user can write:

```yaml
base:
- url: template.yaml
  digest: decafbad
```

Same thing for `file` properties of provisoning scripts and probes.

The digest values are currently being ignored; verification will happen
in a later PR.

Signed-off-by: Jan Dubois <[email protected]>
  • Loading branch information
jandubois committed Feb 10, 2025
1 parent 253c7ca commit 05a151c
Show file tree
Hide file tree
Showing 8 changed files with 58 additions and 38 deletions.
6 changes: 3 additions & 3 deletions pkg/limatmpl/abs.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (tmpl *Template) useAbsLocators() error {
return err
}
for i, baseLocator := range tmpl.Config.Base {
locator, err := absPath(baseLocator, basePath)
locator, err := absPath(baseLocator.URL, basePath)
if err != nil {
return err
}
Expand All @@ -40,7 +40,7 @@ func (tmpl *Template) useAbsLocators() error {
}
for i, p := range tmpl.Config.Probes {
if p.File != nil {
locator, err := absPath(*p.File, basePath)
locator, err := absPath((*p.File).URL, basePath)

Check failure on line 43 in pkg/limatmpl/abs.go

View workflow job for this annotation

GitHub Actions / Lints

underef: could simplify (*p.File).URL to p.File.URL (gocritic)
if err != nil {
return err
}
Expand All @@ -49,7 +49,7 @@ func (tmpl *Template) useAbsLocators() error {
}
for i, p := range tmpl.Config.Provision {
if p.File != nil {
locator, err := absPath(*p.File, basePath)
locator, err := absPath((*p.File).URL, basePath)

Check failure on line 52 in pkg/limatmpl/abs.go

View workflow job for this annotation

GitHub Actions / Lints

underef: could simplify (*p.File).URL to p.File.URL (gocritic)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/limatmpl/abs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ func TestAbsPath(t *testing.T) {
assert.ErrorContains(t, err, "volume")
})
}

t.Run("", func(t *testing.T) {
actual, err := absPath("foo", "template://")
assert.NilError(t, err)
Expand Down
27 changes: 14 additions & 13 deletions pkg/limatmpl/embed.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package limatmpl
import (
"context"
"fmt"
"github.com/lima-vm/lima/pkg/limayaml"
"os"
"path/filepath"
"slices"
Expand Down Expand Up @@ -69,23 +70,23 @@ func (tmpl *Template) embedAllBases(ctx context.Context, embedAll, defaultBase b
break
}
baseLocator := tmpl.Config.Base[0]
isTemplate, _ := SeemsTemplateURL(baseLocator)
isTemplate, _ := SeemsTemplateURL(baseLocator.URL)
if isTemplate && !embedAll {
// Once we skip a template:// URL we can no longer embed any other base template
for i := 1; i < len(tmpl.Config.Base); i++ {
isTemplate, _ = SeemsTemplateURL(tmpl.Config.Base[i])
isTemplate, _ = SeemsTemplateURL(tmpl.Config.Base[i].URL)
if !isTemplate {
return fmt.Errorf("cannot embed template %q after not embedding %q", tmpl.Config.Base[i], baseLocator)
return fmt.Errorf("cannot embed template %q after not embedding %q", tmpl.Config.Base[i].URL, baseLocator.URL)
}
}
break
// TODO should we track embedding of template:// URLs so we can warn if we embed a non-template:// URL afterwards?
}

if seen[baseLocator] {
return fmt.Errorf("base template loop detected: template %q already included", baseLocator)
if seen[baseLocator.URL] {
return fmt.Errorf("base template loop detected: template %q already included", baseLocator.URL)
}
seen[baseLocator] = true
seen[baseLocator.URL] = true

// remove base[0] from template before merging
if err := tmpl.embedBase(ctx, baseLocator, embedAll, seen); err != nil {
Expand All @@ -101,13 +102,13 @@ func (tmpl *Template) embedAllBases(ctx context.Context, embedAll, defaultBase b
return nil
}

func (tmpl *Template) embedBase(ctx context.Context, baseLocator string, embedAll bool, seen map[string]bool) error {
func (tmpl *Template) embedBase(ctx context.Context, baseLocator limayaml.LocatorWithDigest, embedAll bool, seen map[string]bool) error {
warnBaseIsExperimental()
logrus.Debugf("Embedding base %q in template %q", baseLocator, tmpl.Locator)
logrus.Debugf("Embedding base %q in template %q", baseLocator.URL, tmpl.Locator)
if err := tmpl.Unmarshal(); err != nil {
return err
}
base, err := Read(ctx, "", baseLocator)
base, err := Read(ctx, "", baseLocator.URL)
if err != nil {
return err
}
Expand Down Expand Up @@ -557,9 +558,9 @@ func (tmpl *Template) embedAllScripts(ctx context.Context, embedAll bool) error
// Don't overwrite existing script. This should throw an error during validation.
if p.File != nil && p.Script == "" {
warnFileIsExperimental()
isTemplate, _ := SeemsTemplateURL(*p.File)
isTemplate, _ := SeemsTemplateURL((*p.File).URL)

Check failure on line 561 in pkg/limatmpl/embed.go

View workflow job for this annotation

GitHub Actions / Lints

underef: could simplify (*p.File).URL to p.File.URL (gocritic)
if embedAll || !isTemplate {
scriptTmpl, err := Read(ctx, "", *p.File)
scriptTmpl, err := Read(ctx, "", (*p.File).URL)

Check failure on line 563 in pkg/limatmpl/embed.go

View workflow job for this annotation

GitHub Actions / Lints

underef: could simplify (*p.File).URL to p.File.URL (gocritic)
if err != nil {
return err
}
Expand All @@ -570,9 +571,9 @@ func (tmpl *Template) embedAllScripts(ctx context.Context, embedAll bool) error
for i, p := range tmpl.Config.Provision {
if p.File != nil && p.Script == "" {
warnFileIsExperimental()
isTemplate, _ := SeemsTemplateURL(*p.File)
isTemplate, _ := SeemsTemplateURL((*p.File).URL)

Check failure on line 574 in pkg/limatmpl/embed.go

View workflow job for this annotation

GitHub Actions / Lints

underef: could simplify (*p.File).URL to p.File.URL (gocritic)
if embedAll || !isTemplate {
scriptTmpl, err := Read(ctx, "", *p.File)
scriptTmpl, err := Read(ctx, "", (*p.File).URL)

Check failure on line 576 in pkg/limatmpl/embed.go

View workflow job for this annotation

GitHub Actions / Lints

underef: could simplify (*p.File).URL to p.File.URL (gocritic)
if err != nil {
return err
}
Expand Down
9 changes: 6 additions & 3 deletions pkg/limatmpl/embed_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,16 @@ mounts:
},
{
"template:// URLs are not embedded when embedAll is false",
// also tests file.url format
``,
`
base: template://default
provision:
- file: template://provision.sh
- file:
url: template://provision.sh
probes:
- file: template://probe.sh
- file:
url: template://probe.sh
`,
`
base: template://default
Expand Down Expand Up @@ -228,7 +231,7 @@ base: baseX.yaml`,
"Bases are embedded depth-first",
`#`,
`
base: [base1.yaml, base2.yaml]
base: [base1.yaml, {url: base2.yaml}] # also test file.url format
additionalDisks: [disk0]
---
base: base3.yaml
Expand Down
2 changes: 1 addition & 1 deletion pkg/limatmpl/locator.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func Read(ctx context.Context, name, locator string) (*Template, error) {
return nil, err
}
}
logrus.Debugf("interpreting argument %q as a file url for instance %q", locator, tmpl.Name)
logrus.Debugf("interpreting argument %q as a file URL for instance %q", locator, tmpl.Name)
filePath := strings.TrimPrefix(locator, "file://")
if !filepath.IsAbs(filePath) {
return nil, fmt.Errorf("file URL %q is not an absolute path", locator)
Expand Down
27 changes: 16 additions & 11 deletions pkg/limayaml/limayaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ type LimaYAML struct {
User User `yaml:"user,omitempty" json:"user,omitempty"`
}

type BaseTemplates []string
type BaseTemplates []LocatorWithDigest

type LocatorWithDigest struct {
URL string `yaml:"url" json:"url"`
Digest *string `yaml:"digest,omitempty" json:"digest,omitempty" jsonschema:"nullable"` // TODO currently unused
}

type (
OS = string
Expand Down Expand Up @@ -215,11 +220,11 @@ const (
)

type Provision struct {
Mode ProvisionMode `yaml:"mode,omitempty" json:"mode,omitempty" jsonschema:"default=system"`
SkipDefaultDependencyResolution *bool `yaml:"skipDefaultDependencyResolution,omitempty" json:"skipDefaultDependencyResolution,omitempty"`
Script string `yaml:"script" json:"script"`
File *string `yaml:"file,omitempty" json:"file,omitempty" jsonschema:"nullable"`
Playbook string `yaml:"playbook,omitempty" json:"playbook,omitempty"`
Mode ProvisionMode `yaml:"mode,omitempty" json:"mode,omitempty" jsonschema:"default=system"`
SkipDefaultDependencyResolution *bool `yaml:"skipDefaultDependencyResolution,omitempty" json:"skipDefaultDependencyResolution,omitempty"`
Script string `yaml:"script" json:"script"`
File *LocatorWithDigest `yaml:"file,omitempty" json:"file,omitempty" jsonschema:"nullable"`
Playbook string `yaml:"playbook,omitempty" json:"playbook,omitempty"`
}

type Containerd struct {
Expand All @@ -235,11 +240,11 @@ const (
)

type Probe struct {
Mode ProbeMode `yaml:"mode,omitempty" json:"mode,omitempty" jsonschema:"default=readiness"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Script string `yaml:"script,omitempty" json:"script,omitempty"`
File *string `yaml:"file,omitempty" json:"file,omitempty" jsonschema:"nullable"`
Hint string `yaml:"hint,omitempty" json:"hint,omitempty"`
Mode ProbeMode `yaml:"mode,omitempty" json:"mode,omitempty" jsonschema:"default=readiness"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Script string `yaml:"script,omitempty" json:"script,omitempty"`
File *LocatorWithDigest `yaml:"file,omitempty" json:"file,omitempty" jsonschema:"nullable"`
Hint string `yaml:"hint,omitempty" json:"hint,omitempty"`
}

type Proto = string
Expand Down
15 changes: 13 additions & 2 deletions pkg/limayaml/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,21 @@ func unmarshalDisk(dst *Disk, b []byte) error {
return yaml.Unmarshal(b, dst)
}

// unmarshalBaseTemplates unmarshalls `base` which is either a string or a list of strings.
// unmarshalBaseTemplates unmarshalls `base` which is either a string or a list of Locators.
func unmarshalBaseTemplates(dst *BaseTemplates, b []byte) error {
var s string
if err := yaml.Unmarshal(b, &s); err == nil {
*dst = BaseTemplates{s}
*dst = BaseTemplates{LocatorWithDigest{URL: s}}
return nil
}
return yaml.UnmarshalWithOptions(b, dst, yaml.CustomUnmarshaler[LocatorWithDigest](unmarshalLocatorWithDigest))
}

// unmarshalLocator unmarshalls a locator which is either a string or a Locator struct.
func unmarshalLocatorWithDigest(dst *LocatorWithDigest, b []byte) error {
var s string
if err := yaml.Unmarshal(b, &s); err == nil {
*dst = LocatorWithDigest{URL: s}
return nil
}
return yaml.Unmarshal(b, dst)
Expand All @@ -49,6 +59,7 @@ func Unmarshal(data []byte, v any, comment string) error {
opts := []yaml.DecodeOption{
yaml.CustomUnmarshaler[BaseTemplates](unmarshalBaseTemplates),
yaml.CustomUnmarshaler[Disk](unmarshalDisk),
yaml.CustomUnmarshaler[LocatorWithDigest](unmarshalLocatorWithDigest),
}
if err := yaml.UnmarshalWithOptions(data, v, opts...); err != nil {
return fmt.Errorf("failed to unmarshal YAML (%s): %w", comment, err)
Expand Down
8 changes: 4 additions & 4 deletions pkg/limayaml/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ func Validate(y *LimaYAML, warn bool) error {
// y.Firmware.LegacyBIOS is ignored for aarch64, but not a fatal error.

for i, p := range y.Provision {
if p.File != nil && *p.File != "" {
return fmt.Errorf("field `provision[%d].file` must be empty during validation (script should already be embedded)", i)
if p.File != nil && (*p.File).URL != "" {

Check failure on line 209 in pkg/limayaml/validate.go

View workflow job for this annotation

GitHub Actions / Lints

underef: could simplify (*p.File).URL to p.File.URL (gocritic)
return fmt.Errorf("field `provision[%d].file.url` must be empty during validation (script should already be embedded)", i)
}
switch p.Mode {
case ProvisionModeSystem, ProvisionModeUser, ProvisionModeBoot:
Expand Down Expand Up @@ -249,8 +249,8 @@ func Validate(y *LimaYAML, warn bool) error {
}
}
for i, p := range y.Probes {
if p.File != nil && *p.File != "" {
return fmt.Errorf("field `probes[%d].file` must be empty during validation (script should already be embedded)", i)
if p.File != nil && (*p.File).URL != "" {

Check failure on line 252 in pkg/limayaml/validate.go

View workflow job for this annotation

GitHub Actions / Lints

underef: could simplify (*p.File).URL to p.File.URL (gocritic)
return fmt.Errorf("field `probes[%d].file.url` must be empty during validation (script should already be embedded)", i)
}
if !strings.HasPrefix(p.Script, "#!") {
return fmt.Errorf("field `probe[%d].script` must start with a '#!' line", i)
Expand Down

0 comments on commit 05a151c

Please sign in to comment.