Use bundle mock for bootstrap tests

Mock the usage of the bundle object so that the testdata files
can be removed. The necessary data from each file was exported
to the cloud-init_testutils.go file so that the mocked functions
would behave identically as before this change.

Relates-To: #464
Relates-To: #465
Change-Id: I222da08cf2e1dc082f8219a826b21f23a43f5a69
This commit is contained in:
Herrera, Josh (jh813b) 2021-08-19 16:53:53 -07:00 committed by Josh Herrera
parent fc20c79e47
commit 0254fa6a03
12 changed files with 343 additions and 355 deletions

View File

@ -35,24 +35,22 @@ var (
} }
validSelectors = selectors{ validSelectors = selectors{
userDataSelector: document.NewSelector(). userDataSelector: document.NewSelector().
ByKind("Secret"). ByKind(secret).
ByLabel("airshipit.org/ephemeral-user-data in (True, true)"), ByLabel(ephUser),
userDataKey: defaultUserDataKey, userDataKey: defaultUserDataKey,
networkConfigSelector: document.NewSelector(). networkConfigSelector: document.NewSelector().
ByKind("BareMetalHost"). ByKind(bmh).
ByLabel("airshipit.org/ephemeral-node in (True, true)"), ByLabel(ephNode),
networkConfigKey: defaultNetworkConfigKey, networkConfigKey: defaultNetworkConfigKey,
} }
) )
func TestGetCloudData(t *testing.T) { func TestGetCloudData(t *testing.T) {
bundle, err := document.NewBundleByPath("testdata")
require.NoError(t, err, "Building Bundle Failed")
tests := []struct { tests := []struct {
name string name string
selectors selectors
labelFilter string labelFilter string
testBundle document.Bundle
expectedUserData []byte expectedUserData []byte
expectedNetData []byte expectedNetData []byte
expectedErr error expectedErr error
@ -64,6 +62,18 @@ func TestGetCloudData(t *testing.T) {
expectedUserData: []byte("cloud-init"), expectedUserData: []byte("cloud-init"),
expectedNetData: []byte("net-config"), expectedNetData: []byte("net-config"),
expectedErr: nil, expectedErr: nil,
testBundle: createTestBundle(t, testData{
docsCfg: getValidDocSet(),
secret: "Secret",
bmh: "BareMetalHost",
ephNode: "airshipit.org/ephemeral-node in (True, true)",
ephUser: "airshipit.org/ephemeral-user-data in (True, true)",
selectorNamespace: "metal3",
selectorName: "master-1-networkdata",
mockErr1: nil,
mockErr2: nil,
mockErr3: nil,
}),
}, },
{ {
name: "BareMetalHost document not found", name: "BareMetalHost document not found",
@ -74,8 +84,24 @@ func TestGetCloudData(t *testing.T) {
expectedErr: document.ErrDocNotFound{ expectedErr: document.ErrDocNotFound{
Selector: document.NewSelector(). Selector: document.NewSelector().
ByLabel(document.EphemeralHostSelector). ByLabel(document.EphemeralHostSelector).
ByKind("BareMetalHost"), ByKind(bmh),
}, },
testBundle: createTestBundle(t, testData{
docsCfg: getEphemeralMissing(),
secret: "Secret",
bmh: "BareMetalHost",
ephNode: "airshipit.org/ephemeral-node in (True, true)",
ephUser: "airshipit.org/ephemeral-user-data in (True, true)",
selectorNamespace: "",
selectorName: "",
mockErr1: nil,
mockErr2: document.ErrDocNotFound{
Selector: document.NewSelector().
ByLabel(document.EphemeralHostSelector).
ByKind(bmh),
},
mockErr3: nil,
}),
}, },
{ {
name: "BareMetalHost document duplication", name: "BareMetalHost document duplication",
@ -86,8 +112,24 @@ func TestGetCloudData(t *testing.T) {
expectedErr: document.ErrMultiDocsFound{ expectedErr: document.ErrMultiDocsFound{
Selector: document.NewSelector(). Selector: document.NewSelector().
ByLabel(document.EphemeralHostSelector). ByLabel(document.EphemeralHostSelector).
ByKind("BareMetalHost"), ByKind(bmh),
}, },
testBundle: createTestBundle(t, testData{
docsCfg: getEphemeralDuplicate(),
secret: "Secret",
bmh: "BareMetalHost",
ephNode: "airshipit.org/ephemeral-node in (True, true)",
ephUser: "airshipit.org/ephemeral-user-data in (True, true)",
selectorNamespace: "",
selectorName: "",
mockErr1: nil,
mockErr2: document.ErrMultiDocsFound{
Selector: document.NewSelector().
ByLabel(document.EphemeralHostSelector).
ByKind(bmh),
},
mockErr3: nil,
}),
}, },
{ {
name: "Bad network data document reference", name: "Bad network data document reference",
@ -97,10 +139,27 @@ func TestGetCloudData(t *testing.T) {
expectedNetData: nil, expectedNetData: nil,
expectedErr: document.ErrDocNotFound{ expectedErr: document.ErrDocNotFound{
Selector: document.NewSelector(). Selector: document.NewSelector().
ByKind("Secret"). ByKind(secret).
ByNamespace("networkdatabadpointer-missing"). ByNamespace("networkdatabadpointer-missing").
ByName("networkdatabadpointer-missing"), ByName("networkdatabadpointer-missing"),
}, },
testBundle: createTestBundle(t, testData{
docsCfg: getNetworkBadPointer(),
secret: "Secret",
bmh: "BareMetalHost",
ephNode: "airshipit.org/ephemeral-node in (True, true)",
ephUser: "airshipit.org/ephemeral-user-data in (True, true)",
selectorNamespace: "metal3",
selectorName: "master-1-networkdata",
mockErr1: nil,
mockErr2: nil,
mockErr3: document.ErrDocNotFound{
Selector: document.NewSelector().
ByKind(secret).
ByNamespace("networkdatabadpointer-missing").
ByName("networkdatabadpointer-missing"),
},
}),
}, },
{ {
name: "Bad network data document structure", name: "Bad network data document structure",
@ -112,6 +171,18 @@ func TestGetCloudData(t *testing.T) {
DocName: "networkdatamalformed-malformed", DocName: "networkdatamalformed-malformed",
Key: defaultNetworkConfigKey, Key: defaultNetworkConfigKey,
}, },
testBundle: createTestBundle(t, testData{
docsCfg: getNetDataMalformed(),
secret: "Secret",
bmh: "BareMetalHost",
ephNode: "airshipit.org/ephemeral-node in (True, true)",
ephUser: "airshipit.org/ephemeral-user-data in (True, true)",
selectorNamespace: "metal3",
selectorName: "master-1-networkdata",
mockErr1: nil,
mockErr2: nil,
mockErr3: nil,
}),
}, },
{ {
name: "Bad user data document structure", name: "Bad user data document structure",
@ -123,6 +194,18 @@ func TestGetCloudData(t *testing.T) {
DocName: "userdatamalformed-somesecret", DocName: "userdatamalformed-somesecret",
Key: defaultUserDataKey, Key: defaultUserDataKey,
}, },
testBundle: createTestBundle(t, testData{
docsCfg: getUserDataMalformed(),
secret: "Secret",
bmh: "BareMetalHost",
ephNode: "airshipit.org/ephemeral-node in (True, true)",
ephUser: "airshipit.org/ephemeral-user-data in (True, true)",
selectorNamespace: "",
selectorName: "",
mockErr1: nil,
mockErr2: nil,
mockErr3: nil,
}),
}, },
{ {
name: "User data document not found", name: "User data document not found",
@ -132,26 +215,36 @@ func TestGetCloudData(t *testing.T) {
expectedNetData: nil, expectedNetData: nil,
expectedErr: document.ErrDocNotFound{ expectedErr: document.ErrDocNotFound{
Selector: document.NewSelector(). Selector: document.NewSelector().
ByKind("Secret"). ByKind(secret).
ByLabel(document.EphemeralUserDataSelector), ByLabel(document.EphemeralUserDataSelector),
}, },
testBundle: createTestBundle(t, testData{
docsCfg: getUserDataMissing(),
secret: "Secret",
bmh: "BareMetalHost",
ephNode: "airshipit.org/ephemeral-node in (True, true)",
ephUser: "airshipit.org/ephemeral-user-data in (True, true)",
selectorNamespace: "",
selectorName: "",
mockErr1: document.ErrDocNotFound{
Selector: document.NewSelector().
ByKind(secret).
ByLabel(document.EphemeralUserDataSelector),
},
mockErr2: nil,
mockErr3: nil,
}),
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
// 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 // ensure each test case filter has at least one document
docs, err := filteredBundle.GetAllDocuments() docs, err := tt.testBundle.GetAllDocuments()
require.NoError(t, err, "GetAllDocuments failed") require.NoError(t, err, "GetAllDocuments failed")
require.NotZero(t, docs) require.NotZero(t, docs)
actualUserData, actualNetData, actualErr := GetCloudData( actualUserData, actualNetData, actualErr := GetCloudData(
filteredBundle, tt.testBundle,
tt.userDataSelector, tt.userDataSelector,
tt.userDataKey, tt.userDataKey,
tt.networkConfigSelector, tt.networkConfigSelector,

View File

@ -0,0 +1,231 @@
/*
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 cloudinit
import (
"strings"
"testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"opendev.org/airship/airshipctl/pkg/document"
testdoc "opendev.org/airship/airshipctl/testutil/document"
)
type testData struct {
docsCfg []string
secret string
ephUser string
ephNode string
bmh string
selectorNamespace string
selectorName string
mockErr1 interface{}
mockErr2 interface{}
mockErr3 interface{}
}
const (
ephNode = "airshipit.org/ephemeral-node in (True, true)"
secret = "Secret"
bmh = "BareMetalHost"
ephUser = "airshipit.org/ephemeral-user-data in (True, true)"
ephUserData = `apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/ephemeral-user-data: 'true'
name: networkdatamalformed-malformed
type: Opaque
stringData:
userData: cloud-init
`
ephUserDataMalformed = `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
`
ephUserDataNoLabel = `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"`
ephUserDataCredentials = `apiVersion: v1
kind: Secret
metadata:
labels:
name: master-1-bmc
type: Opaque
stringData:
username: foobar
password: goober
`
bmhMaster1 = `apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
labels:
airshipit.org/ephemeral-node: 'true'
name: master-1
spec:
bmc:
address: ipmi://127.0.0.1
credentialsName: master-1-bmc
networkData:
name: master-1-networkdata
namespace: metal3
`
netDataMalFormed = `apiVersion: v1
kind: Secret
metadata:
labels:
name: networkdatamalformed-malformed
namespace: malformed
type: Opaque
stringData:
no-net-data-key: the required 'net-data' key is missing
`
bmhDuplicate = `apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
labels:
test: ephemeralduplicate
airshipit.org/ephemeral-node: 'true'
name: ephemeralduplicate-master-1
`
ephMissing = `apiVersion: v1
kind: Secret
metadata:
labels:
test: ephemeralmissing
name: ephemeralmissing
type: Opaque
`
ephNetValid = `apiVersion: v1
kind: Secret
metadata:
labels:
test: validdocset
name: master-1-networkdata
namespace: metal3
type: Opaque
stringData:
networkData: net-config
`
)
func getValidDocSet() []string {
return []string{
ephUserData,
bmhMaster1,
ephNetValid,
ephUserDataCredentials,
}
}
func getEphemeralMissing() []string {
return []string{
ephUserData,
ephMissing,
bmhMaster1,
}
}
func getEphemeralDuplicate() []string {
return []string{
ephUserData,
bmhDuplicate,
bmhDuplicate,
}
}
func getNetworkBadPointer() []string {
return []string{
ephUserData,
bmhMaster1,
ephUserDataCredentials,
}
}
func getNetDataMalformed() []string {
return []string{
ephUserData,
bmhMaster1,
netDataMalFormed,
ephUserDataCredentials,
}
}
func getUserDataMalformed() []string {
return []string{
ephUserDataMalformed,
}
}
func getUserDataMissing() []string {
return []string{
ephUserDataNoLabel,
}
}
func createTestBundle(t *testing.T, td testData) document.Bundle {
bundle := &testdoc.MockBundle{}
allDocs := make([]document.Document, len(td.docsCfg))
returnedDocs := make(map[string]document.Document)
for i, cfg := range td.docsCfg {
doc, err := document.NewDocumentFromBytes([]byte(cfg))
require.NoError(t, err)
allDocs[i] = doc
kind, selectorMap, name, namespace := doc.GetKind(), doc.GetLabels(), doc.GetName(), doc.GetNamespace()
// We use data in the document to determine which document should be returned from each mock
// function call.
if _, ok := selectorMap[strings.TrimSuffix(td.ephUser, " in (True, true)")]; ok && kind == td.secret {
returnedDocs["userDataDoc"] = doc
// initialize these two key-value pairs so that we avoid
// memory address errors. These will be overwritten.
returnedDocs["bmhDoc"] = doc
returnedDocs["ephOrBmhDoc"] = doc
} else if _, ok := selectorMap[strings.TrimSuffix(td.ephNode, " in (True, true)")]; ok && kind == td.bmh {
returnedDocs["bmhDoc"] = doc
} else if kind == td.secret && name == td.selectorName && namespace == td.selectorNamespace {
returnedDocs["ephOrBmhDoc"] = doc
}
}
bundle.On("GetAllDocuments").Return(allDocs, nil)
bundle.On("SelectOne", mock.MatchedBy(func(selector document.Selector) bool {
return selector.Kind == td.secret && selector.LabelSelector == td.ephUser
})).
Return(returnedDocs["userDataDoc"], td.mockErr1)
bundle.On("SelectOne", mock.MatchedBy(func(selector document.Selector) bool {
return selector.Kind == td.bmh && selector.LabelSelector == td.ephNode
})).
Return(returnedDocs["bmhDoc"], td.mockErr2)
bundle.On("SelectOne", mock.MatchedBy(func(selector document.Selector) bool {
return selector.Kind == td.secret && selector.Namespace == td.selectorNamespace && selector.Name == td.selectorName
})).
Return(returnedDocs["ephOrBmhDoc"], td.mockErr3)
return bundle
}

View File

@ -1,36 +0,0 @@
# 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

View File

@ -1,27 +0,0 @@
# 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

View File

@ -1,9 +0,0 @@
resources:
- ephemeralduplicate.yaml
- ephemeralmissing.yaml
- networkdatabadpointer.yaml
- networkdatamalformed.yaml
- networkdatamissing.yaml
- userdatamalformed.yaml
- userdatamissing.yaml
- validdocset.yaml

View File

@ -1,38 +0,0 @@
# 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

View File

@ -1,50 +0,0 @@
# 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
metadata:
labels:
test: networkdatamalformed
name: networkdatamalformed-malformed
namespace: 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

View File

@ -1,46 +0,0 @@
# 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

View File

@ -1,41 +0,0 @@
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/node-role: "control-plane"
name: node1-bmc-secret
type: Opaque
data:
netconfig: bmV0Y29uZmlnCg==
stringData:
userdata: cloud-init
---
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/node-role: "worker"
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

View File

@ -1,12 +0,0 @@
# 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

View File

@ -1,11 +0,0 @@
# 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"

View File

@ -1,66 +0,0 @@
# 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
metadata:
labels:
test: validdocset
name: master-1-networkdata
namespace: metal3
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