Skip to content

Commit

Permalink
fix: provide stashed META values before installation
Browse files Browse the repository at this point in the history
Previously, if META values were supplied to the Talos ISO via
environment variable, they will be written down and available after the
install. With this fix, values are also readable and available before
the installation runs (in maintenance mode).

Most of the PR is refactoring `meta.Value(s)` to be a shared library
which is used by the installer/imager and (now) Talos.

Also fixes an issue with not returning properly `NotExist` error when
META is not yet available as a partition on disk.

Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
  • Loading branch information
smira committed Jun 27, 2023
1 parent 258f074 commit 35d6adc
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 83 deletions.
5 changes: 1 addition & 4 deletions cmd/installer/cmd/iso.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@ package cmd
import (
"bytes"
_ "embed"
"encoding/base64"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
"text/template"

"github.com/siderolabs/go-procfs/procfs"
Expand Down Expand Up @@ -134,8 +132,7 @@ func createISO(out string) error {

if metaValues := options.MetaValues.GetSlice(); len(metaValues) > 0 {
// pass META values as kernel talos.environment args which will be passed via the environment to the installer
metaBase64 := base64.StdEncoding.EncodeToString([]byte(strings.Join(metaValues, ";")))
cmdline.Append(constants.KernelParamEnvironment, metaValueEnvVariable+"="+metaBase64)
cmdline.Append(constants.KernelParamEnvironment, constants.MetaValuesEnvVar+"="+options.MetaValues.Encode())
}

var grubCfg bytes.Buffer
Expand Down
15 changes: 2 additions & 13 deletions cmd/installer/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@
package cmd

import (
"encoding/base64"
"fmt"
"os"
"runtime"
"strings"

"github.com/spf13/cobra"

Expand All @@ -25,20 +23,11 @@ var rootCmd = &cobra.Command{
Long: ``,
}

const metaValueEnvVariable = "INSTALLER_META_BASE64"

func setFlagsFromEnvironment() error {
if metaEnvBase64 := os.Getenv(metaValueEnvVariable); metaEnvBase64 != "" {
metaEnv, err := base64.StdEncoding.DecodeString(metaEnvBase64)
if err != nil {
if metaEnvBase64 := os.Getenv(constants.MetaValuesEnvVar); metaEnvBase64 != "" {
if err := options.MetaValues.Decode(metaEnvBase64); err != nil {
return err
}

for _, val := range strings.Split(string(metaEnv), ";") {
if err := options.MetaValues.Set(val); err != nil {
return err
}
}
}

return nil
Expand Down
61 changes: 25 additions & 36 deletions cmd/installer/pkg/install/meta_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,19 @@
package install

import (
"fmt"
"strconv"
"strings"

"github.com/spf13/pflag"

"github.com/siderolabs/talos/pkg/machinery/meta"
)

// MetaValues is a list of MetaValue.
type MetaValues struct {
values []MetaValue
values meta.Values
changed bool
}

// MetaValue represents a key/value pair for META.
type MetaValue struct {
Key uint8
Value string
}

func (v *MetaValue) String() string {
return fmt.Sprintf("0x%x=%s", v.Key, v.Value)
}

// Parse k=v expression.
func (v *MetaValue) Parse(s string) error {
k, vv, ok := strings.Cut(s, "=")
if !ok {
return fmt.Errorf("invalid value %q", s)
}

key, err := strconv.ParseUint(k, 0, 8)
if err != nil {
return fmt.Errorf("invalid key %q", k)
}

v.Key = uint8(key)
v.Value = vv

return nil
}

// Interface check.
var (
_ pflag.Value = &MetaValues{}
Expand All @@ -54,14 +26,14 @@ var (

// Set implements pflag.Value.
func (s *MetaValues) Set(val string) error {
var v MetaValue
var v meta.Value

if err := v.Parse(val); err != nil {
return err
}

if !s.changed {
s.values = []MetaValue{v}
s.values = meta.Values{v}
} else {
s.values = append(s.values, v)
}
Expand All @@ -83,7 +55,7 @@ func (s *MetaValues) String() string {

// Append implements pflag.SliceValue.
func (s *MetaValues) Append(val string) error {
var v MetaValue
var v meta.Value

if err := v.Parse(val); err != nil {
return err
Expand All @@ -96,10 +68,10 @@ func (s *MetaValues) Append(val string) error {

// Replace implements pflag.SliceValue.
func (s *MetaValues) Replace(val []string) error {
out := make([]MetaValue, len(val))
out := make(meta.Values, len(val))

for i, pair := range val {
var v MetaValue
var v meta.Value

if err := v.Parse(pair); err != nil {
return err
Expand All @@ -123,3 +95,20 @@ func (s *MetaValues) GetSlice() []string {

return out
}

// Encode returns the encoded values.
func (s *MetaValues) Encode() string {
return s.values.Encode()
}

// Decode the values from the given string.
func (s *MetaValues) Decode(val string) error {
values, err := meta.DecodeValues(val)
if err != nil {
return err
}

s.values = values

return nil
}
26 changes: 7 additions & 19 deletions cmd/installer/pkg/install/meta_value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,6 @@ import (
"github.com/siderolabs/talos/cmd/installer/pkg/install"
)

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

var v install.MetaValue

require.NoError(t, v.Parse("10=foo"))

assert.Equal(t, uint8(10), v.Key)
assert.Equal(t, "foo", v.Value)

assert.Equal(t, "0xa=foo", v.String())

var v2 install.MetaValue

require.NoError(t, v2.Parse(v.String()))

assert.Equal(t, v, v2)
}

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

Expand All @@ -41,4 +22,11 @@ func TestMetaValues(t *testing.T) {
require.NoError(t, s.Append("20=bar"))

assert.Equal(t, "[0xa=foo,0x14=bar]", s.String())

encoded := s.Encode()

var s2 install.MetaValues

require.NoError(t, s2.Decode(encoded))
assert.Equal(t, s.String(), s2.String())
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import (
"github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/kernel"
metamachinery "github.com/siderolabs/talos/pkg/machinery/meta"
resourcefiles "github.com/siderolabs/talos/pkg/machinery/resources/files"
"github.com/siderolabs/talos/pkg/machinery/resources/k8s"
resourceruntime "github.com/siderolabs/talos/pkg/machinery/resources/runtime"
Expand Down Expand Up @@ -2370,6 +2371,31 @@ func ReloadMeta(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) {
return err
}

// attempt to populate meta from the environment if Talos is not installed (yet)
if os.IsNotExist(err) {
env := environment.Get(r.Config())

prefix := constants.MetaValuesEnvVar + "="

for _, e := range env {
if !strings.HasPrefix(e, prefix) {
continue
}

values, err := metamachinery.DecodeValues(e[len(prefix):])
if err != nil {
return fmt.Errorf("error decoding meta values: %w", err)
}

for _, value := range values {
_, err = r.State().Machine().Meta().SetTag(ctx, value.Key, value.Value)
if err != nil {
return fmt.Errorf("error setting meta tag %x: %w", value.Key, err)
}
}
}
}

return nil
}, "reloadMeta"
}
Expand Down
19 changes: 12 additions & 7 deletions internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,17 @@ func (s *MachineState) Meta() runtime.Meta {
return s
}

var justLoaded bool
var (
justLoaded bool
loadErr error
)

s.metaOnce.Do(func() {
var err error

s.meta, err = meta.New(context.Background(), s.resources)
if err != nil {
log.Printf("META: failed to load: %s", err)
s.meta, loadErr = meta.New(context.Background(), s.resources)
if loadErr != nil {
if !os.IsNotExist(loadErr) {
log.Printf("META: failed to load: %s", loadErr)
}
} else {
s.probeMeta()
}
Expand All @@ -165,6 +168,7 @@ func (s *MachineState) Meta() runtime.Meta {
return metaWrapper{
MachineState: s,
justLoaded: justLoaded,
loadErr: loadErr,
}
}

Expand Down Expand Up @@ -330,11 +334,12 @@ func (s *MachineState) DBus() runtime.DBusState {
type metaWrapper struct {
*MachineState
justLoaded bool
loadErr error
}

func (m metaWrapper) Reload(ctx context.Context) error {
if m.justLoaded {
return nil
return m.loadErr
}

return m.MachineState.Reload(ctx)
Expand Down
5 changes: 1 addition & 4 deletions internal/pkg/meta/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,8 @@ func New(ctx context.Context, st state.State, opts ...Option) (*Meta, error) {
}

err = meta.Reload(ctx)
if err != nil && !os.IsNotExist(err) {
return meta, err
}

return meta, nil
return meta, err
}

func (meta *Meta) getPath() (string, error) {
Expand Down
3 changes: 3 additions & 0 deletions pkg/machinery/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,9 @@ const (

// PlatformMetal is the name of the metal platform.
PlatformMetal = "metal"

// MetaValuesEnvVar is the name of the environment variable to store encoded meta values for the disk image (installer).
MetaValuesEnvVar = "INSTALLER_META_BASE64"
)

// See https://linux.die.net/man/3/klogctl
Expand Down
Loading

0 comments on commit 35d6adc

Please sign in to comment.