Move bootstrapInfo config section to manifests

This is a part of two activities
  * Refactor config and store all parameters to document model
  * Implement everything as phases (which are supposed to be purely document driven)

 This patch removes bootstrapInfo section from the airshipctl config and
 makes these to commands
  * airshipctl baremetal isogen
  * airthipctl baremetal remotedirect
 to take necessary parameters from documents

 We introduce two airship API kinds ImageGenerator and RemoteDirect.
 Instead of storing config parameters in cm/secrets we use these two
 API objects.

Relates-To: #246
Closes: #254
Change-Id: I42903c45dce1c73da184c07277fec76fd88c700f
This commit is contained in:
Vladimir Kozhukalov 2020-05-22 13:57:41 +03:00
parent 61633d30fc
commit e9c8425c30
47 changed files with 747 additions and 339 deletions

View File

@ -1,6 +1,5 @@
Cluster: clusterBar
ephemeral:
bootstrapInfo: ""
clusterKubeconf: clusterBar_ephemeral
managementConfiguration: ""
@ -10,7 +9,6 @@ server: ""
Cluster: clusterBar
target:
bootstrapInfo: ""
clusterKubeconf: clusterBar_target
managementConfiguration: ""
@ -20,7 +18,6 @@ server: ""
Cluster: clusterBaz
ephemeral:
bootstrapInfo: ""
clusterKubeconf: clusterBaz_ephemeral
managementConfiguration: ""
@ -30,7 +27,6 @@ server: ""
Cluster: clusterBaz
target:
bootstrapInfo: ""
clusterKubeconf: clusterBaz_target
managementConfiguration: ""
@ -40,7 +36,6 @@ server: ""
Cluster: clusterFoo
ephemeral:
bootstrapInfo: ""
clusterKubeconf: clusterFoo_ephemeral
managementConfiguration: ""
@ -50,7 +45,6 @@ server: ""
Cluster: clusterFoo
target:
bootstrapInfo: ""
clusterKubeconf: clusterFoo_target
managementConfiguration: ""

View File

@ -1,6 +1,5 @@
Cluster: clusterBar
ephemeral:
bootstrapInfo: ""
clusterKubeconf: clusterBar_ephemeral
managementConfiguration: ""
@ -10,7 +9,6 @@ server: ""
Cluster: clusterBar
target:
bootstrapInfo: ""
clusterKubeconf: clusterBar_target
managementConfiguration: ""
@ -20,7 +18,6 @@ server: ""
Cluster: clusterBaz
ephemeral:
bootstrapInfo: ""
clusterKubeconf: clusterBaz_ephemeral
managementConfiguration: ""
@ -30,7 +27,6 @@ server: ""
Cluster: clusterBaz
target:
bootstrapInfo: ""
clusterKubeconf: clusterBaz_target
managementConfiguration: ""
@ -40,7 +36,6 @@ server: ""
Cluster: clusterFoo
ephemeral:
bootstrapInfo: ""
clusterKubeconf: clusterFoo_ephemeral
managementConfiguration: ""
@ -50,7 +45,6 @@ server: ""
Cluster: clusterFoo
target:
bootstrapInfo: ""
clusterKubeconf: clusterFoo_target
managementConfiguration: ""

View File

@ -1,6 +1,5 @@
Cluster: clusterFoo
ephemeral:
bootstrapInfo: ""
clusterKubeconf: clusterFoo_ephemeral
managementConfiguration: ""

View File

@ -1,6 +1,5 @@
Cluster: clusterBar
ephemeral:
bootstrapInfo: ""
clusterKubeconf: clusterBar_ephemeral
managementConfiguration: ""
@ -10,7 +9,6 @@ server: ""
Cluster: clusterBar
target:
bootstrapInfo: ""
clusterKubeconf: clusterBar_target
managementConfiguration: ""
@ -20,7 +18,6 @@ server: ""
Cluster: clusterBaz
ephemeral:
bootstrapInfo: ""
clusterKubeconf: clusterBaz_ephemeral
managementConfiguration: ""
@ -30,7 +27,6 @@ server: ""
Cluster: clusterBaz
target:
bootstrapInfo: ""
clusterKubeconf: clusterBaz_target
managementConfiguration: ""
@ -40,7 +36,6 @@ server: ""
Cluster: clusterFoo
ephemeral:
bootstrapInfo: ""
clusterKubeconf: clusterFoo_ephemeral
managementConfiguration: ""
@ -50,7 +45,6 @@ server: ""
Cluster: clusterFoo
target:
bootstrapInfo: ""
clusterKubeconf: clusterFoo_target
managementConfiguration: ""

View File

@ -1,6 +1,5 @@
Cluster: clusterBar
ephemeral:
bootstrapInfo: ""
clusterKubeconf: clusterBar_ephemeral
managementConfiguration: ""
@ -10,7 +9,6 @@ server: ""
Cluster: clusterBar
target:
bootstrapInfo: ""
clusterKubeconf: clusterBar_target
managementConfiguration: ""
@ -20,7 +18,6 @@ server: ""
Cluster: clusterBaz
ephemeral:
bootstrapInfo: ""
clusterKubeconf: clusterBaz_ephemeral
managementConfiguration: ""
@ -30,7 +27,6 @@ server: ""
Cluster: clusterBaz
target:
bootstrapInfo: ""
clusterKubeconf: clusterBaz_target
managementConfiguration: ""
@ -40,7 +36,6 @@ server: ""
Cluster: clusterFoo
ephemeral:
bootstrapInfo: ""
clusterKubeconf: clusterFoo_ephemeral
managementConfiguration: ""
@ -50,7 +45,6 @@ server: ""
Cluster: clusterFoo
target:
bootstrapInfo: ""
clusterKubeconf: clusterFoo_target
managementConfiguration: ""

View File

@ -1,6 +1,5 @@
Cluster: clusterFoo
target:
bootstrapInfo: ""
clusterKubeconf: clusterFoo_target
managementConfiguration: ""

View File

@ -0,0 +1,14 @@
apiVersion: airshipit.org/v1alpha1
kind: ImageConfiguration
metadata:
name: default
labels:
airshipit.org/deploy-k8s: "false"
builder:
networkConfigFileName: network-config
outputMetadataFileName: output-metadata.yaml
userDataFileName: user-data
container:
containerRuntime: docker
image: quay.io/airshipit/isogen:latest-ubuntu_focal
volume: /srv/iso:/config

View File

@ -1,2 +1,4 @@
resources:
- secret.yaml
- image_configuration.yaml
- remote_direct_configuration.yaml

View File

@ -0,0 +1,7 @@
apiVersion: airshipit.org/v1alpha1
kind: RemoteDirectConfiguration
metadata:
name: default
labels:
airshipit.org/deploy-k8s: "false"
isoUrl: http://localhost:8099/ubuntu-focal.iso

View File

@ -46,6 +46,8 @@ func init() {
&PhasePlan{},
&KubeConfig{},
&KubernetesApply{},
&ImageConfiguration{},
&RemoteDirectConfiguration{},
)
_ = AddToScheme(Scheme) //nolint:errcheck
}

View File

@ -1,6 +1,4 @@
/*
Copyright 2014 The Kubernetes 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
@ -14,21 +12,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package config
package v1alpha1
import "sigs.k8s.io/yaml"
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Bootstrap holds configurations for bootstrap steps
type Bootstrap struct {
// Configuration parameters for container
Container *Container `json:"container,omitempty"`
// Configuration parameters for ISO builder
Builder *Builder `json:"builder,omitempty"`
// Configuration parameters for ephemeral node remote management
RemoteDirect *RemoteDirect `json:"remoteDirect,omitempty"`
}
// Container parameters
// Container structure contains parameters related to Docker runtime, used for building image
type Container struct {
// Container volume directory binding.
Volume string `json:"volume,omitempty"`
@ -38,7 +28,7 @@ type Container struct {
ContainerRuntime string `json:"containerRuntime,omitempty"`
}
// Builder parameters
// Builder structure defines metadata files (including Cloud Init metadata) used for image
type Builder struct {
// Cloud Init user-data file name placed to the container volume root
UserDataFileName string `json:"userDataFileName,omitempty"`
@ -48,35 +38,12 @@ type Builder struct {
OutputMetadataFileName string `json:"outputMetadataFileName,omitempty"`
}
// RemoteDirect configuration options
type RemoteDirect struct {
// IsoURL specifies url to download ISO image for ephemeral node
IsoURL string `json:"isoUrl,omitempty"`
}
// ImageConfiguration structure is inherited from apimachinery TypeMeta and ObjectMeta and is a top level
// configuration structure for building image
type ImageConfiguration struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// Bootstrap functions
func (b *Bootstrap) String() string {
yamlData, err := yaml.Marshal(&b)
if err != nil {
return ""
}
return string(yamlData)
}
// String returns Container object in a serialized string format
func (c *Container) String() string {
yamlData, err := yaml.Marshal(&c)
if err != nil {
return ""
}
return string(yamlData)
}
// String returns Builder object in a serialized string format
func (b *Builder) String() string {
yamlData, err := yaml.Marshal(&b)
if err != nil {
return ""
}
return string(yamlData)
Container *Container `json:"container,omitempty"`
Builder *Builder `json:"builder,omitempty"`
}

View File

@ -0,0 +1,29 @@
/*
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 v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// RemoteDirectConfiguration structure is inherited from apimachinery TypeMeta and ObjectMeta structures
// and defines parameters used to bootstrap the ephemeral node during the remote direct
type RemoteDirectConfiguration struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// IsoURL specifies url to download ISO image for ephemeral node
IsoURL string `json:"isoUrl,omitempty"`
}

View File

@ -345,3 +345,85 @@ func (in *Provider) DeepCopy() *Provider {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ImageConfiguration) DeepCopyInto(out *ImageConfiguration) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Container.DeepCopyInto(out.Container)
in.Builder.DeepCopyInto(out.Builder)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Phase.
func (in *ImageConfiguration) DeepCopy() *ImageConfiguration {
if in == nil {
return nil
}
out := new(ImageConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ImageConfiguration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Container) DeepCopyInto(out *Container) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PhaseConfig.
func (in *Container) DeepCopy() *Container {
if in == nil {
return nil
}
out := new(Container)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Builder) DeepCopyInto(out *Builder) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PhaseConfig.
func (in *Builder) DeepCopy() *Builder {
if in == nil {
return nil
}
out := new(Builder)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RemoteDirectConfiguration) DeepCopyInto(out *RemoteDirectConfiguration) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Phase.
func (in *RemoteDirectConfiguration) DeepCopy() *RemoteDirectConfiguration {
if in == nil {
return nil
}
out := new(RemoteDirectConfiguration)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *RemoteDirectConfiguration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}

View File

@ -21,6 +21,7 @@ import (
"path/filepath"
"strings"
api "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/bootstrap/cloudinit"
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/container"
@ -43,17 +44,6 @@ func GenerateBootstrapIso(settings *environment.AirshipCTLSettings) error {
return err
}
cfg, err := globalConf.CurrentContextBootstrapInfo()
if err != nil {
return err
}
if err = verifyInputs(cfg); err != nil {
return err
}
// TODO (dukov) replace with the appropriate function once it's available
// in document module
root, err := globalConf.CurrentContextEntryPoint(config.BootstrapPhase)
if err != nil {
return err
@ -63,23 +53,41 @@ func GenerateBootstrapIso(settings *environment.AirshipCTLSettings) error {
return err
}
log.Print("Creating ISO builder container")
builder, err := container.NewContainer(
&ctx, cfg.Container.ContainerRuntime,
cfg.Container.Image)
imageConfiguration := &api.ImageConfiguration{}
selector, err := document.NewSelector().ByObject(imageConfiguration, api.Scheme)
if err != nil {
return err
}
doc, err := docBundle.SelectOne(selector)
if err != nil {
return err
}
err = generateBootstrapIso(docBundle, builder, cfg, log.DebugEnabled())
err = doc.ToAPIObject(imageConfiguration, api.Scheme)
if err != nil {
return err
}
if err = verifyInputs(imageConfiguration); err != nil {
return err
}
log.Print("Creating ISO builder container")
builder, err := container.NewContainer(
&ctx, imageConfiguration.Container.ContainerRuntime,
imageConfiguration.Container.Image)
if err != nil {
return err
}
err = generateBootstrapIso(docBundle, builder, doc, imageConfiguration, log.DebugEnabled())
if err != nil {
return err
}
log.Print("Checking artifacts")
return verifyArtifacts(cfg)
return verifyArtifacts(imageConfiguration)
}
func verifyInputs(cfg *config.Bootstrap) error {
func verifyInputs(cfg *api.ImageConfiguration) error {
if cfg.Container.Volume == "" {
return config.ErrMissingConfig{
What: "Must specify volume bind for ISO builder container",
@ -104,19 +112,22 @@ func verifyInputs(cfg *config.Bootstrap) error {
return nil
}
func getContainerCfg(cfg *config.Bootstrap, userData []byte, netConf []byte) map[string][]byte {
func getContainerCfg(
cfg *api.ImageConfiguration,
builderCfgYaml []byte,
userData []byte,
netConf []byte,
) map[string][]byte {
hostVol := strings.Split(cfg.Container.Volume, ":")[0]
fls := make(map[string][]byte)
fls[filepath.Join(hostVol, cfg.Builder.UserDataFileName)] = userData
fls[filepath.Join(hostVol, cfg.Builder.NetworkConfigFileName)] = netConf
// TODO (dukov) Get rid of this ugly conversion byte -> string -> byte
builderData := []byte(cfg.String())
fls[filepath.Join(hostVol, builderConfigFileName)] = builderData
fls[filepath.Join(hostVol, builderConfigFileName)] = builderCfgYaml
return fls
}
func verifyArtifacts(cfg *config.Bootstrap) error {
func verifyArtifacts(cfg *api.ImageConfiguration) error {
hostVol := strings.Split(cfg.Container.Volume, ":")[0]
metadataPath := filepath.Join(hostVol, cfg.Builder.OutputMetadataFileName)
_, err := os.Stat(metadataPath)
@ -126,7 +137,8 @@ func verifyArtifacts(cfg *config.Bootstrap) error {
func generateBootstrapIso(
docBundle document.Bundle,
builder container.Container,
cfg *config.Bootstrap,
doc document.Document,
cfg *api.ImageConfiguration,
debug bool,
) error {
cntVol := strings.Split(cfg.Container.Volume, ":")[1]
@ -136,7 +148,12 @@ func generateBootstrapIso(
return err
}
fls := getContainerCfg(cfg, userData, netConf)
builderCfgYaml, err := doc.AsYAML()
if err != nil {
return err
}
fls := getContainerCfg(cfg, builderCfgYaml, userData, netConf)
if err = util.WriteFiles(fls, 0600); err != nil {
return err
}

View File

@ -21,9 +21,12 @@ import (
"strings"
"testing"
"opendev.org/airship/airshipctl/pkg/environment"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
api "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/log"
@ -66,17 +69,26 @@ func TestBootstrapIso(t *testing.T) {
defer cleanup(t)
volBind := tempVol + ":/dst"
testErr := fmt.Errorf("testErr")
testCfg := &config.Bootstrap{
Container: &config.Container{
testErr := fmt.Errorf("TestErr")
testCfg := &api.ImageConfiguration{
Container: &api.Container{
Volume: volBind,
ContainerRuntime: "docker",
},
Builder: &config.Builder{
Builder: &api.Builder{
UserDataFileName: "user-data",
NetworkConfigFileName: "net-conf",
},
}
testDoc := &MockDocument{
MockAsYAML: func() ([]byte, error) { return []byte("TESTDOC"), nil },
}
testBuilder := &mockContainer{
runCommand: func() error { return nil },
getID: func() string { return "TESTID" },
rmContainer: func() error { return nil },
}
expOut := []string{
"Creating cloud-init for ephemeral K8s",
fmt.Sprintf("Running default container command. Mounted dir: [%s]", volBind),
@ -87,7 +99,8 @@ func TestBootstrapIso(t *testing.T) {
tests := []struct {
builder *mockContainer
cfg *config.Bootstrap
cfg *api.ImageConfiguration
doc *MockDocument
debug bool
expectedOut []string
expectedErr error
@ -97,16 +110,15 @@ func TestBootstrapIso(t *testing.T) {
runCommand: func() error { return testErr },
},
cfg: testCfg,
doc: testDoc,
debug: false,
expectedOut: []string{expOut[0], expOut[1]},
expectedErr: testErr,
},
{
builder: &mockContainer{
runCommand: func() error { return nil },
getID: func() string { return "TESTID" },
},
builder: testBuilder,
cfg: testCfg,
doc: testDoc,
debug: true,
expectedOut: []string{expOut[0], expOut[1], expOut[2], expOut[3]},
expectedErr: nil,
@ -118,16 +130,27 @@ func TestBootstrapIso(t *testing.T) {
rmContainer: func() error { return testErr },
},
cfg: testCfg,
doc: testDoc,
debug: false,
expectedOut: []string{expOut[0], expOut[1], expOut[2], expOut[4]},
expectedErr: testErr,
},
{
builder: testBuilder,
cfg: testCfg,
doc: &MockDocument{
MockAsYAML: func() ([]byte, error) { return nil, testErr },
},
debug: false,
expectedOut: []string{expOut[0]},
expectedErr: testErr,
},
}
for _, tt := range tests {
outBuf := &bytes.Buffer{}
log.Init(tt.debug, outBuf)
actualErr := generateBootstrapIso(bundle, tt.builder, tt.cfg, tt.debug)
actualErr := generateBootstrapIso(bundle, tt.builder, tt.doc, tt.cfg, tt.debug)
actualOut := outBuf.String()
for _, line := range tt.expectedOut {
@ -144,14 +167,14 @@ func TestVerifyInputs(t *testing.T) {
tests := []struct {
name string
cfg *config.Bootstrap
cfg *api.ImageConfiguration
args []string
expectedErr error
}{
{
name: "missing-container-field",
cfg: &config.Bootstrap{
Container: &config.Container{},
cfg: &api.ImageConfiguration{
Container: &api.Container{},
},
expectedErr: config.ErrMissingConfig{
What: "Must specify volume bind for ISO builder container",
@ -159,11 +182,11 @@ func TestVerifyInputs(t *testing.T) {
},
{
name: "missing-filenames",
cfg: &config.Bootstrap{
Container: &config.Container{
cfg: &api.ImageConfiguration{
Container: &api.Container{
Volume: tempVol + ":/dst",
},
Builder: &config.Builder{},
Builder: &api.Builder{},
},
expectedErr: config.ErrMissingConfig{
What: "UserDataFileName or NetworkConfigFileName are not specified in ISO builder config",
@ -171,11 +194,11 @@ func TestVerifyInputs(t *testing.T) {
},
{
name: "invalid-host-path",
cfg: &config.Bootstrap{
Container: &config.Container{
cfg: &api.ImageConfiguration{
Container: &api.Container{
Volume: tempVol + ":/dst:/dst1",
},
Builder: &config.Builder{
Builder: &api.Builder{
UserDataFileName: "user-data",
NetworkConfigFileName: "net-conf",
},
@ -186,11 +209,11 @@ func TestVerifyInputs(t *testing.T) {
},
{
name: "success",
cfg: &config.Bootstrap{
Container: &config.Container{
cfg: &api.ImageConfiguration{
Container: &api.Container{
Volume: tempVol,
},
Builder: &config.Builder{
Builder: &api.Builder{
UserDataFileName: "user-data",
NetworkConfigFileName: "net-conf",
},
@ -207,3 +230,90 @@ func TestVerifyInputs(t *testing.T) {
})
}
}
func TestGenerateBootstrapIso(t *testing.T) {
t.Run("EnsureCompleteError", func(t *testing.T) {
settings := &environment.AirshipCTLSettings{
Debug: false,
AirshipConfigPath: "testdata/config/config",
KubeConfigPath: "testdata/config/kubeconfig",
Config: &config.Config{},
}
expectedErr := config.ErrMissingConfig{What: "Current Context is not defined"}
settings.InitConfig()
settings.Config.CurrentContext = ""
actualErr := GenerateBootstrapIso(settings)
assert.Equal(t, expectedErr, actualErr)
})
t.Run("ContextEntryPointError", func(t *testing.T) {
settings := &environment.AirshipCTLSettings{
Debug: false,
AirshipConfigPath: "testdata/config/config",
KubeConfigPath: "testdata/config/kubeconfig",
Config: &config.Config{},
}
expectedErr := config.ErrMissingPrimaryRepo{}
settings.InitConfig()
settings.Config.Manifests["default"].Repositories = make(map[string]*config.Repository)
actualErr := GenerateBootstrapIso(settings)
assert.Equal(t, expectedErr, actualErr)
})
t.Run("NewBundleByPathError", func(t *testing.T) {
settings := &environment.AirshipCTLSettings{
Debug: false,
AirshipConfigPath: "testdata/config/config",
KubeConfigPath: "testdata/config/kubeconfig",
Config: &config.Config{},
}
expectedErr := config.ErrMissingPhaseDocument{PhaseName: "bootstrap"}
settings.InitConfig()
settings.Config.Manifests["default"].TargetPath = "/nonexistent"
actualErr := GenerateBootstrapIso(settings)
assert.Equal(t, expectedErr, actualErr)
})
t.Run("SelectOneError", func(t *testing.T) {
settings := &environment.AirshipCTLSettings{
Debug: false,
AirshipConfigPath: "testdata/config/config",
KubeConfigPath: "testdata/config/kubeconfig",
Config: &config.Config{},
}
expectedErr := document.ErrDocNotFound{
Selector: document.NewSelector().ByGvk("airshipit.org", "v1alpha1", "ImageConfiguration")}
settings.InitConfig()
settings.Config.Manifests["default"].SubPath = "missingkinddoc/site/test-site"
actualErr := GenerateBootstrapIso(settings)
assert.Equal(t, expectedErr, actualErr)
})
t.Run("ToObjectError", func(t *testing.T) {
settings := &environment.AirshipCTLSettings{
Debug: false,
AirshipConfigPath: "testdata/config/config",
KubeConfigPath: "testdata/config/kubeconfig",
Config: &config.Config{},
}
expectedErrMessage := "missing metadata.name in object"
settings.InitConfig()
settings.Config.Manifests["default"].SubPath = "missingmetadoc/site/test-site"
actualErr := GenerateBootstrapIso(settings)
assert.Contains(t, actualErr.Error(), expectedErrMessage)
})
t.Run("verifyInputsError", func(t *testing.T) {
settings := &environment.AirshipCTLSettings{
Debug: false,
AirshipConfigPath: "testdata/config/config",
KubeConfigPath: "testdata/config/kubeconfig",
Config: &config.Config{},
}
expectedErr := config.ErrMissingConfig{What: "Must specify volume bind for ISO builder container"}
settings.InitConfig()
settings.Config.Manifests["default"].SubPath = "missingvoldoc/site/test-site"
actualErr := GenerateBootstrapIso(settings)
assert.Equal(t, expectedErr, actualErr)
})
}

View File

@ -0,0 +1,127 @@
/*
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
https://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 isogen
import (
"k8s.io/apimachinery/pkg/runtime"
)
type MockDocument struct {
MockAnnotate func()
MockAsYAML func() ([]byte, error)
MockGetAnnotations func() map[string]string
MockGetBool func() (bool, error)
MockGetFloat64 func() (float64, error)
MockGetGroup func() string
MockGetInt64 func() (int64, error)
MockGetKind func() string
MockGetLabels func() map[string]string
MockGetMap func() (map[string]interface{}, error)
MockGetName func() string
MockGetNamespace func() string
MockGetSlice func() ([]interface{}, error)
MockGetString func() (string, error)
MockGetStringMap func() (map[string]string, error)
MockGetStringSlice func() ([]string, error)
MockGetVersion func() string
MockLabel func()
MockMarshalJSON func() ([]byte, error)
MockToObject func() error
MockToAPIObject func() error
}
func (md *MockDocument) Annotate(_ map[string]string) {
md.MockAnnotate()
}
func (md *MockDocument) AsYAML() ([]byte, error) {
return md.MockAsYAML()
}
func (md *MockDocument) GetAnnotations() map[string]string {
return md.MockGetAnnotations()
}
func (md *MockDocument) GetBool(_ string) (bool, error) {
return md.MockGetBool()
}
func (md *MockDocument) GetFloat64(_ string) (float64, error) {
return md.MockGetFloat64()
}
func (md *MockDocument) GetGroup() string {
return md.MockGetGroup()
}
func (md *MockDocument) GetInt64(_ string) (int64, error) {
return md.MockGetInt64()
}
func (md *MockDocument) GetKind() string {
return md.MockGetKind()
}
func (md *MockDocument) GetLabels() map[string]string {
return md.MockGetLabels()
}
func (md *MockDocument) GetMap(_ string) (map[string]interface{}, error) {
return md.MockGetMap()
}
func (md *MockDocument) GetName() string {
return md.MockGetName()
}
func (md *MockDocument) GetNamespace() string {
return md.MockGetNamespace()
}
func (md *MockDocument) GetSlice(_ string) ([]interface{}, error) {
return md.MockGetSlice()
}
func (md *MockDocument) GetString(_ string) (string, error) {
return md.MockGetString()
}
func (md *MockDocument) GetStringMap(_ string) (map[string]string, error) {
return md.MockGetStringMap()
}
func (md *MockDocument) GetStringSlice(_ string) ([]string, error) {
return md.MockGetStringSlice()
}
func (md *MockDocument) GetVersion() string {
return md.MockGetVersion()
}
func (md *MockDocument) Label(_ map[string]string) {
md.MockLabel()
}
func (md *MockDocument) MarshalJSON() ([]byte, error) {
return md.MockMarshalJSON()
}
func (md *MockDocument) ToObject(_ interface{}) error {
return md.MockToObject()
}
func (md *MockDocument) ToAPIObject(obj runtime.Object, scheme *runtime.Scheme) error {
return md.MockToAPIObject()
}

View File

@ -0,0 +1,34 @@
apiVersion: airshipit.org/v1alpha1
clusters:
default:
clusterType:
ephemeral:
clusterKubeconf: default_ephemeral
managementConfiguration: default
contexts:
default:
contextKubeconf: default_ephemeral
manifest: default
currentContext: default
kind: Config
managementConfiguration:
default:
insecure: true
systemActionRetries: 30
systemRebootDelay: 30
type: redfish
manifests:
default:
primaryRepositoryName: primary
repositories:
primary:
checkout:
branch: master
commitHash: ""
force: false
tag: ""
url: https://opendev.org/airship/treasuremap
subPath: primary/site/test-site
targetPath: testdata
users:
admin: {}

View File

@ -0,0 +1,17 @@
apiVersion: v1
clusters:
- cluster:
server: https://172.17.0.1:6443
name: default_ephemeral
contexts:
- context:
cluster: default_ephemeral
user: admin
name: default
current-context: default
kind: Config
preferences: {}
users:
- name: admin
user:
username: airship-admin

View File

@ -0,0 +1,12 @@
apiVersion: airshipit.org/v1alpha1
kind: FakeImageConfiguration
metadata:
name: default
builder:
networkConfigFileName: network-config
outputMetadataFileName: output-metadata.yaml
userDataFileName: user-data
container:
containerRuntime: docker
image: quay.io/airshipit/isogen:latest-debian_stable
volume: /srv/iso:/config

View File

@ -0,0 +1,2 @@
resources:
- image_configuration.yaml

View File

@ -0,0 +1,10 @@
apiVersion: airshipit.org/v1alpha1
kind: ImageConfiguration
builder:
networkConfigFileName: network-config
outputMetadataFileName: output-metadata.yaml
userDataFileName: user-data
container:
containerRuntime: docker
image: quay.io/airshipit/isogen:latest-debian_stable
volume: /srv/iso:/config

View File

@ -0,0 +1,2 @@
resources:
- image_configuration.yaml

View File

@ -0,0 +1,11 @@
apiVersion: airshipit.org/v1alpha1
kind: ImageConfiguration
metadata:
name: default
builder:
networkConfigFileName: network-config
outputMetadataFileName: output-metadata.yaml
userDataFileName: user-data
container:
containerRuntime: docker
image: quay.io/airshipit/isogen:latest-debian_stable

View File

@ -0,0 +1,2 @@
resources:
- image_configuration.yaml

View File

@ -34,9 +34,6 @@ type Cluster struct {
// Management configuration which will be used for all hosts in the cluster
ManagementConfiguration string `json:"managementConfiguration"`
// Bootstrap configuration this clusters ephemeral hosts will rely on
Bootstrap string `json:"bootstrapInfo"`
}
// ClusterPurpose encapsulates the Cluster Type as an enumeration

View File

@ -67,9 +67,6 @@ type Config struct {
// Management configuration defines management information for all baremetal hosts in a cluster.
ManagementConfiguration map[string]*ManagementConfiguration `json:"managementConfiguration"`
// BootstrapInfo is the configuration for container runtime, ISO builder and remote management
BootstrapInfo map[string]*Bootstrap `json:"bootstrapInfo"`
// loadedConfigPath is the full path to the the location of the config
// file from which this config was loaded
// +not persisted in file
@ -952,26 +949,6 @@ func (c *Config) importAuthInfos(importKubeConfig *clientcmdapi.Config) {
}
}
// CurrentContextBootstrapInfo returns bootstrap info for current context
func (c *Config) CurrentContextBootstrapInfo() (*Bootstrap, error) {
currentCluster, err := c.CurrentContextCluster()
if err != nil {
return nil, err
}
if currentCluster.Bootstrap == "" {
return nil, ErrMissingConfig{
What: fmt.Sprintf("No bootstrapInfo defined for context %q", c.CurrentContext),
}
}
bootstrap, exists := c.BootstrapInfo[currentCluster.Bootstrap]
if !exists {
return nil, ErrBootstrapInfoNotFound{Name: currentCluster.Bootstrap}
}
return bootstrap, nil
}
// GetManifests returns all of the Manifests associated with the Config sorted by name
func (c *Config) GetManifests() []*Manifest {
keys := make([]string, 0, len(c.Manifests))

View File

@ -80,30 +80,10 @@ func TestString(t *testing.T) {
name: "repo-checkout",
stringer: testutil.DummyRepoCheckout(),
},
{
name: "bootstrapinfo",
stringer: testutil.DummyBootstrapInfo(),
},
{
name: "managementconfiguration",
stringer: testutil.DummyManagementConfiguration(),
},
{
name: "builder",
stringer: &config.Builder{
UserDataFileName: "user-data",
NetworkConfigFileName: "netconfig",
OutputMetadataFileName: "output-metadata.yaml",
},
},
{
name: "container",
stringer: &config.Container{
Volume: "/dummy:dummy",
Image: "dummy_image:dummy_tag",
ContainerRuntime: "docker",
},
},
}
for _, tt := range tests {
@ -258,27 +238,6 @@ func TestEnsureComplete(t *testing.T) {
}
}
func TestCurrentContextBootstrapInfo(t *testing.T) {
conf, cleanup := testutil.InitConfig(t)
defer cleanup(t)
clusterName := "def"
clusterType := "ephemeral"
bootstrapInfo, err := conf.CurrentContextBootstrapInfo()
require.Error(t, err)
assert.Nil(t, bootstrapInfo)
conf.CurrentContext = currentContextName
conf.Clusters[clusterName].ClusterTypes[clusterType].Bootstrap = defaultString
conf.Contexts[currentContextName].Manifest = defaultString
conf.Contexts[currentContextName].KubeContext().Cluster = clusterName
bootstrapInfo, err = conf.CurrentContextBootstrapInfo()
require.NoError(t, err)
assert.Equal(t, conf.BootstrapInfo[defaultString], bootstrapInfo)
}
func TestCurrentContextManagementConfig(t *testing.T) {
conf, cleanup := testutil.InitConfig(t)
defer cleanup(t)

View File

@ -43,7 +43,6 @@ const (
AirshipConfigGroup = "airshipit.org"
AirshipConfigKind = "Config"
AirshipConfigVersion = "v1alpha1"
AirshipDefaultBootstrapInfo = "default"
AirshipDefaultContext = "default"
AirshipDefaultDirectoryPermission = 0750
AirshipDefaultFilePermission = 0640
@ -57,8 +56,6 @@ const (
AirshipPluginPathEnv = "AIRSHIP_KUSTOMIZE_PLUGINS"
// Modules
AirshipDefaultBootstrapImage = "quay.io/airshipit/isogen:latest-ubuntu_focal"
AirshipDefaultIsoURL = "http://localhost:8099/ubuntu-focal.iso"
AirshipDefaultManagementType = redfish.ClientType
)

View File

@ -107,16 +107,6 @@ func (e ErrMissingRepoCheckoutOptions) Error() string {
return "Missing repository checkout options."
}
// ErrBootstrapInfoNotFound returned if bootstrap
// information is not found for cluster
type ErrBootstrapInfoNotFound struct {
Name string
}
func (e ErrBootstrapInfoNotFound) Error() string {
return fmt.Sprintf("Bootstrap info %q not found.", e.Name)
}
// ErrInvalidConfig returned in case of incorrect configuration
type ErrInvalidConfig struct {
What string

View File

@ -1,8 +0,0 @@
builder:
networkConfigFileName: netconfig
outputMetadataFileName: output-metadata.yaml
userDataFileName: user-data
container:
containerRuntime: docker
image: dummy_image:dummy_tag
volume: /dummy:dummy

View File

@ -1,3 +0,0 @@
networkConfigFileName: netconfig
outputMetadataFileName: output-metadata.yaml
userDataFileName: user-data

View File

@ -1,4 +1,3 @@
bootstrapInfo: dummy_bootstrap_config
clusterKubeconf: dummy_cluster_target
managementConfiguration: dummy_management_config

View File

@ -1,23 +1,11 @@
apiVersion: airshipit.org/v1alpha1
bootstrapInfo:
dummy_bootstrap_config:
builder:
networkConfigFileName: netconfig
outputMetadataFileName: output-metadata.yaml
userDataFileName: user-data
container:
containerRuntime: docker
image: dummy_image:dummy_tag
volume: /dummy:dummy
clusters:
dummy_cluster:
clusterType:
ephemeral:
bootstrapInfo: dummy_bootstrap_config
clusterKubeconf: dummy_cluster_ephemeral
managementConfiguration: dummy_management_config
target:
bootstrapInfo: dummy_bootstrap_config
clusterKubeconf: dummy_cluster_target
managementConfiguration: dummy_management_config
contexts:

View File

@ -1,6 +1,5 @@
Cluster: dummy_cluster
target:
bootstrapInfo: dummy_bootstrap_config
clusterKubeconf: dummy_cluster_target
managementConfiguration: dummy_management_config

View File

@ -25,24 +25,7 @@ func NewConfig() *Config {
return &Config{
Kind: AirshipConfigKind,
APIVersion: AirshipConfigAPIVersion,
BootstrapInfo: map[string]*Bootstrap{
AirshipDefaultBootstrapInfo: {
Container: &Container{
Volume: "/srv/iso:/config",
Image: AirshipDefaultBootstrapImage,
ContainerRuntime: "docker",
},
Builder: &Builder{
UserDataFileName: "user-data",
NetworkConfigFileName: "network-config",
OutputMetadataFileName: "output-metadata.yaml",
},
RemoteDirect: &RemoteDirect{
IsoURL: AirshipDefaultIsoURL,
},
},
},
Clusters: make(map[string]*ClusterPurpose),
Clusters: make(map[string]*ClusterPurpose),
Permissions: Permissions{
DirectoryPermission: AirshipDefaultDirectoryPermission,
FilePermission: AirshipDefaultFilePermission,
@ -84,7 +67,6 @@ func NewContext() *Context {
func NewCluster() *Cluster {
return &Cluster{
NameInKubeconf: "",
Bootstrap: AirshipDefaultBootstrapInfo,
ManagementConfiguration: AirshipDefaultManagementConfiguration,
}
}

View File

@ -45,14 +45,13 @@ func (e ErrUnknownManagementType) Error() string {
return fmt.Sprintf("unknown management type: %s", e.Type)
}
// ErrMissingBootstrapInfoOption is an error that indicates a bootstrap option is missing in the airshipctl
// bootstrapInfo configuration.
type ErrMissingBootstrapInfoOption struct {
// ErrMissingOption is an error that indicates a remote direct config option is missing
type ErrMissingOption struct {
What string
}
func (e ErrMissingBootstrapInfoOption) Error() string {
return fmt.Sprintf("missing bootstrapInfo option: %s", e.What)
func (e ErrMissingOption) Error() string {
return fmt.Sprintf("missing option: %s", e.What)
}
// ErrNoHostsFound is an error that indicates that no hosts matched the selection criteria passed to a manager.

View File

@ -15,7 +15,9 @@
package remote
import (
api "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/environment"
"opendev.org/airship/airshipctl/pkg/log"
@ -25,14 +27,30 @@ import (
// DoRemoteDirect bootstraps the ephemeral node.
func (b baremetalHost) DoRemoteDirect(settings *environment.AirshipCTLSettings) error {
cfg := settings.Config
bootstrapSettings, err := cfg.CurrentContextBootstrapInfo()
root, err := cfg.CurrentContextEntryPoint(config.BootstrapPhase)
if err != nil {
return err
}
remoteConfig := bootstrapSettings.RemoteDirect
if remoteConfig == nil {
return config.ErrMissingConfig{What: "RemoteDirect options not defined in bootstrap config"}
docBundle, err := document.NewBundleByPath(root)
if err != nil {
return err
}
remoteDirectConfiguration := &api.RemoteDirectConfiguration{}
selector, err := document.NewSelector().ByObject(remoteDirectConfiguration, api.Scheme)
if err != nil {
return err
}
doc, err := docBundle.SelectOne(selector)
if err != nil {
return err
}
err = doc.ToAPIObject(remoteDirectConfiguration, api.Scheme)
if err != nil {
return err
}
log.Debugf("Bootstrapping ephemeral host '%s' with ID '%s' and BMC Address '%s'.", b.HostName, b.NodeID(),
@ -52,11 +70,11 @@ func (b baremetalHost) DoRemoteDirect(settings *environment.AirshipCTLSettings)
}
// Perform remote direct operations
if remoteConfig.IsoURL == "" {
return ErrMissingBootstrapInfoOption{What: "isoURL"}
if remoteDirectConfiguration.IsoURL == "" {
return ErrMissingOption{What: "isoURL"}
}
err = b.SetVirtualMedia(b.Context, remoteConfig.IsoURL)
err = b.SetVirtualMedia(b.Context, remoteDirectConfiguration.IsoURL)
if err != nil {
return err
}

View File

@ -20,8 +20,6 @@ import (
"github.com/stretchr/testify/assert"
"opendev.org/airship/airshipctl/pkg/config"
"opendev.org/airship/airshipctl/pkg/environment"
"opendev.org/airship/airshipctl/pkg/remote/power"
"opendev.org/airship/airshipctl/pkg/remote/redfish"
"opendev.org/airship/airshipctl/testutil/redfishutils"
@ -35,18 +33,6 @@ const (
password = "password"
)
// withRemoteDirectConfig initializes the remote direct settings when used as an argument to "initSettings".
func withRemoteDirectConfig(cfg *config.RemoteDirect) Configuration {
return func(settings *environment.AirshipCTLSettings) {
bootstrapInfo, err := settings.Config.CurrentContextBootstrapInfo()
if err != nil {
panic(fmt.Sprintf("Unable to initialize remote direct tests. Current Context error %q", err))
}
bootstrapInfo.RemoteDirect = cfg
}
}
func TestDoRemoteDirectMissingConfigOpts(t *testing.T) {
ctx, rMock, err := redfishutils.NewClient(redfishURL, false, false, username, password)
assert.NoError(t, err)
@ -60,8 +46,12 @@ func TestDoRemoteDirectMissingConfigOpts(t *testing.T) {
password,
}
settings := initSettings(t, withRemoteDirectConfig(nil), withTestDataPath("base"))
settings := initSettings(t, withTestDataPath("noremote"))
// there must be document.ErrDocNotFound
err = ephemeralHost.DoRemoteDirect(settings)
expectedErrorMessage := `document filtered by selector [Group="airshipit.org", Version="v1alpha1", ` +
`Kind="RemoteDirectConfiguration"] found no documents`
assert.Equal(t, expectedErrorMessage, fmt.Sprintf("%s", err))
assert.Error(t, err)
}
@ -81,10 +71,10 @@ func TestDoRemoteDirectMissingISOURL(t *testing.T) {
password,
}
cfg := &config.RemoteDirect{}
settings := initSettings(t, withRemoteDirectConfig(cfg), withTestDataPath("base"))
settings := initSettings(t, withTestDataPath("noisourl"))
err = ephemeralHost.DoRemoteDirect(settings)
expectedErrorMessage := `missing option: isoURL`
assert.Equal(t, expectedErrorMessage, fmt.Sprintf("%s", err))
assert.Error(t, err)
}
@ -108,11 +98,7 @@ func TestDoRemoteDirectRedfish(t *testing.T) {
password,
}
cfg := &config.RemoteDirect{
IsoURL: isoURL,
}
settings := initSettings(t, withRemoteDirectConfig(cfg), withTestDataPath("base"))
settings := initSettings(t, withTestDataPath("base"))
err = ephemeralHost.DoRemoteDirect(settings)
assert.NoError(t, err)
}
@ -138,11 +124,7 @@ func TestDoRemoteDirectRedfishNodePoweredOff(t *testing.T) {
password,
}
cfg := &config.RemoteDirect{
IsoURL: isoURL,
}
settings := initSettings(t, withRemoteDirectConfig(cfg), withTestDataPath("base"))
settings := initSettings(t, withTestDataPath("base"))
err = ephemeralHost.DoRemoteDirect(settings)
assert.NoError(t, err)
}
@ -169,11 +151,7 @@ func TestDoRemoteDirectRedfishVirtualMediaError(t *testing.T) {
password,
}
cfg := &config.RemoteDirect{
IsoURL: isoURL,
}
settings := initSettings(t, withRemoteDirectConfig(cfg), withTestDataPath("base"))
settings := initSettings(t, withTestDataPath("base"))
err = ephemeralHost.DoRemoteDirect(settings)
_, ok := err.(redfish.ErrRedfishClient)
@ -202,11 +180,7 @@ func TestDoRemoteDirectRedfishBootSourceError(t *testing.T) {
password,
}
cfg := &config.RemoteDirect{
IsoURL: isoURL,
}
settings := initSettings(t, withRemoteDirectConfig(cfg), withTestDataPath("base"))
settings := initSettings(t, withTestDataPath("base"))
err = ephemeralHost.DoRemoteDirect(settings)
_, ok := err.(redfish.ErrRedfishClient)
@ -236,11 +210,7 @@ func TestDoRemoteDirectRedfishRebootError(t *testing.T) {
password,
}
cfg := &config.RemoteDirect{
IsoURL: isoURL,
}
settings := initSettings(t, withRemoteDirectConfig(cfg), withTestDataPath("base"))
settings := initSettings(t, withTestDataPath("base"))
err = ephemeralHost.DoRemoteDirect(settings)
_, ok := err.(redfish.ErrRedfishClient)

View File

@ -1,2 +1,3 @@
resources:
- baremetal.yaml
- remote_direct_configuration.yaml

View File

@ -0,0 +1,5 @@
apiVersion: airshipit.org/v1alpha1
kind: RemoteDirectConfiguration
metadata:
name: default
isoUrl: https://localhost:8080/ubuntu.iso

View File

@ -0,0 +1,74 @@
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
labels:
airshipit.org/ephemeral-node: "true"
name: master-0
spec:
online: true
bootMACAddress: 00:3b:8b:0c:ec:8b
bmc:
address: redfish+http://nolocalhost:8888/redfish/v1/Systems/ephemeral
credentialsName: master-0-bmc-secret
---
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/ephemeral-node: "true"
name: master-0-bmc-secret
type: Opaque
data:
username: YWRtaW4=
password: cGFzc3dvcmQ=
...
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
labels:
airshipit.org/test-node: "true"
name: master-1
spec:
online: true
bootMACAddress: 00:3b:8b:0c:ec:8b
bmc:
address: redfish+http://nolocalhost:8888/redfish/v1/Systems/node-master-1
credentialsName: master-1-bmc-secret
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
labels:
airshipit.org/test-node: "true"
name: master-2
spec:
online: true
bootMACAddress: 00:3b:8b:0c:ec:8b
bmc:
address: redfish+http://nolocalhost:8888/redfish/v1/Systems/node-master-2
credentialsName: master-1-bmc-secret
---
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/ephemeral-node: "true"
name: master-1-bmc-secret
type: Opaque
data:
username: YWRtaW4=
password: cGFzc3dvcmQ=
...
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
name: no-creds
spec:
online: true
bootMACAddress: 00:3b:8b:0c:ec:8b
bmc:
address: redfish+http://nolocalhost:8888/redfish/v1/Systems/test-node
...

View File

@ -0,0 +1,3 @@
resources:
- baremetal.yaml
- remote_direct_configuration.yaml

View File

@ -0,0 +1,4 @@
apiVersion: airshipit.org/v1alpha1
kind: RemoteDirectConfiguration
metadata:
name: default

View File

@ -0,0 +1,74 @@
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
labels:
airshipit.org/ephemeral-node: "true"
name: master-0
spec:
online: true
bootMACAddress: 00:3b:8b:0c:ec:8b
bmc:
address: redfish+http://nolocalhost:8888/redfish/v1/Systems/ephemeral
credentialsName: master-0-bmc-secret
---
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/ephemeral-node: "true"
name: master-0-bmc-secret
type: Opaque
data:
username: YWRtaW4=
password: cGFzc3dvcmQ=
...
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
labels:
airshipit.org/test-node: "true"
name: master-1
spec:
online: true
bootMACAddress: 00:3b:8b:0c:ec:8b
bmc:
address: redfish+http://nolocalhost:8888/redfish/v1/Systems/node-master-1
credentialsName: master-1-bmc-secret
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
labels:
airshipit.org/test-node: "true"
name: master-2
spec:
online: true
bootMACAddress: 00:3b:8b:0c:ec:8b
bmc:
address: redfish+http://nolocalhost:8888/redfish/v1/Systems/node-master-2
credentialsName: master-1-bmc-secret
---
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/ephemeral-node: "true"
name: master-1-bmc-secret
type: Opaque
data:
username: YWRtaW4=
password: cGFzc3dvcmQ=
...
---
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
name: no-creds
spec:
online: true
bootMACAddress: 00:3b:8b:0c:ec:8b
bmc:
address: redfish+http://nolocalhost:8888/redfish/v1/Systems/test-node
...

View File

@ -0,0 +1,2 @@
resources:
- baremetal.yaml

View File

@ -46,9 +46,6 @@ func DummyConfig() *config.Config {
DirectoryPermission: config.AirshipDefaultDirectoryPermission,
FilePermission: config.AirshipDefaultFilePermission,
},
BootstrapInfo: map[string]*config.Bootstrap{
"dummy_bootstrap_config": DummyBootstrapInfo(),
},
Contexts: map[string]*config.Context{
"dummy_context": DummyContext(),
},
@ -92,7 +89,6 @@ func DummyCluster() *config.Cluster {
cluster.CertificateAuthority = "dummy_ca"
c.SetKubeCluster(cluster)
c.NameInKubeconf = "dummy_cluster_target"
c.Bootstrap = "dummy_bootstrap_config"
c.ManagementConfiguration = "dummy_management_config"
return c
}
@ -227,26 +223,6 @@ func DummyAuthInfoOptions() *config.AuthInfoOptions {
return authinfo
}
// DummyBootstrapInfo creates a dummy BootstrapInfo config object for unit testing
func DummyBootstrapInfo() *config.Bootstrap {
bs := &config.Bootstrap{}
cont := config.Container{
Volume: "/dummy:dummy",
Image: "dummy_image:dummy_tag",
ContainerRuntime: "docker",
}
builder := config.Builder{
UserDataFileName: "user-data",
NetworkConfigFileName: "netconfig",
OutputMetadataFileName: "output-metadata.yaml",
}
bs.Container = &cont
bs.Builder = &builder
return bs
}
// DummyManagementConfiguration creates a management configuration for unit testing
func DummyManagementConfiguration() *config.ManagementConfiguration {
return &config.ManagementConfiguration{
@ -283,22 +259,18 @@ clusters:
def:
clusterType:
ephemeral:
bootstrapInfo: ""
clusterKubeconf: def_ephemeral
target:
bootstrapInfo: ""
clusterKubeconf: def_target
onlyinkubeconf:
clusterType:
target:
bootstrapInfo: ""
clusterKubeconf: onlyinkubeconf_target
wrongonlyinconfig:
clusterType: {}
wrongonlyinkubeconf:
clusterType:
target:
bootstrapInfo: ""
clusterKubeconf: wrongonlyinkubeconf_target
clustertypenil:
clusterType: null

View File

@ -54,18 +54,6 @@ function generate_airshipconf {
cat <<EOL > ${AIRSHIPCONFIG}
apiVersion: airshipit.org/v1alpha1
bootstrapInfo:
default:
builder:
networkConfigFileName: network-config
outputMetadataFileName: output-metadata.yaml
userDataFileName: user-data
container:
containerRuntime: docker
image: quay.io/airshipit/isogen:latest-ubuntu_focal
volume: /srv/iso:/config
remoteDirect:
isoUrl: http://localhost:8099/ubuntu-focal.iso
clusters:
${CONTEXT}_${cluster}:
clusterType: