[#45] iso generation pulls network data from ephemeral host
This commit updates the iso generation process to pull the network data from the ephemeral host network data from the baremetalhost spec of the host with the right label, namely: airshipit.org/ephemeral-node=true It will back into the secret name and namespace specified for the given host and extract the network data from this secret which should be identical to the network configuration the host would receive during normal provisioning. It also pulls the user-data for the iso generation process which is specific to the iso generation process from a secret with a similar special label: airshipit.org/ephemeral-user-data=true Change-Id: Iae6edeb231d9dbae0b316aa73009f145d57ac316
This commit is contained in:
parent
d155be2145
commit
28db50e6d6
@ -7,72 +7,137 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// TODO (dukov) This should depend on cluster api version once it is
|
UserDataKind = "Secret"
|
||||||
// fully available for Metal3. In other words:
|
NetworkDataKind = "Secret"
|
||||||
// - Secret for v1alpha1
|
BareMetalHostKind = "BareMetalHost"
|
||||||
// - KubeAdmConfig for v1alpha2
|
EphemeralHostLabel = "airshipit.org/ephemeral-node=true"
|
||||||
EphemeralClusterConfKind = "Secret"
|
EphemeralUserDataLabel = "airshipit.org/ephemeral-user-data=true"
|
||||||
|
networkDataKey = "networkData"
|
||||||
|
userDataKey = "userData"
|
||||||
)
|
)
|
||||||
|
|
||||||
func decodeData(cfg document.Document, key string) ([]byte, error) {
|
// GetCloudData reads YAML document input and generates cloud-init data for
|
||||||
data, err := cfg.GetStringMap("data")
|
// ephemeral node.
|
||||||
|
func GetCloudData(docBundle document.Bundle) (userData []byte, netConf []byte, err error) {
|
||||||
|
userData, err = getUserData(docBundle)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrDataNotSupplied{DocName: cfg.GetName(), Key: key}
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
res, ok := data[key]
|
netConf, err = getNetworkData(docBundle)
|
||||||
if !ok {
|
|
||||||
return nil, ErrDataNotSupplied{DocName: cfg.GetName(), Key: key}
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return b64.StdEncoding.DecodeString(res)
|
return userData, netConf, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDataFromSecret extracts data from Secret with respect to overrides
|
func getUserData(docBundle document.Bundle) ([]byte, error) {
|
||||||
func getDataFromSecret(cfg document.Document, key string) ([]byte, error) {
|
// find the user-data document
|
||||||
data, err := cfg.GetStringMap("stringData")
|
selector := document.NewSelector().ByKind(UserDataKind).ByLabel(EphemeralUserDataLabel)
|
||||||
|
docs, err := docBundle.Select(selector)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return decodeData(cfg, key)
|
return nil, err
|
||||||
|
}
|
||||||
|
var userDataDoc document.Document = &document.Factory{}
|
||||||
|
switch numDocsFound := len(docs); {
|
||||||
|
case numDocsFound == 0:
|
||||||
|
return nil, document.ErrDocNotFound{Selector: selector}
|
||||||
|
case numDocsFound > 1:
|
||||||
|
return nil, document.ErrMultipleDocsFound{Selector: selector}
|
||||||
|
case numDocsFound == 1:
|
||||||
|
userDataDoc = docs[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally, try and retrieve the data we want from the document
|
||||||
|
userData, err := decodeData(userDataDoc, userDataKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return userData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNetworkData(docBundle document.Bundle) ([]byte, error) {
|
||||||
|
// find the baremetal host indicated as the ephemeral node
|
||||||
|
selector := document.NewSelector().ByKind(BareMetalHostKind).ByLabel(EphemeralHostLabel)
|
||||||
|
docs, err := docBundle.Select(selector)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var bmhDoc document.Document = &document.Factory{}
|
||||||
|
switch numDocsFound := len(docs); {
|
||||||
|
case numDocsFound == 0:
|
||||||
|
return nil, document.ErrDocNotFound{Selector: selector}
|
||||||
|
case numDocsFound > 1:
|
||||||
|
return nil, document.ErrMultipleDocsFound{Selector: selector}
|
||||||
|
case numDocsFound == 1:
|
||||||
|
bmhDoc = docs[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract the network data document pointer from the bmh document
|
||||||
|
netConfDocName, err := bmhDoc.GetString("spec.networkData.name")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
netConfDocNamespace, err := bmhDoc.GetString("spec.networkData.namespace")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// try and find these documents in our bundle
|
||||||
|
selector = document.NewSelector().ByKind(NetworkDataKind).ByNamespace(netConfDocNamespace).ByName(netConfDocName)
|
||||||
|
docs, err = docBundle.Select(selector)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var networkDataDoc document.Document = &document.Factory{}
|
||||||
|
switch numDocsFound := len(docs); {
|
||||||
|
case numDocsFound == 0:
|
||||||
|
return nil, document.ErrDocNotFound{Selector: selector}
|
||||||
|
case numDocsFound > 1:
|
||||||
|
return nil, document.ErrMultipleDocsFound{Selector: selector}
|
||||||
|
case numDocsFound == 1:
|
||||||
|
networkDataDoc = docs[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally, try and retrieve the data we want from the document
|
||||||
|
netData, err := decodeData(networkDataDoc, networkDataKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return netData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeData(cfg document.Document, key string) ([]byte, error) {
|
||||||
|
var needsBase64Decode = false
|
||||||
|
|
||||||
|
// TODO(alanmeadows): distinguish between missing net-data key
|
||||||
|
// and missing data/stringData keys in the Secret
|
||||||
|
data, err := cfg.GetStringMap("data")
|
||||||
|
if err == nil {
|
||||||
|
needsBase64Decode = true
|
||||||
|
} else {
|
||||||
|
// we'll catch any error below
|
||||||
|
data, err = cfg.GetStringMap("stringData")
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrDataNotSupplied{DocName: cfg.GetName(), Key: "data or stringData"}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res, ok := data[key]
|
res, ok := data[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
return decodeData(cfg, key)
|
return nil, ErrDataNotSupplied{DocName: cfg.GetName(), Key: key}
|
||||||
|
}
|
||||||
|
|
||||||
|
if needsBase64Decode {
|
||||||
|
return b64.StdEncoding.DecodeString(res)
|
||||||
}
|
}
|
||||||
return []byte(res), nil
|
return []byte(res), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCloudData reads YAML document input and generates cloud-init data for
|
|
||||||
// node (i.e. Cluster API Machine) with bootstrap label.
|
|
||||||
func GetCloudData(docBundle document.Bundle, bsSelector string) ([]byte, []byte, error) {
|
|
||||||
var userData []byte
|
|
||||||
var netConf []byte
|
|
||||||
docs, err := docBundle.GetByLabel(bsSelector)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
var ephemeralCfg document.Document
|
|
||||||
for _, doc := range docs {
|
|
||||||
if doc.GetKind() == EphemeralClusterConfKind {
|
|
||||||
ephemeralCfg = doc
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ephemeralCfg == nil {
|
|
||||||
return nil, nil, document.ErrDocNotFound{
|
|
||||||
Selector: bsSelector,
|
|
||||||
Kind: EphemeralClusterConfKind,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
netConf, err = getDataFromSecret(ephemeralCfg, "netconfig")
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
userData, err = getDataFromSecret(ephemeralCfg, "userdata")
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return userData, netConf, nil
|
|
||||||
}
|
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"sigs.k8s.io/kustomize/v3/pkg/types"
|
||||||
|
|
||||||
"opendev.org/airship/airshipctl/pkg/document"
|
"opendev.org/airship/airshipctl/pkg/document"
|
||||||
"opendev.org/airship/airshipctl/testutil"
|
"opendev.org/airship/airshipctl/testutil"
|
||||||
@ -16,54 +17,90 @@ func TestGetCloudData(t *testing.T) {
|
|||||||
require.NoError(t, err, "Building Bundle Failed")
|
require.NoError(t, err, "Building Bundle Failed")
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
selector string
|
labelFilter string
|
||||||
expectedUserData []byte
|
expectedUserData []byte
|
||||||
expectedNetData []byte
|
expectedNetData []byte
|
||||||
expectedErr error
|
expectedErr error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
selector: "test=test",
|
labelFilter: "test=validdocset",
|
||||||
|
expectedUserData: []byte("cloud-init"),
|
||||||
|
expectedNetData: []byte("net-config"),
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelFilter: "test=ephemeralmissing",
|
||||||
expectedUserData: nil,
|
expectedUserData: nil,
|
||||||
expectedNetData: nil,
|
expectedNetData: nil,
|
||||||
expectedErr: document.ErrDocNotFound{
|
expectedErr: document.ErrDocNotFound{
|
||||||
Selector: "test=test",
|
Selector: document.NewSelector().
|
||||||
Kind: "Secret",
|
ByLabel("airshipit.org/ephemeral-node=true").
|
||||||
|
ByKind("BareMetalHost"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: "airshipit.org/ephemeral=false",
|
labelFilter: "test=ephemeralduplicate",
|
||||||
expectedUserData: nil,
|
expectedUserData: nil,
|
||||||
expectedNetData: nil,
|
expectedNetData: nil,
|
||||||
expectedErr: ErrDataNotSupplied{
|
expectedErr: document.ErrMultipleDocsFound{
|
||||||
DocName: "node1-bmc-secret1",
|
Selector: document.NewSelector().
|
||||||
Key: "netconfig",
|
ByLabel("airshipit.org/ephemeral-node=true").
|
||||||
|
ByKind("BareMetalHost"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: "test=nodataforcfg",
|
labelFilter: "test=networkdatabadpointer",
|
||||||
expectedUserData: nil,
|
expectedUserData: nil,
|
||||||
expectedNetData: nil,
|
expectedNetData: nil,
|
||||||
expectedErr: ErrDataNotSupplied{
|
expectedErr: document.ErrDocNotFound{
|
||||||
DocName: "node1-bmc-secret2",
|
Selector: document.NewSelector().
|
||||||
Key: "netconfig",
|
ByKind("Secret").
|
||||||
|
ByNamespace("networkdatabadpointer-missing").
|
||||||
|
ByName("networkdatabadpointer-missing"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: "airshipit.org/ephemeral=true",
|
labelFilter: "test=networkdatamalformed",
|
||||||
expectedUserData: []byte("cloud-init"),
|
expectedUserData: nil,
|
||||||
expectedNetData: []byte("netconfig\n"),
|
expectedNetData: nil,
|
||||||
expectedErr: nil,
|
expectedErr: ErrDataNotSupplied{DocName: "networkdatamalformed-malformed", Key: networkDataKey},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: "some-data in (true, True)",
|
labelFilter: "test=networkdatamissing",
|
||||||
expectedUserData: []byte("cloud-init"),
|
expectedUserData: nil,
|
||||||
expectedNetData: []byte("netconfig\n"),
|
expectedNetData: nil,
|
||||||
expectedErr: nil,
|
expectedErr: types.NoFieldError{Field: "spec.networkData.name"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelFilter: "test=userdatamalformed",
|
||||||
|
expectedUserData: nil,
|
||||||
|
expectedNetData: nil,
|
||||||
|
expectedErr: ErrDataNotSupplied{DocName: "userdatamalformed-somesecret", Key: userDataKey},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
labelFilter: "test=userdatamissing",
|
||||||
|
expectedUserData: nil,
|
||||||
|
expectedNetData: nil,
|
||||||
|
expectedErr: document.ErrDocNotFound{
|
||||||
|
Selector: document.NewSelector().
|
||||||
|
ByKind("Secret").
|
||||||
|
ByLabel("airshipit.org/ephemeral-user-data=true"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
actualUserData, actualNetData, actualErr := GetCloudData(bundle, tt.selector)
|
// prune the bundle down using the label filter for the specific test
|
||||||
|
selector := document.NewSelector().ByLabel(tt.labelFilter)
|
||||||
|
filteredBundle, err := bundle.SelectBundle(selector)
|
||||||
|
require.NoError(t, err, "Building filtered bundle for %s failed", tt.labelFilter)
|
||||||
|
|
||||||
|
// ensure each test case filter has at least one document
|
||||||
|
docs, err := filteredBundle.GetAllDocuments()
|
||||||
|
require.NoError(t, err, "GetAllDocuments failed")
|
||||||
|
require.NotZero(t, docs)
|
||||||
|
|
||||||
|
actualUserData, actualNetData, actualErr := GetCloudData(filteredBundle)
|
||||||
|
|
||||||
assert.Equal(t, tt.expectedUserData, actualUserData)
|
assert.Equal(t, tt.expectedUserData, actualUserData)
|
||||||
assert.Equal(t, tt.expectedNetData, actualNetData)
|
assert.Equal(t, tt.expectedNetData, actualNetData)
|
||||||
|
@ -11,6 +11,17 @@ type ErrDataNotSupplied struct {
|
|||||||
Key string
|
Key string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrDuplicateNetworkDataDocuments error returned if multiple network documents
|
||||||
|
// were found with the same name in the same namespace
|
||||||
|
type ErrDuplicateNetworkDataDocuments struct {
|
||||||
|
DocName string
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
|
||||||
func (e ErrDataNotSupplied) Error() string {
|
func (e ErrDataNotSupplied) Error() string {
|
||||||
return fmt.Sprintf("Document %s has no key %s", e.DocName, e.Key)
|
return fmt.Sprintf("Document %s has no key %s", e.DocName, e.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e ErrDuplicateNetworkDataDocuments) Error() string {
|
||||||
|
return fmt.Sprintf("Found more than one document with the name %s in namespace %s", e.DocName, e.Namespace)
|
||||||
|
}
|
||||||
|
36
pkg/bootstrap/cloudinit/testdata/ephemeralduplicate.yaml
vendored
Normal file
36
pkg/bootstrap/cloudinit/testdata/ephemeralduplicate.yaml
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# in this document set, we have no ephemerally labeled node
|
||||||
|
# which should cause an error
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: ephemeralduplicate
|
||||||
|
name: ephemeralduplicate
|
||||||
|
type: Opaque
|
||||||
|
---
|
||||||
|
apiVersion: metal3.io/v1alpha1
|
||||||
|
kind: BareMetalHost
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: ephemeralduplicate
|
||||||
|
airshipit.org/ephemeral-node: 'true'
|
||||||
|
name: ephemeralduplicate-master-1
|
||||||
|
---
|
||||||
|
apiVersion: metal3.io/v1alpha1
|
||||||
|
kind: BareMetalHost
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: ephemeralduplicate
|
||||||
|
airshipit.org/ephemeral-node: 'true'
|
||||||
|
name: ephemeralduplicate-master-2
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: 'true'
|
||||||
|
test: ephemeralduplicate
|
||||||
|
name: ephemeralduplicate-airship-isogen-userdata
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
userData: cloudinit
|
27
pkg/bootstrap/cloudinit/testdata/ephemeralmissing.yaml
vendored
Normal file
27
pkg/bootstrap/cloudinit/testdata/ephemeralmissing.yaml
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# in this document set, we have no ephemerally labeled node
|
||||||
|
# which should cause an error
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: ephemeralmissing
|
||||||
|
name: ephemeralmissing
|
||||||
|
type: Opaque
|
||||||
|
---
|
||||||
|
apiVersion: metal3.io/v1alpha1
|
||||||
|
kind: BareMetalHost
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: ephemeralmissing
|
||||||
|
name: ephemeralmissing-master-1
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: 'true'
|
||||||
|
test: ephemeralmissing
|
||||||
|
name: ephemeralmissing-airship-isogen-userdata
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
userData: cloud-init
|
@ -1,2 +1,9 @@
|
|||||||
resources:
|
resources:
|
||||||
- secret.yaml
|
- ephemeralduplicate.yaml
|
||||||
|
- ephemeralmissing.yaml
|
||||||
|
- networkdatabadpointer.yaml
|
||||||
|
- networkdatamalformed.yaml
|
||||||
|
- networkdatamissing.yaml
|
||||||
|
- userdatamalformed.yaml
|
||||||
|
- userdatamissing.yaml
|
||||||
|
- validdocset.yaml
|
38
pkg/bootstrap/cloudinit/testdata/networkdatabadpointer.yaml
vendored
Normal file
38
pkg/bootstrap/cloudinit/testdata/networkdatabadpointer.yaml
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# in this document set, we have an ephemeral node however
|
||||||
|
# it lacks a networkData clause
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: networkdatabadpointer
|
||||||
|
name: networkdatabadpointer-master-1-bmc
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
username: foobar
|
||||||
|
password: goober
|
||||||
|
---
|
||||||
|
apiVersion: metal3.io/v1alpha1
|
||||||
|
kind: BareMetalHost
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-node: 'true'
|
||||||
|
test: networkdatabadpointer
|
||||||
|
name: networkdatabadpointer-master-1
|
||||||
|
spec:
|
||||||
|
bmc:
|
||||||
|
address: ipmi://127.0.0.1
|
||||||
|
credentialsName: networkdatabadpointer-master-1-bmc
|
||||||
|
networkData:
|
||||||
|
name: networkdatabadpointer-missing
|
||||||
|
namespace: networkdatabadpointer-missing
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: 'true'
|
||||||
|
test: networkdatabadpointer
|
||||||
|
name: networkdatabadpointer-airship-isogen-userdata
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
userData: cloud-init
|
50
pkg/bootstrap/cloudinit/testdata/networkdatamalformed.yaml
vendored
Normal file
50
pkg/bootstrap/cloudinit/testdata/networkdatamalformed.yaml
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# in this document set, we have an ephemeral node with
|
||||||
|
# resolvable network data, but it is malformed lacking
|
||||||
|
# the proper field
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: networkdatamalformed
|
||||||
|
name: networkdatamalformed-master-1-bmc
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
username: foobar
|
||||||
|
password: goober
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
namespace: malformed
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: networkdatamalformed
|
||||||
|
name: networkdatamalformed-malformed
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
no-net-data-key: the required 'net-data' key is missing
|
||||||
|
---
|
||||||
|
apiVersion: metal3.io/v1alpha1
|
||||||
|
kind: BareMetalHost
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-node: 'true'
|
||||||
|
test: networkdatamalformed
|
||||||
|
name: networkdatamalformed-master-1
|
||||||
|
spec:
|
||||||
|
bmc:
|
||||||
|
address: ipmi://127.0.0.1
|
||||||
|
credentialsName: networkdatamalformed-master-1-bmc
|
||||||
|
networkData:
|
||||||
|
name: networkdatamalformed-malformed
|
||||||
|
namespace: malformed
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: 'true'
|
||||||
|
test: networkdatamalformed
|
||||||
|
name: networkdatamalformed-airship-isogen-userdata
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
userData: cloud-init
|
46
pkg/bootstrap/cloudinit/testdata/networkdatamissing.yaml
vendored
Normal file
46
pkg/bootstrap/cloudinit/testdata/networkdatamissing.yaml
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# in this document set, we have an ephemeral node with
|
||||||
|
# but it lacks a networkData clause
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: networkdatamissing
|
||||||
|
name: networkdatamissing-master-1-bmc
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
username: foobar
|
||||||
|
password: goober
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
namespace: missing
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: missing
|
||||||
|
name: networkdatamissing-missing
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
networkData: there is network data here, but we have no reference to it
|
||||||
|
---
|
||||||
|
apiVersion: metal3.io/v1alpha1
|
||||||
|
kind: BareMetalHost
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-node: 'true'
|
||||||
|
test: networkdatamissing
|
||||||
|
name: networkdatamissing-master-1
|
||||||
|
spec:
|
||||||
|
bmc:
|
||||||
|
address: ipmi://127.0.0.1
|
||||||
|
credentialsName: networkdatamissing-master-1-bmc
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: 'true'
|
||||||
|
test: networkdatamissing
|
||||||
|
name: networkdatamissing-airship-isogen-userdata
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
userData: cloud-init
|
41
pkg/bootstrap/cloudinit/testdata/secret.yaml
vendored
41
pkg/bootstrap/cloudinit/testdata/secret.yaml
vendored
@ -1,41 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
airshipit.org/ephemeral: "true"
|
|
||||||
name: node1-bmc-secret
|
|
||||||
type: Opaque
|
|
||||||
data:
|
|
||||||
netconfig: bmV0Y29uZmlnCg==
|
|
||||||
stringData:
|
|
||||||
userdata: cloud-init
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
airshipit.org/ephemeral: "false"
|
|
||||||
name: node1-bmc-secret1
|
|
||||||
type: Opaque
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
test: nodataforcfg
|
|
||||||
name: node1-bmc-secret2
|
|
||||||
type: Opaque
|
|
||||||
data:
|
|
||||||
foo: bmV0Y29uZmlnCg==
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
some-data: "True"
|
|
||||||
name: node1-bmc-in-secret2
|
|
||||||
type: Opaque
|
|
||||||
data:
|
|
||||||
netconfig: bmV0Y29uZmlnCg==
|
|
||||||
stringData:
|
|
||||||
userdata: cloud-init
|
|
12
pkg/bootstrap/cloudinit/testdata/userdatamalformed.yaml
vendored
Normal file
12
pkg/bootstrap/cloudinit/testdata/userdatamalformed.yaml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# in this document set, we have a secret that contains a label for our
|
||||||
|
# iso generation userdata, but it is malformed lacking a user-data key
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: 'true'
|
||||||
|
test: userdatamalformed
|
||||||
|
name: userdatamalformed-somesecret
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
no-user-data: this secret has the right label but is missing the 'user-data' key
|
11
pkg/bootstrap/cloudinit/testdata/userdatamissing.yaml
vendored
Normal file
11
pkg/bootstrap/cloudinit/testdata/userdatamissing.yaml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# in this document set, we lack a document that contains our ephemeral
|
||||||
|
# iso generation userdata
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: userdatamissing
|
||||||
|
name: userdatamissing-somesecret
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
userData: "this secret lacks the label airshipit.org/ephemeral-user-data: true"
|
66
pkg/bootstrap/cloudinit/testdata/validdocset.yaml
vendored
Normal file
66
pkg/bootstrap/cloudinit/testdata/validdocset.yaml
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# in this document set, we have an ephemeral node with
|
||||||
|
# the right label, resolvable/valid network data and
|
||||||
|
# a user-data secret with the right label
|
||||||
|
#
|
||||||
|
# we also introduce a second baremetal host that is not
|
||||||
|
# labeled as the ephemeral node to facilitate testing
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: validdocset
|
||||||
|
name: master-1-bmc
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
username: foobar
|
||||||
|
password: goober
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: 'true'
|
||||||
|
test: validdocset
|
||||||
|
name: airship-isogen-userdata
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
userData: cloud-init
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
namespace: metal3
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: validdocset
|
||||||
|
name: master-1-networkdata
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
networkData: net-config
|
||||||
|
---
|
||||||
|
apiVersion: metal3.io/v1alpha1
|
||||||
|
kind: BareMetalHost
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: validdocset
|
||||||
|
name: master-2
|
||||||
|
bmc:
|
||||||
|
address: ipmi://127.0.0.1
|
||||||
|
credentialsName: master-2-bmc
|
||||||
|
networkData:
|
||||||
|
name: master-2-networkdata
|
||||||
|
namespace: metal3
|
||||||
|
---
|
||||||
|
apiVersion: metal3.io/v1alpha1
|
||||||
|
kind: BareMetalHost
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-node: 'true'
|
||||||
|
test: validdocset
|
||||||
|
name: master-1
|
||||||
|
spec:
|
||||||
|
bmc:
|
||||||
|
address: ipmi://127.0.0.1
|
||||||
|
credentialsName: master-1-bmc
|
||||||
|
networkData:
|
||||||
|
name: master-1-networkdata
|
||||||
|
namespace: metal3
|
@ -119,8 +119,7 @@ func generateBootstrapIso(
|
|||||||
) error {
|
) error {
|
||||||
cntVol := strings.Split(cfg.Container.Volume, ":")[1]
|
cntVol := strings.Split(cfg.Container.Volume, ":")[1]
|
||||||
log.Print("Creating cloud-init for ephemeral K8s")
|
log.Print("Creating cloud-init for ephemeral K8s")
|
||||||
label := document.EphemeralClusterSelector
|
userData, netConf, err := cloudinit.GetCloudData(docBundle)
|
||||||
userData, netConf, err := cloudinit.GetCloudData(docBundle, label)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
65
pkg/bootstrap/isogen/testdata/secret.yaml
vendored
65
pkg/bootstrap/isogen/testdata/secret.yaml
vendored
@ -1,11 +1,66 @@
|
|||||||
|
# in this document set, we have an ephemeral node with
|
||||||
|
# the right label, resolvable/valid network data and
|
||||||
|
# a user-data secret with the right label
|
||||||
|
#
|
||||||
|
# we also introduce a second baremetal host that is not
|
||||||
|
# labeled as the ephemeral node to facilitate testing
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
airshipit.org/ephemeral: "true"
|
test: validdocset
|
||||||
name: node1-bmc-secret
|
name: master-1-bmc
|
||||||
type: Opaque
|
type: Opaque
|
||||||
data:
|
|
||||||
netconfig: bmV0Y29uZmlnCg==
|
|
||||||
stringData:
|
stringData:
|
||||||
userdata: cloud-init
|
username: foobar
|
||||||
|
password: goober
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-user-data: 'true'
|
||||||
|
test: validdocset
|
||||||
|
name: airship-isogen-userdata
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
userData: cloud-init
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
namespace: metal3
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: validdocset
|
||||||
|
name: master-1-networkdata
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
networkData: net-config
|
||||||
|
---
|
||||||
|
apiVersion: metal3.io/v1alpha1
|
||||||
|
kind: BareMetalHost
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
test: validdocset
|
||||||
|
name: master-2
|
||||||
|
bmc:
|
||||||
|
address: ipmi://127.0.0.1
|
||||||
|
credentialsName: master-2-bmc
|
||||||
|
networkData:
|
||||||
|
name: master-2-networkdata
|
||||||
|
namespace: metal3
|
||||||
|
---
|
||||||
|
apiVersion: metal3.io/v1alpha1
|
||||||
|
kind: BareMetalHost
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
airshipit.org/ephemeral-node: 'true'
|
||||||
|
test: validdocset
|
||||||
|
name: master-1
|
||||||
|
spec:
|
||||||
|
bmc:
|
||||||
|
address: ipmi://127.0.0.1
|
||||||
|
credentialsName: master-1-bmc
|
||||||
|
networkData:
|
||||||
|
name: master-1-networkdata
|
||||||
|
namespace: metal3
|
@ -77,7 +77,7 @@ func (infra *Infra) Deploy() error {
|
|||||||
}
|
}
|
||||||
if len(docs) == 0 {
|
if len(docs) == 0 {
|
||||||
return document.ErrDocNotFound{
|
return document.ErrDocNotFound{
|
||||||
Selector: ls,
|
Selector: selector,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ type Bundle interface {
|
|||||||
SetFileSystem(FileSystem) error
|
SetFileSystem(FileSystem) error
|
||||||
GetFileSystem() FileSystem
|
GetFileSystem() FileSystem
|
||||||
Select(selector Selector) ([]Document, error)
|
Select(selector Selector) ([]Document, error)
|
||||||
|
SelectBundle(selector Selector) (Bundle, error)
|
||||||
GetByGvk(string, string, string) ([]Document, error)
|
GetByGvk(string, string, string) ([]Document, error)
|
||||||
GetByName(string) (Document, error)
|
GetByName(string) (Document, error)
|
||||||
GetByAnnotation(annotationSelector string) ([]Document, error)
|
GetByAnnotation(annotationSelector string) ([]Document, error)
|
||||||
@ -207,6 +208,37 @@ func (b *BundleFactory) Select(selector Selector) ([]Document, error) {
|
|||||||
return docSet, err
|
return docSet, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelectBundle offers an interface to pass a Selector, built on top of kustomize Selector
|
||||||
|
// to the bundle returning a new Bundle that matches the criteria. This is useful
|
||||||
|
// where you want to actually prune the underlying bundle you are working with
|
||||||
|
// rather then getting back the matching documents for scenarios like
|
||||||
|
// test cases where you want to pass in custom "filtered" bundles
|
||||||
|
// specific to the test case
|
||||||
|
func (b *BundleFactory) SelectBundle(selector Selector) (Bundle, error) {
|
||||||
|
// use the kustomize select method
|
||||||
|
resources, err := b.ResMap.Select(selector.Selector)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a blank resourcemap and append the found resources
|
||||||
|
// into the new resource map
|
||||||
|
resourceMap := resmap.New()
|
||||||
|
for _, res := range resources {
|
||||||
|
if err = resourceMap.Append(res); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return a new bundle with the same options and filesystem
|
||||||
|
// as this one but with a reduced resourceMap
|
||||||
|
return &BundleFactory{
|
||||||
|
KustomizeBuildOptions: b.KustomizeBuildOptions,
|
||||||
|
ResMap: resourceMap,
|
||||||
|
FileSystem: b.FileSystem,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetByAnnotation is a convenience method to get documents for a particular annotation
|
// GetByAnnotation is a convenience method to get documents for a particular annotation
|
||||||
func (b *BundleFactory) GetByAnnotation(annotationSelector string) ([]Document, error) {
|
func (b *BundleFactory) GetByAnnotation(annotationSelector string) ([]Document, error) {
|
||||||
// Construct kustomize annotation selector
|
// Construct kustomize annotation selector
|
||||||
|
@ -6,10 +6,18 @@ import (
|
|||||||
|
|
||||||
// ErrDocNotFound returned if desired document not found
|
// ErrDocNotFound returned if desired document not found
|
||||||
type ErrDocNotFound struct {
|
type ErrDocNotFound struct {
|
||||||
Selector string
|
Selector Selector
|
||||||
Kind string
|
}
|
||||||
|
|
||||||
|
// ErrMultipleDocsFound returned if desired document not found
|
||||||
|
type ErrMultipleDocsFound struct {
|
||||||
|
Selector Selector
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ErrDocNotFound) Error() string {
|
func (e ErrDocNotFound) Error() string {
|
||||||
return fmt.Sprintf("Document filtered by selector %s with Kind %s not found", e.Selector, e.Kind)
|
return fmt.Sprintf("Document filtered by selector %q found no documents", e.Selector)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrMultipleDocsFound) Error() string {
|
||||||
|
return fmt.Sprintf("Document filtered by selector %q found more than one document", e.Selector)
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,24 @@ func (s Selector) ByName(name string) Selector {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ByNamespace select by namepace
|
||||||
|
func (s Selector) ByNamespace(namespace string) Selector {
|
||||||
|
s.Namespace = namespace
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// ByGvk select by gvk
|
// ByGvk select by gvk
|
||||||
func (s Selector) ByGvk(group, version, kind string) Selector {
|
func (s Selector) ByGvk(group, version, kind string) Selector {
|
||||||
s.Gvk = gvk.Gvk{Group: group, Version: version, Kind: kind}
|
s.Gvk = gvk.Gvk{Group: group, Version: version, Kind: kind}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ByKind select by Kind
|
||||||
|
func (s Selector) ByKind(kind string) Selector {
|
||||||
|
s.Gvk = gvk.Gvk{Kind: kind}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// ByLabel select by label selector
|
// ByLabel select by label selector
|
||||||
func (s Selector) ByLabel(labelSelector string) Selector {
|
func (s Selector) ByLabel(labelSelector string) Selector {
|
||||||
if s.LabelSelector != "" {
|
if s.LabelSelector != "" {
|
||||||
|
@ -95,8 +95,7 @@ func getRemoteDirectConfig(settings *environment.AirshipCTLSettings) (*config.Re
|
|||||||
}
|
}
|
||||||
if len(docs) == 0 {
|
if len(docs) == 0 {
|
||||||
return nil, "", document.ErrDocNotFound{
|
return nil, "", document.ErrDocNotFound{
|
||||||
Selector: ls,
|
Selector: selector,
|
||||||
Kind: AirshipHostKind,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user