Switch remote direct to airshipctl configuration
Related #7 Change-Id: I015b178895359ea468748eb72e367b4ff56026bb
This commit is contained in:
parent
8b86e4135f
commit
97c114cb21
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
49
pkg/remote/testdata/base/baremetal.yaml
vendored
Normal file
49
pkg/remote/testdata/base/baremetal.yaml
vendored
Normal file
@ -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=
|
||||
...
|
2
pkg/remote/testdata/base/kustomization.yaml
vendored
Normal file
2
pkg/remote/testdata/base/kustomization.yaml
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
resources:
|
||||
- baremetal.yaml
|
49
pkg/remote/testdata/emptyurl/baremetal.yaml
vendored
Normal file
49
pkg/remote/testdata/emptyurl/baremetal.yaml
vendored
Normal file
@ -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=
|
||||
...
|
2
pkg/remote/testdata/emptyurl/kustomization.yaml
vendored
Normal file
2
pkg/remote/testdata/emptyurl/kustomization.yaml
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
resources:
|
||||
- baremetal.yaml
|
Loading…
Reference in New Issue
Block a user