From 14595c95ad11807125337b9859a8e9002bf8a559 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Fri, 13 Oct 2023 11:16:41 +0200 Subject: [PATCH] fix TestLoadSSH* Signed-off-by: Nicolas De Loof --- loader/loader.go | 73 ----------------------------------------- loader/merge_test.go | 73 +---------------------------------------- transform/build.go | 2 +- transform/canonical.go | 1 + transform/ssh.go | 45 +++++++++++++++++++++++++ transform/ssh_test.go | 1 + types/ssh.go | 74 ++++++++++++++++++++++++++++++++++++++++++ types/types.go | 33 ------------------- 8 files changed, 123 insertions(+), 179 deletions(-) create mode 100644 transform/ssh.go create mode 100644 transform/ssh_test.go create mode 100644 types/ssh.go diff --git a/loader/loader.go b/loader/loader.go index 20b08a7b6..7aac6c016 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -681,15 +681,10 @@ func createTransformHook(additionalTransformers ...Transformer) mapstructure.Dec reflect.TypeOf(types.ServiceSecretConfig{}): transformFileReferenceConfig, reflect.TypeOf(types.ServiceConfigObjConfig{}): transformFileReferenceConfig, reflect.TypeOf(map[string]*types.ServiceNetworkConfig{}): transformServiceNetworkMap, - reflect.TypeOf(types.Mapping{}): transformMappingOrListFunc("=", false), - reflect.TypeOf(types.MappingWithEquals{}): transformMappingOrListFunc("=", true), - reflect.TypeOf(types.MappingWithColon{}): transformMappingOrListFunc(":", false), - reflect.TypeOf(types.HostsList{}): transformMappingOrListFunc(":", false), reflect.TypeOf(types.ServiceVolumeConfig{}): transformServiceVolumeConfig, reflect.TypeOf(types.BuildConfig{}): transformBuildConfig, reflect.TypeOf(types.DependsOnConfig{}): transformDependsOnConfig, reflect.TypeOf(types.ExtendsConfig{}): transformExtendsConfig, - reflect.TypeOf(types.SSHConfig{}): transformSSHConfig, reflect.TypeOf(types.IncludeConfig{}): transformIncludeConfig, } @@ -1221,74 +1216,6 @@ var transformServiceNetworkMap TransformerFunc = func(value interface{}) (interf return value, nil } -var transformSSHConfig TransformerFunc = func(data interface{}) (interface{}, error) { - switch value := data.(type) { - case map[string]interface{}: - var result []types.SSHKey - for key, val := range value { - if val == nil { - val = "" - } - result = append(result, types.SSHKey{ID: key, Path: val.(string)}) - } - return result, nil - case []interface{}: - var result []types.SSHKey - for _, v := range value { - key, val := transformValueToMapEntry(v.(string), "=", false) - result = append(result, types.SSHKey{ID: key, Path: val.(string)}) - } - return result, nil - case string: - return ParseShortSSHSyntax(value) - } - return nil, errors.Errorf("expected a sting, map or a list, got %T: %#v", data, data) -} - -// ParseShortSSHSyntax parse short syntax for SSH authentications -func ParseShortSSHSyntax(value string) ([]types.SSHKey, error) { - if value == "" { - value = "default" - } - key, val := transformValueToMapEntry(value, "=", false) - result := []types.SSHKey{{ID: key, Path: val.(string)}} - return result, nil -} - -func transformMappingOrListFunc(sep string, allowNil bool) TransformerFunc { - return func(data interface{}) (interface{}, error) { - return transformMappingOrList(data, sep, allowNil) - } -} - -func transformMappingOrList(mappingOrList interface{}, sep string, allowNil bool) (interface{}, error) { - switch value := mappingOrList.(type) { - case map[string]interface{}: - return toMapStringString(value, allowNil), nil - case []interface{}: - result := make(map[string]interface{}) - for _, value := range value { - key, val := transformValueToMapEntry(value.(string), sep, allowNil) - result[key] = val - } - return result, nil - } - return nil, errors.Errorf("expected a map or a list, got %T: %#v", mappingOrList, mappingOrList) -} - -func transformValueToMapEntry(value string, separator string, allowNil bool) (string, interface{}) { - parts := strings.SplitN(value, separator, 2) - key := parts[0] - switch { - case len(parts) == 1 && allowNil: - return key, nil - case len(parts) == 1 && !allowNil: - return key, "" - default: - return key, parts[1] - } -} - func toMapStringString(value map[string]interface{}, allowNil bool) map[string]interface{} { output := make(map[string]interface{}) for key, value := range value { diff --git a/loader/merge_test.go b/loader/merge_test.go index 02584cd11..78e8359a1 100644 --- a/loader/merge_test.go +++ b/loader/merge_test.go @@ -965,78 +965,7 @@ services: }, config.Services) } -// Issue#972 -func TestLoadMultipleNetworks(t *testing.T) { - base := map[string]interface{}{ - "services": map[string]interface{}{ - "foo": map[string]interface{}{ - "image": "baz", - }, - }, - "volumes": map[string]interface{}{}, - "networks": map[string]interface{}{ - "hostnet": map[string]interface{}{ - "driver": "overlay", - "ipam": map[string]interface{}{ - "driver": "default", - "config": []interface{}{ - map[string]interface{}{ - "subnet": "10.0.0.0/20", - }, - }, - }, - }, - }, - "secrets": map[string]interface{}{}, - "configs": map[string]interface{}{}, - } - override := map[string]interface{}{ - "services": map[string]interface{}{}, - "volumes": map[string]interface{}{}, - "networks": map[string]interface{}{ - "hostnet": map[string]interface{}{ - "external": map[string]interface{}{ - "name": "host", - }, - }, - }, - "secrets": map[string]interface{}{}, - "configs": map[string]interface{}{}, - } - configDetails := types.ConfigDetails{ - ConfigFiles: []types.ConfigFile{ - {Filename: "base.yml", Config: base}, - {Filename: "override.yml", Config: override}, - }, - } - config, err := loadTestProject(configDetails) - assert.NilError(t, err) - assert.DeepEqual(t, &types.Project{ - Name: "", - WorkingDir: "", - Services: []types.ServiceConfig{ - { - Name: "foo", - Image: "baz", - Environment: types.MappingWithEquals{}, - Scale: 1, - }}, - Networks: map[string]types.NetworkConfig{ - "hostnet": { - Name: "host", - External: types.External{ - External: true, - }, - }, - }, - Volumes: types.Volumes{}, - Secrets: types.Secrets{}, - Configs: types.Configs{}, - Extensions: types.Extensions{}, - }, config) -} - -func TestMergeUlimitsConfig(t *testing.T) { +func TestMergUlimitsConfig(t *testing.T) { specials := &specials{ m: map[reflect.Type]func(dst, src reflect.Value) error{ reflect.TypeOf(&types.UlimitsConfig{}): mergeUlimitsConfig, diff --git a/transform/build.go b/transform/build.go index 268691a0b..f00ed2281 100644 --- a/transform/build.go +++ b/transform/build.go @@ -27,7 +27,7 @@ func transformBuild(data any, p tree.Path) (any, error) { if _, ok := v["context"]; !ok { v["context"] = "." // TODO(ndeloof) maybe we miss an explicit "set-defaults" loading phase } - return v, nil + return transformMapping(v, p) case string: return map[string]any{ "context": v, diff --git a/transform/canonical.go b/transform/canonical.go index ace2a6185..3f779776c 100644 --- a/transform/canonical.go +++ b/transform/canonical.go @@ -31,6 +31,7 @@ func init() { transformers["services.*.volumes.*"] = transformVolumeMount transformers["services.*.ports"] = transformPorts transformers["services.*.build"] = transformBuild + transformers["services.*.build.ssh"] = transformSSH transformers["services.*.ulimits.*"] = transformUlimits transformers["volumes.*"] = transformMaybeExternal transformers["networks.*"] = transformMaybeExternal diff --git a/transform/ssh.go b/transform/ssh.go new file mode 100644 index 000000000..f900d10ad --- /dev/null +++ b/transform/ssh.go @@ -0,0 +1,45 @@ +/* + Copyright 2020 The Compose Specification Authors. + + 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 transform + +import ( + "fmt" + + "github.com/compose-spec/compose-go/tree" + "github.com/pkg/errors" +) + +func transformSSH(data any, p tree.Path) (any, error) { + switch v := data.(type) { + case map[string]any: + result := make([]any, len(v)) + i := 0 + for k, e := range v { + if e == nil { + result[i] = k + } else { + result[i] = fmt.Sprintf("%s=%s", k, e) + } + i++ + } + return result, nil + case []any: + return data, nil + default: + return data, errors.Errorf("invalid type %T for ssh", v) + } +} diff --git a/transform/ssh_test.go b/transform/ssh_test.go new file mode 100644 index 000000000..5796f91c6 --- /dev/null +++ b/transform/ssh_test.go @@ -0,0 +1 @@ +package transform diff --git a/types/ssh.go b/types/ssh.go new file mode 100644 index 000000000..39783d25f --- /dev/null +++ b/types/ssh.go @@ -0,0 +1,74 @@ +/* + Copyright 2020 The Compose Specification Authors. + + 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 types + +import ( + "fmt" + "strings" +) + +type SSHKey struct { + ID string `yaml:"id,omitempty" json:"id,omitempty"` + Path string `path:"path,omitempty" json:"path,omitempty"` +} + +// SSHConfig is a mapping type for SSH build config +type SSHConfig []SSHKey + +func (s SSHConfig) Get(id string) (string, error) { + for _, sshKey := range s { + if sshKey.ID == id { + return sshKey.Path, nil + } + } + return "", fmt.Errorf("ID %s not found in SSH keys", id) +} + +// MarshalYAML makes SSHKey implement yaml.Marshaller +func (s SSHKey) MarshalYAML() (interface{}, error) { + if s.Path == "" { + return s.ID, nil + } + return fmt.Sprintf("%s=%s", s.ID, s.Path), nil +} + +// MarshalJSON makes SSHKey implement json.Marshaller +func (s SSHKey) MarshalJSON() ([]byte, error) { + if s.Path == "" { + return []byte(fmt.Sprintf(`%q`, s.ID)), nil + } + return []byte(fmt.Sprintf(`%q=%s`, s.ID, s.Path)), nil +} + +func (s *SSHKey) DecodeMapstructure(value interface{}) error { + v, ok := value.(string) + if !ok { + return fmt.Errorf("invalid ssh key type %T", value) + } + id, path, ok := strings.Cut(v, "=") + key := SSHKey{ID: id} + if !ok { + if id != "default" { + return fmt.Errorf("invalid ssh key syntax %q", value) + } + } + if path != "" { + key.Path = path + } + *s = key + return nil +} diff --git a/types/types.go b/types/types.go index 84a9b5254..e66f6550a 100644 --- a/types/types.go +++ b/types/types.go @@ -328,39 +328,6 @@ type ThrottleDevice struct { Extensions Extensions `yaml:"#extensions,inline" json:"-"` } -type SSHKey struct { - ID string - Path string -} - -// SSHConfig is a mapping type for SSH build config -type SSHConfig []SSHKey - -func (s SSHConfig) Get(id string) (string, error) { - for _, sshKey := range s { - if sshKey.ID == id { - return sshKey.Path, nil - } - } - return "", fmt.Errorf("ID %s not found in SSH keys", id) -} - -// MarshalYAML makes SSHKey implement yaml.Marshaller -func (s SSHKey) MarshalYAML() (interface{}, error) { - if s.Path == "" { - return s.ID, nil - } - return fmt.Sprintf("%s: %s", s.ID, s.Path), nil -} - -// MarshalJSON makes SSHKey implement json.Marshaller -func (s SSHKey) MarshalJSON() ([]byte, error) { - if s.Path == "" { - return []byte(fmt.Sprintf(`%q`, s.ID)), nil - } - return []byte(fmt.Sprintf(`%q: %s`, s.ID, s.Path)), nil -} - // MappingWithColon is a mapping type that can be converted from a list of // 'key: value' strings type MappingWithColon map[string]string