From 97c114cb21a98a571d9e2c0cd1d61153dd3235db Mon Sep 17 00:00:00 2001 From: Dmitry Ukov Date: Wed, 5 Feb 2020 10:41:03 +0400 Subject: [PATCH] Switch remote direct to airshipctl configuration Related #7 Change-Id: I015b178895359ea468748eb72e367b4ff56026bb --- cmd/bootstrap/bootstrap_remotedirect.go | 53 +-------- .../remotedirect-cmd-with-help.golden | 6 +- pkg/remote/redfish/utils.go | 3 +- pkg/remote/remote_direct.go | 108 +++++++++++++----- pkg/remote/remote_direct_test.go | 94 +++++++-------- pkg/remote/testdata/base/baremetal.yaml | 49 ++++++++ pkg/remote/testdata/base/kustomization.yaml | 2 + pkg/remote/testdata/emptyurl/baremetal.yaml | 49 ++++++++ .../testdata/emptyurl/kustomization.yaml | 2 + 9 files changed, 228 insertions(+), 138 deletions(-) create mode 100644 pkg/remote/testdata/base/baremetal.yaml create mode 100644 pkg/remote/testdata/base/kustomization.yaml create mode 100644 pkg/remote/testdata/emptyurl/baremetal.yaml create mode 100644 pkg/remote/testdata/emptyurl/kustomization.yaml diff --git a/cmd/bootstrap/bootstrap_remotedirect.go b/cmd/bootstrap/bootstrap_remotedirect.go index 690643ed0..4b96a84ba 100644 --- a/cmd/bootstrap/bootstrap_remotedirect.go +++ b/cmd/bootstrap/bootstrap_remotedirect.go @@ -4,69 +4,18 @@ import ( "github.com/spf13/cobra" "opendev.org/airship/airshipctl/pkg/environment" - alog "opendev.org/airship/airshipctl/pkg/log" "opendev.org/airship/airshipctl/pkg/remote" ) -// RemoteDirect settings for remotedirect command -type RemoteDirectSettings struct { - *environment.AirshipCTLSettings - - RemoteConfig remote.RemoteDirectConfig -} - -// InitFlags adds flags and their default settings for Remote Direct command -func (cmdSetting *RemoteDirectSettings) InitFlags(cmd *cobra.Command) { - flags := cmd.Flags() - - // TODO: Remove CLI flags after reading configuration from config documents - // ======================================================================== - flags.StringVar(&cmdSetting.RemoteConfig.RemoteURL, - "remote-url", - "http://localhost:8000", - "[Temporary. Will be removed] Remote Redfish/Smash URL") - - flags.StringVar(&cmdSetting.RemoteConfig.EphemeralNodeId, - "eph-node-id", - "", - "[Temporary. Will be removed] Ephemeral Node ID") - - flags.StringVar(&cmdSetting.RemoteConfig.IsoPath, - "iso-path", - "", - "[Temporary. Will be removed] Remote ISO path for ephemeral node") - - flags.StringVar(&cmdSetting.RemoteConfig.RemoteType, - "remote-type", - "redfish", - "Remote type e.g. redfish, smash etc.") - - err := cmd.MarkFlagRequired("eph-node-id") - if err != nil { - alog.Fatal(err) - } - - err = cmd.MarkFlagRequired("iso-path") - if err != nil { - alog.Fatal(err) - } -} - // New Bootstrap remote direct subcommand func NewRemoteDirectCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Command { - settings := &RemoteDirectSettings{AirshipCTLSettings: rootSettings} remoteDirect := &cobra.Command{ Use: "remotedirect", Short: "Bootstrap ephemeral node", RunE: func(cmd *cobra.Command, args []string) error { - /* TODO: Get config from settings.GetCurrentContext() and remove cli arguments */ - - /* Trigger remotedirect based on remote type */ - return remote.DoRemoteDirect(settings.RemoteConfig) + return remote.DoRemoteDirect(rootSettings) }, } - settings.InitFlags(remoteDirect) - return remoteDirect } diff --git a/cmd/bootstrap/testdata/TestRemoteDirectGoldenOutput/remotedirect-cmd-with-help.golden b/cmd/bootstrap/testdata/TestRemoteDirectGoldenOutput/remotedirect-cmd-with-help.golden index 70dbce427..3a32d2c27 100644 --- a/cmd/bootstrap/testdata/TestRemoteDirectGoldenOutput/remotedirect-cmd-with-help.golden +++ b/cmd/bootstrap/testdata/TestRemoteDirectGoldenOutput/remotedirect-cmd-with-help.golden @@ -4,8 +4,4 @@ Usage: remotedirect [flags] Flags: - --eph-node-id string [Temporary. Will be removed] Ephemeral Node ID - -h, --help help for remotedirect - --iso-path string [Temporary. Will be removed] Remote ISO path for ephemeral node - --remote-type string Remote type e.g. redfish, smash etc. (default "redfish") - --remote-url string [Temporary. Will be removed] Remote Redfish/Smash URL (default "http://localhost:8000") + -h, --help help for remotedirect diff --git a/pkg/remote/redfish/utils.go b/pkg/remote/redfish/utils.go index 967cdecc7..e8b210755 100644 --- a/pkg/remote/redfish/utils.go +++ b/pkg/remote/redfish/utils.go @@ -13,7 +13,8 @@ import ( ) const ( - SystemRebootDelay = 2 * time.Second + SystemRebootDelay = 2 * time.Second + RedfishURLSchemeSeparator = "+" ) // Redfish Id ref is a URI which contains resource Id diff --git a/pkg/remote/remote_direct.go b/pkg/remote/remote_direct.go index 876b7226e..b31326e28 100644 --- a/pkg/remote/remote_direct.go +++ b/pkg/remote/remote_direct.go @@ -2,57 +2,58 @@ package remote import ( "context" + "fmt" + "net/url" + "strings" + "opendev.org/airship/airshipctl/pkg/config" + "opendev.org/airship/airshipctl/pkg/document" + "opendev.org/airship/airshipctl/pkg/environment" alog "opendev.org/airship/airshipctl/pkg/log" "opendev.org/airship/airshipctl/pkg/remote/redfish" + + "sigs.k8s.io/kustomize/v3/pkg/fs" + "sigs.k8s.io/kustomize/v3/pkg/gvk" + "sigs.k8s.io/kustomize/v3/pkg/types" ) const ( AirshipRemoteTypeRedfish string = "redfish" AirshipRemoteTypeSmash string = "smash" + AirshipHostKind string = "BareMetalHost" ) -// This structure defines the common remote direct config -// for all remote types. -type RemoteDirectConfig struct { - // remote type - RemoteType string - - // remote URL - RemoteURL string - - // ephemeral Host ID - EphemeralNodeId string - - // ISO URL - IsoPath string - - // TODO: Ephemeral Node IP - - // TODO: kubeconfig (in object form or raw yaml?) for ephemeral node validation. - - // TODO: More fields can be added on need basis -} - // Interface to be implemented by remoteDirect implementation type RemoteDirectClient interface { DoRemoteDirect() error } // Get remotedirect client based on config -func getRemoteDirectClient(remoteConfig RemoteDirectConfig) (RemoteDirectClient, error) { +func getRemoteDirectClient(remoteConfig *config.RemoteDirect, remoteURL string) (RemoteDirectClient, error) { var client RemoteDirectClient - var err error - switch remoteConfig.RemoteType { case AirshipRemoteTypeRedfish: alog.Debug("Remote type redfish") + rfURL, err := url.Parse(remoteURL) + if err != nil { + return nil, err + } + + baseURL := fmt.Sprintf("%s://%s", rfURL.Scheme, rfURL.Host) + schemeSplit := strings.Split(rfURL.Scheme, redfish.RedfishURLSchemeSeparator) + if len(schemeSplit) > 1 { + baseURL = fmt.Sprintf("%s://%s", schemeSplit[len(schemeSplit)-1], rfURL.Host) + } + + urlPath := strings.Split(rfURL.Path, "/") + nodeID := urlPath[len(urlPath)-1] + client, err = redfish.NewRedfishRemoteDirectClient( context.Background(), - remoteConfig.RemoteURL, - remoteConfig.EphemeralNodeId, - remoteConfig.IsoPath) + baseURL, + nodeID, + remoteConfig.IsoURL) if err != nil { alog.Debugf("redfish remotedirect client creation failed") return nil, err @@ -65,9 +66,56 @@ func getRemoteDirectClient(remoteConfig RemoteDirectConfig) (RemoteDirectClient, return client, nil } +func getRemoteDirectConfig(settings *environment.AirshipCTLSettings) (*config.RemoteDirect, string, error) { + cfg := settings.Config() + manifest, err := cfg.CurrentContextManifest() + if err != nil { + return nil, "", err + } + bootstrapSettings, err := cfg.CurrentContextBootstrapInfo() + if err != nil { + return nil, "", err + } + + // TODO (dukov) replace with the appropriate function once it's available + // in document module + docBundle, err := document.NewBundle(fs.MakeRealFS(), manifest.TargetPath, "") + if err != nil { + return nil, "", err + } + + filter := types.Selector{ + Gvk: gvk.FromKind(AirshipHostKind), + LabelSelector: document.EphemeralClusterMarker, + } + docs, err := docBundle.Select(filter) + if err != nil { + return nil, "", err + } + if len(docs) == 0 { + return nil, "", document.ErrDocNotFound{ + Annotation: document.EphemeralClusterMarker, + Kind: AirshipHostKind, + } + } + + // NOTE If filter returned more than one document chose first + remoteURL, err := docs[0].GetString("spec.bmc.address") + if err != nil { + return nil, "", err + } + + return bootstrapSettings.RemoteDirect, remoteURL, nil +} + // Top level function to execute remote direct based on remote type -func DoRemoteDirect(remoteConfig RemoteDirectConfig) error { - client, err := getRemoteDirectClient(remoteConfig) +func DoRemoteDirect(settings *environment.AirshipCTLSettings) error { + remoteConfig, remoteURL, err := getRemoteDirectConfig(settings) + if err != nil { + return err + } + + client, err := getRemoteDirectClient(remoteConfig, remoteURL) if err != nil { return err } diff --git a/pkg/remote/remote_direct_test.go b/pkg/remote/remote_direct_test.go index 0a47c2849..f820c0138 100644 --- a/pkg/remote/remote_direct_test.go +++ b/pkg/remote/remote_direct_test.go @@ -4,75 +4,69 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "opendev.org/airship/airshipctl/pkg/config" + "opendev.org/airship/airshipctl/pkg/environment" "opendev.org/airship/airshipctl/pkg/remote/redfish" ) -func TestUnknownRemoteType(t *testing.T) { - rdCfg := RemoteDirectConfig{ - RemoteType: "new-remote", - RemoteURL: "http://localhost:8000", - EphemeralNodeId: "test-node", - IsoPath: "/test.iso", - } +func initSettings(t *testing.T, rd *config.RemoteDirect, testdata string) *environment.AirshipCTLSettings { + settings := &environment.AirshipCTLSettings{} + settings.SetConfig(config.DummyConfig()) + bi, err := settings.Config().CurrentContextBootstrapInfo() + require.NoError(t, err) + bi.RemoteDirect = rd + cm, err := settings.Config().CurrentContextManifest() + require.NoError(t, err) + cm.TargetPath = "testdata/" + testdata + return settings +} - err := DoRemoteDirect(rdCfg) +func TestUnknownRemoteType(t *testing.T) { + s := initSettings( + t, + &config.RemoteDirect{ + RemoteType: "new-remote", + IsoURL: "/test.iso", + }, + "base", + ) + + err := DoRemoteDirect(s) _, ok := err.(*RemoteDirectError) assert.True(t, ok) } -func TestRedfishRemoteDirectWithBogusConfig(t *testing.T) { - rdCfg := RemoteDirectConfig{ - RemoteType: "redfish", - RemoteURL: "http://nolocalhost:8888", - EphemeralNodeId: "test-node", - IsoPath: "/test.iso", - } - - err := DoRemoteDirect(rdCfg) - - _, ok := err.(*redfish.RedfishClientError) - assert.True(t, ok) -} - func TestRedfishRemoteDirectWithEmptyURL(t *testing.T) { - rdCfg := RemoteDirectConfig{ - RemoteType: "redfish", - RemoteURL: "", - EphemeralNodeId: "test-node", - IsoPath: "/test.iso", - } + s := initSettings( + t, + &config.RemoteDirect{ + RemoteType: "redfish", + IsoURL: "/test.iso", + }, + "emptyurl", + ) - err := DoRemoteDirect(rdCfg) - - _, ok := err.(*redfish.RedfishConfigError) - assert.True(t, ok) -} - -func TestRedfishRemoteDirectWithEmptyNodeID(t *testing.T) { - rdCfg := RemoteDirectConfig{ - RemoteType: "redfish", - RemoteURL: "http://nolocalhost:8888", - EphemeralNodeId: "", - IsoPath: "/test.iso", - } - - err := DoRemoteDirect(rdCfg) + err := DoRemoteDirect(s) _, ok := err.(*redfish.RedfishConfigError) assert.True(t, ok) } func TestRedfishRemoteDirectWithEmptyIsoPath(t *testing.T) { - rdCfg := RemoteDirectConfig{ - RemoteType: "redfish", - RemoteURL: "http://nolocalhost:8888", - EphemeralNodeId: "123", - IsoPath: "", - } + s := initSettings( + t, + &config.RemoteDirect{ + RemoteType: "redfish", + IsoURL: "", + }, + "base", + ) - err := DoRemoteDirect(rdCfg) + err := DoRemoteDirect(s) _, ok := err.(*redfish.RedfishConfigError) assert.True(t, ok) diff --git a/pkg/remote/testdata/base/baremetal.yaml b/pkg/remote/testdata/base/baremetal.yaml new file mode 100644 index 000000000..d74ec0776 --- /dev/null +++ b/pkg/remote/testdata/base/baremetal.yaml @@ -0,0 +1,49 @@ +--- +apiVersion: metal3.io/v1alpha1 +kind: BareMetalHost +metadata: + labels: + airshipit.org/clustertype: ephemeral + name: master-0 +spec: + online: true + bootMACAddress: 00:3b:8b:0c:ec:8b + bmc: + address: redfish+http://nolocalhost:8888/redfish/v1/Systems/test-node + credentialsName: master-0-bmc-secret +--- +apiVersion: v1 +kind: Secret +metadata: + annotations: + airshipit.org/clustertype: ephemeral + name: master-0-bmc-secret +type: Opaque +data: + username: YWRtaW4= + password: cGFzc3dvcmQ= +--- +apiVersion: metal3.io/v1alpha1 +kind: BareMetalHost +metadata: + annotations: + airshipit.org/clustertype: target + name: master-1 +spec: + online: true + bootMACAddress: 01:3b:8b:0c:ec:8b + bmc: + address: ipmi://192.168.111.2:6230 + credentialsName: master-1-bmc-secret +--- +apiVersion: v1 +kind: Secret +metadata: + annotations: + airshipit.org/clustertype: target + name: master-1-bmc-secret +type: Opaque +data: + username: YWRtaW4= + password: cGFzc3dvcmQ= +... \ No newline at end of file diff --git a/pkg/remote/testdata/base/kustomization.yaml b/pkg/remote/testdata/base/kustomization.yaml new file mode 100644 index 000000000..6177200c1 --- /dev/null +++ b/pkg/remote/testdata/base/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - baremetal.yaml \ No newline at end of file diff --git a/pkg/remote/testdata/emptyurl/baremetal.yaml b/pkg/remote/testdata/emptyurl/baremetal.yaml new file mode 100644 index 000000000..6ca921572 --- /dev/null +++ b/pkg/remote/testdata/emptyurl/baremetal.yaml @@ -0,0 +1,49 @@ +--- +apiVersion: metal3.io/v1alpha1 +kind: BareMetalHost +metadata: + labels: + airshipit.org/clustertype: ephemeral + name: master-0 +spec: + online: true + bootMACAddress: 00:3b:8b:0c:ec:8b + bmc: + address: "" + credentialsName: master-0-bmc-secret +--- +apiVersion: v1 +kind: Secret +metadata: + annotations: + airshipit.org/clustertype: ephemeral + name: master-0-bmc-secret +type: Opaque +data: + username: YWRtaW4= + password: cGFzc3dvcmQ= +--- +apiVersion: metal3.io/v1alpha1 +kind: BareMetalHost +metadata: + annotations: + airshipit.org/clustertype: target + name: master-1 +spec: + online: true + bootMACAddress: 01:3b:8b:0c:ec:8b + bmc: + address: ipmi://192.168.111.2:6230 + credentialsName: master-1-bmc-secret +--- +apiVersion: v1 +kind: Secret +metadata: + annotations: + airshipit.org/clustertype: target + name: master-1-bmc-secret +type: Opaque +data: + username: YWRtaW4= + password: cGFzc3dvcmQ= +... diff --git a/pkg/remote/testdata/emptyurl/kustomization.yaml b/pkg/remote/testdata/emptyurl/kustomization.yaml new file mode 100644 index 000000000..6177200c1 --- /dev/null +++ b/pkg/remote/testdata/emptyurl/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - baremetal.yaml \ No newline at end of file