From a288073a76d313f190e7bbe32d53837bf39e2146 Mon Sep 17 00:00:00 2001 From: David Cassany Date: Fri, 15 Jul 2022 09:29:50 +0200 Subject: [PATCH] Delete all commented code Signed-off-by: David Cassany --- pkg/config/coerce.go | 102 -------- pkg/config/config.go | 14 -- pkg/config/read.go | 392 ------------------------------- pkg/config/read_test.go | 494 --------------------------------------- pkg/config/rename.go | 67 ------ pkg/config/tftpget.go | 57 ----- pkg/config/toenv.go | 71 ++++++ pkg/config/toenv_test.go | 163 +++++++++++++ pkg/config/write.go | 48 ---- 9 files changed, 234 insertions(+), 1174 deletions(-) delete mode 100644 pkg/config/coerce.go delete mode 100644 pkg/config/read.go delete mode 100644 pkg/config/read_test.go delete mode 100644 pkg/config/rename.go delete mode 100644 pkg/config/tftpget.go create mode 100644 pkg/config/toenv.go create mode 100644 pkg/config/toenv_test.go delete mode 100644 pkg/config/write.go diff --git a/pkg/config/coerce.go b/pkg/config/coerce.go deleted file mode 100644 index 00a14eff5..000000000 --- a/pkg/config/coerce.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -Copyright © 2022 SUSE LLC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -/*import ( - "github.com/rancher/wrangler/pkg/data" - "github.com/rancher/wrangler/pkg/data/convert" - schemas2 "github.com/rancher/wrangler/pkg/schemas" - "github.com/rancher/wrangler/pkg/schemas/mappers" -) - -type Converter func(val interface{}) interface{} - -type fieldConverter struct { - mappers.DefaultMapper - fieldName string - converter Converter -} - -func (f fieldConverter) ToInternal(data data.Object) error { - val, ok := data[f.fieldName] - if !ok { - return nil - } - data[f.fieldName] = f.converter(val) - return nil -} - -type typeConverter struct { - mappers.DefaultMapper - converter Converter - fieldType string - mappers schemas2.Mappers -} - -func (t *typeConverter) ToInternal(data data.Object) error { - return t.mappers.ToInternal(data) -} - -func (t *typeConverter) ModifySchema(schema *schemas2.Schema, schemas *schemas2.Schemas) error { - for name, field := range schema.ResourceFields { - if field.Type == t.fieldType { - t.mappers = append(t.mappers, fieldConverter{ - fieldName: name, - converter: t.converter, - }) - } - } - return nil -} - -func NewTypeConverter(fieldType string, converter Converter) schemas2.Mapper { - return &typeConverter{ - fieldType: fieldType, - converter: converter, - } -} - -func NewToMap() schemas2.Mapper { - return NewTypeConverter("map[string]", func(val interface{}) interface{} { - if m, ok := val.(map[string]interface{}); ok { - obj := make(map[string]string, len(m)) - for k, v := range m { - obj[k] = convert.ToString(v) - } - return obj - } - return val - }) -} - -func NewToSlice() schemas2.Mapper { - return NewTypeConverter("array[string]", func(val interface{}) interface{} { - if str, ok := val.(string); ok { - return []string{str} - } - return val - }) -} - -func NewToBool() schemas2.Mapper { - return NewTypeConverter("boolean", func(val interface{}) interface{} { - if str, ok := val.(string); ok { - return str == "true" //nolint:goconst - } - return val - }) -}*/ diff --git a/pkg/config/config.go b/pkg/config/config.go index 838531651..855a47eeb 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -76,20 +76,6 @@ type Config struct { Data map[string]interface{} `yaml:"data,omitempty" json:"data,omitempty"` } -type YipConfig struct { - Stages map[string][]Stage `json:"stages,omitempty"` -} - -type Stage struct { - Users map[string]User `json:"users,omitempty"` -} - -type User struct { - Name string `json:"name,omitempty"` - PasswordHash string `json:"passwd,omitempty"` - SSHAuthorizedKeys []string `json:"ssh_authorized_keys,omitempty"` -} - func (in *Config) DeepCopyInto(out *Config) { *out = *in } diff --git a/pkg/config/read.go b/pkg/config/read.go deleted file mode 100644 index 6d694dece..000000000 --- a/pkg/config/read.go +++ /dev/null @@ -1,392 +0,0 @@ -/* -Copyright © 2022 SUSE LLC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -import ( - "fmt" - "strings" - - "github.com/rancher/wrangler/pkg/data/convert" -) - -/*const authorizedKeysKey = "ssh_authorized_keys" - -var ( - defaultMappers = schemas2.Mappers{ - NewToMap(), - NewToSlice(), - NewToBool(), - &FuzzyNames{}, - } - schemas = schemas2.EmptySchemas().Init(func(s *schemas2.Schemas) *schemas2.Schemas { - s.AddMapper("config", defaultMappers) - s.AddMapper("elemental", defaultMappers) - s.AddMapper("install", defaultMappers) - return s - }).MustImport(Config{}) - schema = schemas.Schema("config") -)*/ - -// ToEnv converts the config into a slice env. -// The configuration fields are prefixed with "_COS" -// to allow installation parameters to be set in the cos.sh script: -// e.g. https://github.com/rancher-sandbox/cOS-toolkit/blob/affc831b76d50298bbbbe637f31c81c52c5489b8/packages/backports/installer/cos.sh#L698 -func ToEnv(cfg Config) ([]string, error) { - // Pass only the elemental values, as those are the ones used by the installer/cli - // Ignore the Data as that is cloud-config stuff - data, err := convert.EncodeToMap(&cfg.Elemental) - if err != nil { - return nil, err - } - - return mapToEnv("ELEMENTAL_", data), nil -} - -// it's a mapping of how config env option should be transliterated to the elemental CLI -var defaultOverrides = map[string]string{ - "ELEMENTAL_INSTALL_CONFIG_URL": "ELEMENTAL_INSTALL_CLOUD_INIT", - "ELEMENTAL_INSTALL_POWEROFF": "ELEMENTAL_POWEROFF", - "ELEMENTAL_INSTALL_DEVICE": "ELEMENTAL_INSTALL_TARGET", - "ELEMENTAL_INSTALL_SYSTEM_URI": "ELEMENTAL_INSTALL_SYSTEM", - "ELEMENTAL_INSTALL_DEBUG": "ELEMENTAL_DEBUG", -} - -func envOverrides(keyName string) string { - for k, v := range defaultOverrides { - keyName = strings.ReplaceAll(keyName, k, v) - } - return keyName -} - -func mapToEnv(prefix string, data map[string]interface{}) []string { - var result []string - for k, v := range data { - keyName := strings.ToUpper(prefix + convert.ToYAMLKey(k)) - keyName = strings.ReplaceAll(keyName, "-", "_") - // Apply overrides needed to convert between configs types - keyName = envOverrides(keyName) - - if data, ok := v.(map[string]interface{}); ok { - subResult := mapToEnv(keyName+"_", data) - result = append(result, subResult...) - } else { - result = append(result, fmt.Sprintf("%s=%v", keyName, v)) - } - } - return result -} - -/*func readFileFunc(path string) func() (map[string]interface{}, error) { - return func() (map[string]interface{}, error) { - return readFile(path) - } -} - -func readNested(data map[string]interface{}, overlay bool) (map[string]interface{}, error) { - var ( - nestedConfigFiles = convert.ToStringSlice(values.GetValueN(data, "elemental", "install", "configUrl")) - funcs []reader - ) - - if overlay { - funcs = append(funcs, func() (map[string]interface{}, error) { - return data, nil - }) - } - - for _, nestedConfigFile := range nestedConfigFiles { - funcs = append(funcs, readFileFunc(nestedConfigFile)) - } - - if !overlay { - funcs = append(funcs, func() (map[string]interface{}, error) { - return data, nil - }) - } - - return merge(funcs...) -} - -func readFile(path string) (result map[string]interface{}, _ error) { - result = map[string]interface{}{} - - switch { - case strings.HasPrefix(path, "http://"): - fallthrough - case strings.HasPrefix(path, "https://"): - resp, err := http.Get(path) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - buffer, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("read %s: %w", path, err) - } - - return result, yaml.Unmarshal(buffer, &result) - case strings.HasPrefix(path, "tftp://"): - return tftpGet(path) - } - - f, err := ioutil.ReadFile(path) - if os.IsNotExist(err) { - return nil, nil - } else if err != nil { - return nil, err - } - - data := map[string]interface{}{} - if err := yaml.Unmarshal(f, &data); err != nil { - return nil, err - } - - return readNested(data, false) -} - -type reader func() (map[string]interface{}, error) - -func merge(readers ...reader) (map[string]interface{}, error) { - d := map[string]interface{}{} - for _, r := range readers { - newData, err := r() - if err != nil { - return nil, err - } - if err := schema.Mapper.ToInternal(newData); err != nil { - return nil, err - } - d = values.MergeMapsConcatSlice(d, newData) - } - return d, nil -} - -func readConfigMap(ctx context.Context, cfg string, includeCmdline bool) (map[string]interface{}, error) { - var ( - data map[string]interface{} - err error - ) - - if includeCmdline { - data, err = merge(readCmdline, readFileFunc(cfg)) - if err != nil { - return nil, err - } - } else { - data, err = merge(readFileFunc(cfg)) - if err != nil { - return nil, err - } - } - - if cfg != "" { - values.PutValue(data, cfg, "elemental", "install", "configUrl") - } - - return updateData(ctx, data) -} - -func updateData(ctx context.Context, data map[string]interface{}) (map[string]interface{}, error) { - registrationURL := convert.ToString(values.GetValueN(data, "elemental", "registration", "url")) - registrationCA := convert.ToString(values.GetValueN(data, "elemental", "registration", "ca_cert")) - emulatedTPM := convert.ToString(values.GetValueN(data, "elemental", "registration", "emulateTPM")) - emulatedSeed := convert.ToString(values.GetValueN(data, "elemental", "registration", "emulatedTPMSeed")) - noSmbios := convert.ToString(values.GetValueN(data, "elemental", "registration", "noSMBIOS")) - isoURL := convert.ToString(values.GetValueN(data, "elemental", "install", "iso")) - ContainerImage := convert.ToString(values.GetValueN(data, "elemental", "install", "system-uri")) - // Can't set both values at the same time - if isoURL != "" && ContainerImage != "" { - return nil, fmt.Errorf("cant set both elemental.install.iso and elemental.install.system-uri in /proc/cmdline or in MachineRegistration both .spec.cloudConfig.elemental.install.iso and .spec.cloudConfig.elemental.install.system-uri") - } - - if registrationURL != "" { - for { - select { - case <-ctx.Done(): - return data, nil - default: - newData, err := returnRegistrationData(registrationURL, registrationCA, emulatedTPM, emulatedSeed, noSmbios) - if err == nil { - newISOURL := convert.ToString(values.GetValueN(newData, "elemental", "install", "iso")) - newContainerImage := convert.ToString(values.GetValueN(newData, "elemental", "install", "system-uri")) - // If values from registration server are empty, override the old values into the new data - if newISOURL == "" { - values.PutValue(newData, isoURL, "elemental", "install", "iso") - } - if newContainerImage == "" { - values.PutValue(newData, ContainerImage, "elemental", "install", "system-uri") - } - // Return the data obtained from the registration url with our full data - return newData, nil - } - logrus.Errorf("failed to read registration URL %s, retrying: %v", registrationURL, err) - time.Sleep(15 * time.Second) - } - } - } - - return data, nil -} - -func ToFile(cfg Config, output string) error { - data, err := ToBytes(cfg) - if err != nil { - return err - } - return ioutil.WriteFile(output, data, 0600) -} - -func ToBytes(cfg Config) ([]byte, error) { - var ( - data map[string]interface{} - err error - ) - if len(cfg.Data) > 0 { - data = values.MergeMaps(nil, cfg.Data) - } else { - data, err = convert.EncodeToMap(cfg) - if err != nil { - return nil, err - } - if len(cfg.Elemental.Install.SSHKeys) > 0 { - data[authorizedKeysKey] = cfg.Elemental.Install.SSHKeys - } - } - values.RemoveValue(data, "install") - values.RemoveValue(data, "elemental", "install") - values.RemoveValue(data, "elemental", "registration") - values.RemoveValue(data, "elemental", "system_agent") - values.RemoveValue(data, "data") // Remove data here, as we are merging the data into the cfg directly, we want to output a valid cloud-config - bytes, err := yaml.Marshal(data) - if err != nil { - return nil, err - } - - return append([]byte("#cloud-config\n"), bytes...), nil -} - -func ReadConfig(ctx context.Context, cfg string, includeCmdline bool) (result Config, err error) { - data, err := readConfigMap(ctx, cfg, includeCmdline) - if err != nil { - return result, err - } - - if err := convert.ToObj(data, &result); err != nil { - return result, err - } - - result.Data = data - return result, nil -} - -func returnRegistrationData(url, ca, emulatedTPM, TPMSeed, noSmbios string) (map[string]interface{}, error) { - opts := []tpm.Option{tpm.WithCAs([]byte(ca)), tpm.AppendCustomCAToSystemCA} - - // can be explicitly disabled for testing purposes - if noSmbios != "true" { - smbios, err := getSMBiosHeaders() - if err != nil { - return nil, err - } - opts = append(opts, tpm.WithHeader(smbios)) - } - - if emulatedTPM == "true" { - logrus.Info("TPM Emulation enabled") - opts = append(opts, tpm.Emulated) - if TPMSeed != "" { - s, err := strconv.ParseInt(TPMSeed, 10, 64) - if err == nil { - logrus.Infof("TPM Emulation Seed '%s'", TPMSeed) - opts = append(opts, tpm.WithSeed(s)) - } - } else { - opts = append(opts, tpm.WithSeed(1)) - } - } - - data, err := tpm.Get(url, opts...) - if err != nil { - return nil, err - } - - logrus.Infof("Retrieved config from registrationURL: %s", data) - result := map[string]interface{}{} - return result, json.Unmarshal(data, &result) -} - -func getSMBiosHeaders() (http.Header, error) { - smbios, err := dmidecode.Decode() - if err != nil { - return nil, err - } - smbiosData, err := json.Marshal(smbios) - if err != nil { - return nil, err - } - - header := http.Header{} - header.Set("X-Cattle-Smbios", base64.StdEncoding.EncodeToString(smbiosData)) - return header, nil -} - -func readCmdline() (map[string]interface{}, error) { - //supporting regex https://regexr.com/4mq0s - parser, err := regexp.Compile(`(\"[^\"]+\")|([^\s]+=(\"[^\"]+\")|([^\s]+))`) - if err != nil { - return nil, nil - } - - procCmdLine := os.Getenv("PROC_CMDLINE") - if procCmdLine == "" { - procCmdLine = "/proc/cmdline" - } - bytes, err := ioutil.ReadFile(procCmdLine) - if os.IsNotExist(err) { - return nil, nil - } else if err != nil { - return nil, err - } - - data := map[string]interface{}{} - for _, item := range parser.FindAllString(string(bytes), -1) { - parts := strings.SplitN(item, "=", 2) - value := "true" - if len(parts) > 1 { - value = strings.Trim(parts[1], `"`) - } - keys := strings.Split(strings.Trim(parts[0], `"`), ".") - existing, ok := values.GetValue(data, keys...) - if ok { - switch v := existing.(type) { - case string: - values.PutValue(data, []string{v, value}, keys...) - case []string: - values.PutValue(data, append(v, value), keys...) - } - } else { - values.PutValue(data, value, keys...) - } - } - - if err := schema.Mapper.ToInternal(data); err != nil { - return nil, err - } - - return readNested(data, true) -}*/ diff --git a/pkg/config/read_test.go b/pkg/config/read_test.go deleted file mode 100644 index 70012373f..000000000 --- a/pkg/config/read_test.go +++ /dev/null @@ -1,494 +0,0 @@ -/* -Copyright © 2022 SUSE LLC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config_test - -import ( - "context" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - - "time" - - gotpm "github.com/rancher-sandbox/go-tpm" - - "github.com/gorilla/websocket" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - . "github.com/rancher/elemental-operator/pkg/config" -) - -func writeRead(conn *websocket.Conn, input []byte) ([]byte, error) { - writer, err := conn.NextWriter(websocket.BinaryMessage) - if err != nil { - return nil, err - } - - if _, err := writer.Write(input); err != nil { - return nil, err - } - - if err := writer.Close(); err != nil { - return nil, err - } - - _, reader, err := conn.NextReader() - if err != nil { - return nil, err - } - - return ioutil.ReadAll(reader) -} - -var upgrader = websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, -} - -// Mimics a WS server which accepts TPM Bearer token -func WSServer(ctx context.Context, data map[string]interface{}) { - s := http.Server{ - Addr: "127.0.0.1:9980", - ReadTimeout: 10 * time.Second, - WriteTimeout: 10 * time.Second, - } - - m := http.NewServeMux() - m.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) { - conn, _ := upgrader.Upgrade(w, r, nil) // error ignored for sake of simplicity - - for { - - token := r.Header.Get("Authorization") - ek, at, err := gotpm.GetAttestationData(token) - if err != nil { - fmt.Println("error", err.Error()) - return - } - - secret, challenge, err := gotpm.GenerateChallenge(ek, at) - if err != nil { - fmt.Println("error", err.Error()) - return - } - - resp, _ := writeRead(conn, challenge) - - if err := gotpm.ValidateChallenge(secret, resp); err != nil { - fmt.Println(string(resp)) - fmt.Println("error validating challenge", err.Error()) - return - } - - writer, _ := conn.NextWriter(websocket.BinaryMessage) - _ = json.NewEncoder(writer).Encode(data) - } - }) - - s.Handler = m - - go s.ListenAndServe() - go func() { - <-ctx.Done() - _ = s.Shutdown(ctx) - }() -} - -var _ = Describe("os2 config unit tests", func() { - - /*var c Config - var data map[string]interface{} - - BeforeEach(func() { - c = Config{} - data = map[string]interface{}{ - "elemental": map[string]interface{}{ - "install": map[string]string{ - "iso": "foo", - }, - }, - } - })*/ - - /*Context("Validation", func() { - It("fails if iso and system-uri are both used at the same time", func() { - f, err := ioutil.TempFile("", "xxxxtest") - Expect(err).ToNot(HaveOccurred()) - defer os.Remove(f.Name()) - - _ = ioutil.WriteFile(f.Name(), []byte(` - elemental: - install: - system-uri: "docker/image:test" - iso: "test" - `), os.ModePerm) - - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - _, err = ReadConfig(ctx, f.Name(), false) - Expect(err).To(HaveOccurred()) - }) - It("fails if iso and system-uri are both empty", Serial, func() { - f, err := ioutil.TempFile("", "xxxxtest") - Expect(err).ToNot(HaveOccurred()) - defer os.Remove(f.Name()) - - _ = ioutil.WriteFile(f.Name(), []byte(` - elemental: - registration: - emulateTPM: true - noSMBIOS: true - emulatedTPMSeed: "5" - install: - firmware: efi - `), os.ModePerm) - - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - // Empty the install key so there is no isourl nor containerImage - values.PutValue(data, "", "elemental", "install") - WSServer(ctx, data) - _, err = ReadConfig(ctx, f.Name(), false) - Expect(err).To(HaveOccurred()) - }) - })*/ - - Context("convert to environment configuration", func() { - var c Config - It("handle empty config", func() { - c = Config{} - e, err := ToEnv(c) - Expect(err).ToNot(HaveOccurred()) - Expect(e).To(BeEmpty()) - }) - It("converts to env slice installation parameters", func() { - c = Config{ - Data: map[string]interface{}{ - "random": "data", - }, - Elemental: Elemental{ - // Those settings below are tied to the - // elemental installer. - Install: Install{ - Device: "foob", - ConfigURL: "fooc", - Firmware: "efi", - ISO: "http://foo.bar", - NoFormat: true, - Debug: true, - PowerOff: true, - TTY: "foo", - SystemURI: "docker:container", - SSHKeys: []string{"github:mudler"}, - }, - }, - } - e, err := ToEnv(c) - Expect(err).ToNot(HaveOccurred()) - Expect(len(e)).To(Equal(10)) - Expect(e).To( - ContainElements( - "ELEMENTAL_INSTALL_SSH_KEYS=[github:mudler]", - "ELEMENTAL_INSTALL_TARGET=foob", - "ELEMENTAL_INSTALL_CLOUD_INIT=fooc", - "ELEMENTAL_INSTALL_FIRMWARE=efi", - "ELEMENTAL_INSTALL_ISO=http://foo.bar", - "ELEMENTAL_INSTALL_NO_FORMAT=true", - "ELEMENTAL_DEBUG=true", - "ELEMENTAL_POWEROFF=true", - "ELEMENTAL_INSTALL_TTY=foo", - "ELEMENTAL_INSTALL_SYSTEM=docker:container", - ), - ) - }) - }) - - /*Context("reading config file", func() { - It("reads iso_url and registrationUrl", func() { - f, err := ioutil.TempFile("", "xxxxtest") - Expect(err).ToNot(HaveOccurred()) - defer os.Remove(f.Name()) - - _ = ioutil.WriteFile(f.Name(), []byte(` - elemental: - registration: - url: foobaz - install: - iso: "foo_bar" - `), os.ModePerm) - - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - c, err := ReadConfig(ctx, f.Name(), false) - Expect(err).ToNot(HaveOccurred()) - Expect(c.Elemental.Registration.URL).To(Equal("foobaz")) - Expect(c.Elemental.Install.ISO).To(Equal("foo_bar")) - }) - It("reads iso_url only, without contacting a registrationUrl server", func() { - f, err := ioutil.TempFile("", "xxxxtest") - Expect(err).ToNot(HaveOccurred()) - defer os.Remove(f.Name()) - - _ = ioutil.WriteFile(f.Name(), []byte(` - elemental: - install: - iso: "foo_bar" - `), os.ModePerm) - - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - c, err := ReadConfig(ctx, f.Name(), false) - Expect(err).ToNot(HaveOccurred()) - Expect(c.Elemental.Install.ISO).To(Equal("foo_bar")) - }) - It("reads containerImage, without contacting a registrationUrl server", func() { - f, err := ioutil.TempFile("", "xxxxtest") - Expect(err).ToNot(HaveOccurred()) - defer os.Remove(f.Name()) - - _ = ioutil.WriteFile(f.Name(), []byte(` - elemental: - install: - system-uri: "docker:docker/image:test" - `), os.ModePerm) - - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - c, err := ReadConfig(ctx, f.Name(), false) - Expect(err).ToNot(HaveOccurred()) - Expect(c.Elemental.Install.ISO).To(Equal("")) - Expect(c.Elemental.Install.SystemURI).To(Equal("docker:docker/image:test")) - }) - It("reads system-uri and registration url", func() { - - f, err := ioutil.TempFile("", "xxxxtest") - Expect(err).ToNot(HaveOccurred()) - defer os.Remove(f.Name()) - - _ = ioutil.WriteFile(f.Name(), []byte(` - elemental: - registration: - url: "foobar" - install: - system-uri: "docker:docker/image:test" - `), os.ModePerm) - - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - c, err := ReadConfig(ctx, f.Name(), false) - Expect(err).ToNot(HaveOccurred()) - Expect(c.Elemental.Install.SystemURI).To(Equal("docker:docker/image:test")) - }) - - It("reads iso", func() { - f, err := ioutil.TempFile("", "xxxxtest") - Expect(err).ToNot(HaveOccurred()) - defer os.Remove(f.Name()) - - _ = ioutil.WriteFile(f.Name(), []byte(` - elemental: - install: - iso: "foo_bar" - `), os.ModePerm) - - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - c, err := ReadConfig(ctx, f.Name(), false) - Expect(err).ToNot(HaveOccurred()) - Expect(c.Elemental.Install.ISO).To(Equal("foo_bar")) - }) - - It("reads install ssh-keys", Label("format"), func() { - f, err := ioutil.TempFile("", "xxxxtest") - Expect(err).ToNot(HaveOccurred()) - defer os.Remove(f.Name()) - - _ = ioutil.WriteFile(f.Name(), []byte(` - elemental: - install: - ssh-keys: - - foo - `), os.ModePerm) - - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - c, err := ReadConfig(ctx, f.Name(), false) - Expect(err).ToNot(HaveOccurred()) - Expect(c.Elemental.Install.SSHKeys).To(Equal([]string{"foo"})) - }) - }) - - Context("writing config", func() { - It("uses cloud-init format, but if data is present, takes over", func() { - c = Config{ - Data: map[string]interface{}{ - "users": []struct { - User string `json:"user"` - Pass string `json:"pass"` - }{{"foo", "Bar"}}, - }, - - Elemental: Elemental{ - Install: Install{ - Automatic: true, - Firmware: "efi", - SSHKeys: []string{"github:mudler"}, - ISO: "http://foo.bar", - }, Registration: Registration{ - URL: "Foo", - }, - }, - } - - f, err := ioutil.TempFile("", "xxxxtest") - Expect(err).ToNot(HaveOccurred()) - defer os.Remove(f.Name()) - - err = ToFile(c, f.Name()) - Expect(err).ToNot(HaveOccurred()) - - ff, _ := ioutil.ReadFile(f.Name()) - Expect(string(ff)).To(Equal("#cloud-config\nusers:\n- pass: Bar\n user: foo\n")) - }) - It("writes cloud-init files", func() { - c = Config{ - Elemental: Elemental{ - Install: Install{ - Automatic: true, - Firmware: "efi", - SSHKeys: []string{"github:mudler"}, - ISO: "http://foo.bar", - }, Registration: Registration{ - URL: "Foo", - }, - }, - } - - f, err := ioutil.TempFile("", "xxxxtest") - Expect(err).ToNot(HaveOccurred()) - defer os.Remove(f.Name()) - - err = ToFile(c, f.Name()) - Expect(err).ToNot(HaveOccurred()) - - ff, _ := ioutil.ReadFile(f.Name()) - Expect(string(ff)).To(Equal("#cloud-config\nelemental: {}\nssh_authorized_keys:\n- github:mudler\n")) - }) - It("reads iso_url by contacting a registrationUrl server", Serial, func() { - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - WSServer(ctx, data) - f, err := ioutil.TempFile("", "xxxxtest") - Expect(err).ToNot(HaveOccurred()) - defer os.Remove(f.Name()) - - _ = ioutil.WriteFile(f.Name(), []byte(` - elemental: - registration: - emulateTPM: true - noSMBIOS: true - emulatedTPMSeed: "5" - url: "http://127.0.0.1:9980/test" - `), os.ModePerm) - - c, err := ReadConfig(ctx, f.Name(), false) - Expect(err).ToNot(HaveOccurred()) - Expect(c.Elemental.Install.ISO).To(Equal("foo")) - - _ = ioutil.WriteFile(f.Name(), []byte(` - elemental: - registration: - emulateTPM: true - noSMBIOS: true - emulatedTPMSeed: "5" - url: "http://127.0.0.1:9980/test" - `), os.ModePerm) - - c, err = ReadConfig(ctx, f.Name(), false) - Expect(err).ToNot(HaveOccurred()) - Expect(c.Elemental.Install.ISO).To(Equal("foo")) - }) - It("reads system-uri by contacting a registrationUrl server", Serial, func() { - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // Override the install value on the data - value := map[string]string{"system-uri": "docker:test"} - values.PutValue(data, value, "elemental", "install") - - WSServer(ctx, data) - f, err := ioutil.TempFile("", "xxxxtest") - Expect(err).ToNot(HaveOccurred()) - defer os.Remove(f.Name()) - - _ = ioutil.WriteFile(f.Name(), []byte(` - elemental: - registration: - emulateTPM: true - noSMBIOS: true - emulatedTPMSeed: "5" - url: "http://127.0.0.1:9980/test" - `), os.ModePerm) - - c, err := ReadConfig(ctx, f.Name(), false) - Expect(err).ToNot(HaveOccurred()) - Expect(c.Elemental.Install.SystemURI).To(Equal("docker:test")) - - _ = ioutil.WriteFile(f.Name(), []byte(` - elemental: - registration: - emulateTPM: true - noSMBIOS: true - url: "http://127.0.0.1:9980/test" - `), os.ModePerm) - - c, err = ReadConfig(ctx, f.Name(), false) - Expect(err).ToNot(HaveOccurred()) - Expect(c.Elemental.Install.SystemURI).To(Equal("docker:test")) - }) - It("doesn't error out if isoUrl or containerImage are not provided", Serial, func() { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // Override the install value on the data - value := map[string]string{} - values.PutValue(data, value, "elemental", "install") - - WSServer(ctx, data) - f, err := ioutil.TempFile("", "xxxxtest") - Expect(err).ToNot(HaveOccurred()) - defer os.Remove(f.Name()) - - _ = ioutil.WriteFile(f.Name(), []byte(` - elemental: - registration: - emulateTPM: true - noSMBIOS: true - url: "http://127.0.0.1:9980/test" - `), os.ModePerm) - - c, err := ReadConfig(ctx, f.Name(), false) - Expect(err).ToNot(HaveOccurred()) - Expect(c.Elemental.Install.SystemURI).To(Equal("")) - Expect(c.Elemental.Install.ISO).To(Equal("")) - }) - })*/ -}) diff --git a/pkg/config/rename.go b/pkg/config/rename.go deleted file mode 100644 index 854a55612..000000000 --- a/pkg/config/rename.go +++ /dev/null @@ -1,67 +0,0 @@ -/* -Copyright © 2022 SUSE LLC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -/*import ( - "strings" - - "github.com/rancher/wrangler/pkg/data" - "github.com/rancher/wrangler/pkg/data/convert" - schemas2 "github.com/rancher/wrangler/pkg/schemas" - "github.com/rancher/wrangler/pkg/schemas/mappers" -) - -type FuzzyNames struct { - mappers.DefaultMapper - names map[string]string -} - -func (f *FuzzyNames) ToInternal(data data.Object) error { - for k, v := range data { - if newK, ok := f.names[strings.ToLower(k)]; ok && newK != k { - data[newK] = v - } - } - return nil -} - -func (f *FuzzyNames) addName(name, toName string) { - f.names[strings.ToLower(name)] = toName - f.names[convert.ToYAMLKey(name)] = toName - f.names[strings.ToLower(convert.ToYAMLKey(name))] = toName -} - -func (f *FuzzyNames) ModifySchema(schema *schemas2.Schema, schemas *schemas2.Schemas) error { - if f.names == nil { - f.names = map[string]string{} - } - - for name := range schema.ResourceFields { - if strings.HasSuffix(name, "s") && len(name) > 1 { - f.addName(name[:len(name)-1], name) - } - if strings.HasSuffix(name, "es") && len(name) > 2 { - f.addName(name[:len(name)-2], name) - } - f.addName(name, name) - } - - f.names["pass"] = "passphrase" - f.names["password"] = "passphrase" - - return nil -}*/ diff --git a/pkg/config/tftpget.go b/pkg/config/tftpget.go deleted file mode 100644 index e3e09a4bb..000000000 --- a/pkg/config/tftpget.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright © 2022 SUSE LLC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -/*import ( - "bytes" - "fmt" - "net" - "net/url" - - "gopkg.in/pin/tftp.v2" - "sigs.k8s.io/yaml" -) - -func tftpGet(tftpURL string) (map[string]interface{}, error) { - u, err := url.Parse(tftpURL) - if err != nil { - return nil, err - } - - host, _, err := net.SplitHostPort(u.Host) - if err != nil { - host = u.Host + ":69" - } - - fmt.Printf("Downloading config from host %s, file %s\n", host, u.Path) - client, err := tftp.NewClient(host) - if err != nil { - return nil, err - } - writerTo, err := client.Receive(u.Path, "octet") - if err != nil { - return nil, err - } - - buf := &bytes.Buffer{} - if _, err := writerTo.WriteTo(buf); err != nil { - return nil, err - } - - result := map[string]interface{}{} - return result, yaml.Unmarshal(buf.Bytes(), &result) -}*/ diff --git a/pkg/config/toenv.go b/pkg/config/toenv.go new file mode 100644 index 000000000..49f48ddb9 --- /dev/null +++ b/pkg/config/toenv.go @@ -0,0 +1,71 @@ +/* +Copyright © 2022 SUSE LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "fmt" + "strings" + + "github.com/rancher/wrangler/pkg/data/convert" +) + +// ToEnv converts the config into a slice env. +func ToEnv(cfg Config) ([]string, error) { + // Pass only the elemental values, as those are the ones used by the installer/cli + // Ignore the Data as that is cloud-config stuff + data, err := convert.EncodeToMap(&cfg.Elemental) + if err != nil { + return nil, err + } + + return mapToEnv("ELEMENTAL_", data), nil +} + +// it's a mapping of how config env option should be transliterated to the elemental CLI +var defaultOverrides = map[string]string{ + "ELEMENTAL_INSTALL_CONFIG_URL": "ELEMENTAL_INSTALL_CLOUD_INIT", + "ELEMENTAL_INSTALL_POWEROFF": "ELEMENTAL_POWEROFF", + "ELEMENTAL_INSTALL_REBOOT": "ELEMENTAL_REBOOT", + "ELEMENTAL_INSTALL_DEVICE": "ELEMENTAL_INSTALL_TARGET", + "ELEMENTAL_INSTALL_SYSTEM_URI": "ELEMENTAL_INSTALL_SYSTEM", + "ELEMENTAL_INSTALL_DEBUG": "ELEMENTAL_DEBUG", +} + +func envOverrides(keyName string) string { + for k, v := range defaultOverrides { + keyName = strings.ReplaceAll(keyName, k, v) + } + return keyName +} + +func mapToEnv(prefix string, data map[string]interface{}) []string { + var result []string + for k, v := range data { + keyName := strings.ToUpper(prefix + convert.ToYAMLKey(k)) + keyName = strings.ReplaceAll(keyName, "-", "_") + // Apply overrides needed to convert between configs types + keyName = envOverrides(keyName) + + if data, ok := v.(map[string]interface{}); ok { + subResult := mapToEnv(keyName+"_", data) + result = append(result, subResult...) + } else { + result = append(result, fmt.Sprintf("%s=%v", keyName, v)) + } + } + return result +} diff --git a/pkg/config/toenv_test.go b/pkg/config/toenv_test.go new file mode 100644 index 000000000..15e1d9488 --- /dev/null +++ b/pkg/config/toenv_test.go @@ -0,0 +1,163 @@ +/* +Copyright © 2022 SUSE LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config_test + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "time" + + gotpm "github.com/rancher-sandbox/go-tpm" + + "github.com/gorilla/websocket" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + . "github.com/rancher/elemental-operator/pkg/config" +) + +func writeRead(conn *websocket.Conn, input []byte) ([]byte, error) { + writer, err := conn.NextWriter(websocket.BinaryMessage) + if err != nil { + return nil, err + } + + if _, err := writer.Write(input); err != nil { + return nil, err + } + + if err := writer.Close(); err != nil { + return nil, err + } + + _, reader, err := conn.NextReader() + if err != nil { + return nil, err + } + + return ioutil.ReadAll(reader) +} + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +// Mimics a WS server which accepts TPM Bearer token +func WSServer(ctx context.Context, data map[string]interface{}) { + s := http.Server{ + Addr: "127.0.0.1:9980", + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + } + + m := http.NewServeMux() + m.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) { + conn, _ := upgrader.Upgrade(w, r, nil) // error ignored for sake of simplicity + + for { + + token := r.Header.Get("Authorization") + ek, at, err := gotpm.GetAttestationData(token) + if err != nil { + fmt.Println("error", err.Error()) + return + } + + secret, challenge, err := gotpm.GenerateChallenge(ek, at) + if err != nil { + fmt.Println("error", err.Error()) + return + } + + resp, _ := writeRead(conn, challenge) + + if err := gotpm.ValidateChallenge(secret, resp); err != nil { + fmt.Println(string(resp)) + fmt.Println("error validating challenge", err.Error()) + return + } + + writer, _ := conn.NextWriter(websocket.BinaryMessage) + _ = json.NewEncoder(writer).Encode(data) + } + }) + + s.Handler = m + + go s.ListenAndServe() + go func() { + <-ctx.Done() + _ = s.Shutdown(ctx) + }() +} + +var _ = Describe("os2 config unit tests", func() { + + Context("convert to environment configuration", func() { + var c Config + It("handle empty config", func() { + c = Config{} + e, err := ToEnv(c) + Expect(err).ToNot(HaveOccurred()) + Expect(e).To(BeEmpty()) + }) + It("converts to env slice installation parameters", func() { + c = Config{ + Data: map[string]interface{}{ + "random": "data", + }, + Elemental: Elemental{ + // Those settings below are tied to the + // elemental installer. + Install: Install{ + Device: "foob", + ConfigURL: "fooc", + Firmware: "efi", + ISO: "http://foo.bar", + NoFormat: true, + Debug: true, + PowerOff: true, + TTY: "foo", + SystemURI: "docker:container", + SSHKeys: []string{"github:mudler"}, + }, + }, + } + e, err := ToEnv(c) + Expect(err).ToNot(HaveOccurred()) + Expect(len(e)).To(Equal(10)) + Expect(e).To( + ContainElements( + "ELEMENTAL_INSTALL_SSH_KEYS=[github:mudler]", + "ELEMENTAL_INSTALL_TARGET=foob", + "ELEMENTAL_INSTALL_CLOUD_INIT=fooc", + "ELEMENTAL_INSTALL_FIRMWARE=efi", + "ELEMENTAL_INSTALL_ISO=http://foo.bar", + "ELEMENTAL_INSTALL_NO_FORMAT=true", + "ELEMENTAL_DEBUG=true", + "ELEMENTAL_POWEROFF=true", + "ELEMENTAL_INSTALL_TTY=foo", + "ELEMENTAL_INSTALL_SYSTEM=docker:container", + ), + ) + }) + }) +}) diff --git a/pkg/config/write.go b/pkg/config/write.go deleted file mode 100644 index 72f48bb4f..000000000 --- a/pkg/config/write.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -Copyright © 2022 SUSE LLC - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package config - -/*import ( - "github.com/rancher/wrangler/pkg/data/convert" - "sigs.k8s.io/yaml" -) - -func PrintInstall(cfg Config) ([]byte, error) { - if cfg.Elemental.Install.Password != "" { - cfg.Elemental.Install.Password = "******" - } - data, err := convert.EncodeToMap(cfg.Elemental.Install) - if err != nil { - return nil, err - } - - toYAMLKeys(data) - return yaml.Marshal(data) -} - -func toYAMLKeys(data map[string]interface{}) { - for k, v := range data { - if sub, ok := v.(map[string]interface{}); ok { - toYAMLKeys(sub) - } - newK := convert.ToYAMLKey(k) - if newK != k { - delete(data, k) - data[newK] = v - } - } -}*/