Commit 4d188bfd authored by Donnie Adams's avatar Donnie Adams
Browse files

Support passing files into machine provisioning job

Some of the arguments to rancher-machine are paths to files needed for
provisioning. These changes add support for passing these files into the
pod when necessary.
Showing with 123 additions and 49 deletions
+123 -49
......@@ -27,19 +27,19 @@ import (
var (
SchemaLock = sync.Mutex{}
driverLock = sync.Mutex{}
// Aliases maps Driver field => schema field
// DriverToSchemaFields maps Driver field => schema field
// The opposite of this lives in pkg/controllers/management/node/controller.go
Aliases = map[string]map[string]string{
"aliyunecs": map[string]string{"sshKeypath": "sshKeyContents"},
"amazonec2": map[string]string{"sshKeypath": "sshKeyContents", "userdata": "userdata"},
"azure": map[string]string{"customData": "customData"},
"digitalocean": map[string]string{"sshKeyPath": "sshKeyContents", "userdata": "userdata"},
"exoscale": map[string]string{"sshKey": "sshKey", "userdata": "userdata"},
"openstack": map[string]string{"cacert": "cacert", "privateKeyFile": "privateKeyFile", "userDataFile": "userDataFile"},
"otc": map[string]string{"privateKeyFile": "privateKeyFile"},
"packet": map[string]string{"userdata": "userdata"},
"vmwarevsphere": map[string]string{"cloud-config": "cloudConfig"},
"google": map[string]string{"authEncodedJson": "authEncodedJson"},
DriverToSchemaFields = map[string]map[string]string{
"aliyunecs": {"sshKeypath": "sshKeyContents"},
"amazonec2": {"sshKeypath": "sshKeyContents", "userdata": "userdata"},
"azure": {"customData": "customData"},
"digitalocean": {"sshKeyPath": "sshKeyContents", "userdata": "userdata"},
"exoscale": {"sshKey": "sshKey", "userdata": "userdata"},
"openstack": {"cacert": "cacert", "privateKeyFile": "privateKeyFile", "userDataFile": "userDataFile"},
"otc": {"privateKeyFile": "privateKeyFile"},
"packet": {"userdata": "userdata"},
"vmwarevsphere": {"cloud-config": "cloudConfig"},
"google": {"authEncodedJson": "authEncodedJson"},
}
SSHKeyFields = map[string]bool{
"sshKeyContents": true,
......@@ -189,7 +189,7 @@ func (m *Lifecycle) download(obj *v3.NodeDriver) (*v3.NodeDriver, error) {
if err != nil {
return nil, err
}
if aliases, ok := Aliases[driverName]; ok {
if aliases, ok := DriverToSchemaFields[driverName]; ok {
// convert path fields to their alias to take file contents
if alias, ok := aliases[name]; ok {
name = alias
......@@ -290,7 +290,7 @@ func (m *Lifecycle) checkDriverVersion(obj *v3.NodeDriver) bool {
driverName := strings.TrimPrefix(obj.Spec.DisplayName, drivers.DockerMachineDriverPrefix)
if _, ok := Aliases[driverName]; ok {
if _, ok := DriverToSchemaFields[driverName]; ok {
if val, ok := obj.Annotations[uiFieldHintsAnno]; !ok || val == "" {
return true
}
......@@ -322,7 +322,7 @@ func (m *Lifecycle) addVersionInfo(obj *v3.NodeDriver) *v3.NodeDriver {
}
func (m *Lifecycle) addUIHintsAnno(driverName string, obj *v3.NodeDriver) (*v3.NodeDriver, error) {
if aliases, ok := Aliases[driverName]; ok {
if aliases, ok := DriverToSchemaFields[driverName]; ok {
anno := make(map[string]map[string]string)
for _, aliased := range aliases {
......
......@@ -56,19 +56,19 @@ const (
userNodeRemoveAnnotationPrefix = "lifecycle.cattle.io/create.user-node-remove_"
)
// aliases maps Schema field => driver field
// SchemaToDriverFields maps Schema field => driver field
// The opposite of this lives in pkg/controllers/management/drivers/nodedriver/machine_driver.go
var aliases = map[string]map[string]string{
"aliyunecs": map[string]string{"sshKeyContents": "sshKeypath"},
"amazonec2": map[string]string{"sshKeyContents": "sshKeypath", "userdata": "userdata"},
"azure": map[string]string{"customData": "customData"},
"digitalocean": map[string]string{"sshKeyContents": "sshKeyPath", "userdata": "userdata"},
"exoscale": map[string]string{"sshKey": "sshKey", "userdata": "userdata"},
"openstack": map[string]string{"cacert": "cacert", "privateKeyFile": "privateKeyFile", "userDataFile": "userDataFile"},
"otc": map[string]string{"privateKeyFile": "privateKeyFile"},
"packet": map[string]string{"userdata": "userdata"},
"vmwarevsphere": map[string]string{"cloudConfig": "cloud-config"},
"google": map[string]string{"authEncodedJson": "authEncodedJson"},
var SchemaToDriverFields = map[string]map[string]string{
"aliyunecs": {"sshKeyContents": "sshKeypath"},
"amazonec2": {"sshKeyContents": "sshKeypath", "userdata": "userdata"},
"azure": {"customData": "customData"},
"digitalocean": {"sshKeyContents": "sshKeyPath", "userdata": "userdata"},
"exoscale": {"sshKey": "sshKey", "userdata": "userdata"},
"openstack": {"cacert": "cacert", "privateKeyFile": "privateKeyFile", "userDataFile": "userDataFile"},
"otc": {"privateKeyFile": "privateKeyFile"},
"packet": {"userdata": "userdata"},
"vmwarevsphere": {"cloudConfig": "cloud-config"},
"google": {"authEncodedJson": "authEncodedJson"},
}
func Register(ctx context.Context, management *config.ManagementContext, clusterManager *clustermanager.Manager) {
......@@ -346,7 +346,7 @@ func aliasToPath(driver string, config map[string]interface{}, ns string) error
baseDir = os.TempDir()
}
// Check if the required driver has aliased fields
if fields, ok := aliases[driver]; ok {
if fields, ok := SchemaToDriverFields[driver]; ok {
hasher := sha256.New()
for schemaField, driverField := range fields {
if fileRaw, ok := config[schemaField]; ok {
......
......@@ -14,10 +14,10 @@ import (
func TestAliasMaps(t *testing.T) {
assert := assert.New(t)
assert.Len(aliases, len(nodedriver.Aliases), "Alias maps are not equal")
for driver, fields := range aliases {
assert.Contains(nodedriver.Aliases, driver)
nodeAliases := nodedriver.Aliases[driver]
assert.Len(SchemaToDriverFields, len(nodedriver.DriverToSchemaFields), "Alias maps are not equal")
for driver, fields := range SchemaToDriverFields {
assert.Contains(nodedriver.DriverToSchemaFields, driver)
nodeAliases := nodedriver.DriverToSchemaFields[driver]
for k, v := range fields {
// check that the value from the first map is the key to the 2nd map
val, ok := nodeAliases[v]
......@@ -34,12 +34,12 @@ func TestAliasToPath(t *testing.T) {
os.Setenv("CATTLE_DEV_MODE", "true")
defer os.Unsetenv("CATTLE_DEV_MODE")
for driver, fields := range aliases {
for driver, fields := range SchemaToDriverFields {
testData, _ := createFakeConfig(fields)
err := aliasToPath(driver, testData, "fake")
assert.Nil(err)
for alias := range nodedriver.Aliases[driver] {
for alias := range nodedriver.DriverToSchemaFields[driver] {
assert.Contains(testData, alias)
}
tempdir := os.TempDir()
......
......@@ -46,14 +46,11 @@ func MachineStateSecretName(machineName string) string {
return name2.SafeConcatName(machineName, "machine", "state")
}
func (h *handler) getArgsEnvAndStatus(typeMeta meta.Type, meta metav1.Object, data data.Object, create bool) (driverArgs, error) {
func (h *handler) getArgsEnvAndStatus(typeMeta meta.Type, meta metav1.Object, data data.Object, args map[string]interface{}, driver string, create bool) (driverArgs, error) {
var (
url, hash, cloudCredentialSecretName string
)
args := data.Map("spec")
driver := getNodeDriverName(typeMeta)
nd, err := h.nodeDriverCache.Get(driver)
if !create && apierror.IsNotFound(err) {
url = data.String("status", "driverURL")
......
......@@ -3,11 +3,14 @@ package machineprovision
import (
"context"
errors2 "errors"
"path"
"strings"
"time"
"github.com/rancher/lasso/pkg/dynamic"
rkev1 "github.com/rancher/rancher/pkg/apis/rke.cattle.io/v1"
"github.com/rancher/rancher/pkg/controllers/management/drivers/nodedriver"
"github.com/rancher/rancher/pkg/controllers/management/node"
capicontrollers "github.com/rancher/rancher/pkg/generated/controllers/cluster.x-k8s.io/v1alpha4"
mgmtcontrollers "github.com/rancher/rancher/pkg/generated/controllers/management.cattle.io/v3"
"github.com/rancher/rancher/pkg/wrangler"
......@@ -265,22 +268,30 @@ func (h *handler) run(obj runtime.Object, create bool) (runtime.Object, error) {
return obj, nil
}
d, err := data.Convert(obj)
d, err := data.Convert(obj.DeepCopyObject())
if err != nil {
return nil, err
}
args, err := h.getArgsEnvAndStatus(typeMeta, meta, d, create)
args := d.Map("spec")
driver := getNodeDriverName(typeMeta)
filesSecret, err := constructFilesSecret(driver, args)
if err != nil {
return obj, err
}
dArgs, err := h.getArgsEnvAndStatus(typeMeta, meta, d, args, driver, create)
if err != nil {
return obj, err
}
if args.BootstrapSecretName == "" && !args.BootstrapOptional {
if dArgs.BootstrapSecretName == "" && !dArgs.BootstrapOptional {
return obj,
h.dynamic.EnqueueAfter(obj.GetObjectKind().GroupVersionKind(), meta.GetNamespace(), meta.GetName(), 2*time.Second)
}
objs, err := h.objects(d.Bool("status", "ready") && create, typeMeta, meta, args)
objs, err := h.objects(d.Bool("status", "ready") && create, typeMeta, meta, dArgs, filesSecret)
if err != nil {
return nil, err
}
......@@ -290,10 +301,10 @@ func (h *handler) run(obj runtime.Object, create bool) (runtime.Object, error) {
}
if create {
return h.patchStatus(obj, d, args.RKEMachineStatus)
return h.patchStatus(obj, d, dArgs.RKEMachineStatus)
}
return obj, h.apply.WithOwner(obj).ApplyObjects(objs...)
return obj, nil
}
func (h *handler) patchStatus(obj runtime.Object, d data.Object, state rkev1.RKEMachineStatus) (runtime.Object, error) {
......@@ -392,3 +403,36 @@ func setCondition(dynamic *dynamic.Controller, obj runtime.Object, conditionType
}
return obj, updateErr
}
func constructFilesSecret(driver string, config map[string]interface{}) (*corev1.Secret, error) {
secretData := make(map[string][]byte)
// Check if the required driver has aliased fields
if fields, ok := node.SchemaToDriverFields[driver]; ok {
for schemaField, driverField := range fields {
if fileContents, ok := config[schemaField].(string); ok {
// Delete our aliased fields
delete(config, schemaField)
if fileContents == "" {
continue
}
fileName := driverField
if ok := nodedriver.SSHKeyFields[schemaField]; ok {
fileName = "id_rsa"
}
// The ending newline gets stripped, add em back
if !strings.HasSuffix(fileContents, "\n") {
fileContents = fileContents + "\n"
}
// Add the file to the secret
secretData[fileName] = []byte(fileContents)
// Add the field and path
config[driverField] = path.Join(pathToMachineFiles, fileName)
}
}
return &corev1.Secret{Data: secretData}, nil
}
return nil, nil
}
......@@ -15,13 +15,17 @@ const (
InfraMachineVersion = "rke.cattle.io/infra-machine-version"
InfraMachineKind = "rke.cattle.io/infra-machine-kind"
InfraMachineName = "rke.cattle.io/infra-machine-name"
pathToMachineFiles = "/path/to/machine/files"
)
func getJobName(name string) string {
return name2.SafeConcatName(name, "machine", "provision")
}
func (h *handler) objects(ready bool, typeMeta metav1.Type, meta metav1.Object, args driverArgs) ([]runtime.Object, error) {
func (h *handler) objects(ready bool, typeMeta metav1.Type, meta metav1.Object, args driverArgs, filesSecret *corev1.Secret) ([]runtime.Object, error) {
var volumes []corev1.Volume
var volumeMounts []corev1.VolumeMount
machineGVK := schema.FromAPIVersionAndKind(typeMeta.GetAPIVersion(), typeMeta.GetKind())
saName := getJobName(meta.GetName())
secret := &corev1.Secret{
......@@ -40,6 +44,34 @@ func (h *handler) objects(ready bool, typeMeta metav1.Type, meta metav1.Object,
args.BootstrapSecretName = "not-found"
}
if filesSecret != nil {
if filesSecret.Name == "" {
filesSecret.Name = saName
filesSecret.Namespace = meta.GetNamespace()
}
volumeMounts = append(volumeMounts, corev1.VolumeMount{
Name: "machine-files",
ReadOnly: true,
MountPath: pathToMachineFiles,
})
keysToPaths := make([]corev1.KeyToPath, 0, len(filesSecret.Data))
for file := range filesSecret.Data {
keysToPaths = append(keysToPaths, corev1.KeyToPath{Key: file, Path: file})
}
volumes = append(volumes, corev1.Volume{
Name: "machine-files",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: filesSecret.Name,
Items: keysToPaths,
DefaultMode: &[]int32{0600}[0],
},
},
})
}
sa := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: saName,
......@@ -113,7 +145,7 @@ func (h *handler) objects(ready bool, typeMeta metav1.Type, meta metav1.Object,
},
},
Spec: corev1.PodSpec{
Volumes: []corev1.Volume{
Volumes: append(volumes, []corev1.Volume{
{
Name: "bootstrap",
VolumeSource: corev1.VolumeSource{
......@@ -134,7 +166,7 @@ func (h *handler) objects(ready bool, typeMeta metav1.Type, meta metav1.Object,
},
},
},
},
}...),
RestartPolicy: corev1.RestartPolicyNever,
Containers: []corev1.Container{
{
......@@ -151,7 +183,7 @@ func (h *handler) objects(ready bool, typeMeta metav1.Type, meta metav1.Object,
},
},
},
VolumeMounts: []corev1.VolumeMount{
VolumeMounts: append(volumeMounts, []corev1.VolumeMount{
{
Name: "bootstrap",
ReadOnly: false,
......@@ -163,7 +195,7 @@ func (h *handler) objects(ready bool, typeMeta metav1.Type, meta metav1.Object,
MountPath: "/etc/ssl/certs/ca-additional.pem",
SubPath: "ca-additional.pem",
},
},
}...),
},
},
ServiceAccountName: saName,
......@@ -178,6 +210,7 @@ func (h *handler) objects(ready bool, typeMeta metav1.Type, meta metav1.Object,
sa,
role,
rb,
filesSecret,
rb2,
job,
}, nil
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment