diff --git a/config/crd/bases/airship.airshipit.org_sipclusters.yaml b/config/crd/bases/airship.airshipit.org_sipclusters.yaml
index bd1c82d..4d4d5e5 100644
--- a/config/crd/bases/airship.airshipit.org_sipclusters.yaml
+++ b/config/crd/bases/airship.airshipit.org_sipclusters.yaml
@@ -120,6 +120,15 @@ spec:
type: object
nodePort:
type: integer
+ nodeSSHPrivateKeys:
+ description: NodeSSHPrivateKeys holds the name of a Secret
+ in the same namespace as the SIPCluster CR, whose key values
+ each represent an ssh private key that can be used to access
+ the cluster nodes. They are mounted into the jumphost with
+ the secret keys serving as file names relative to a common
+ directory, and then configured as identity files in the
+ SSH config file of the default user.
+ type: string
sshAuthorizedKeys:
items:
type: string
@@ -127,6 +136,7 @@ spec:
required:
- image
- nodePort
+ - nodeSSHPrivateKeys
type: object
type: array
loadBalancer:
diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml
index c899040..310f120 100644
--- a/config/rbac/role.yaml
+++ b/config/rbac/role.yaml
@@ -6,6 +6,12 @@ metadata:
creationTimestamp: null
name: manager-role
rules:
+- apiGroups:
+ - ""
+ resources:
+ - secrets
+ verbs:
+ - get
- apiGroups:
- airship.airshipit.org
resources:
diff --git a/config/samples/airship_v1beta1_sipcluster.yaml b/config/samples/airship_v1beta1_sipcluster.yaml
index 01327b2..d44f34a 100644
--- a/config/samples/airship_v1beta1_sipcluster.yaml
+++ b/config/samples/airship_v1beta1_sipcluster.yaml
@@ -1,7 +1,7 @@
apiVersion: airship.airshipit.org/v1
kind: SIPCluster
metadata:
- name: sipcluster-test
+ name: sipcluster-system
namespace: sipcluster-system
finalizers:
- sip.airship.airshipit.org/finalizer
@@ -43,6 +43,7 @@ spec:
sshAuthorizedKeys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCyaozS8kZRw2a1d0O4YXhxtJlDPThqIZilGCsXLbukIFOyMUmMTwQAtwWp5epwU1+5ponC2uBENB6xCCj3cl5Rd43d2/B6HxyAPQGKo6/zKYGAKW2nzYDxSWMl6NUSsiJAyXUA7ZlNZQe0m8PmaferlkQyLLZo3NJpizz6U6ZCtxvj43vEl7NYWnLUEIzGP9zMqltIGnD4vYrU9keVKKXSsp+DkApnbrDapeigeGATCammy2xRrUQDuOvGHsfnQbXr2j0onpTIh0PiLrXLQAPDg8UJRgVB+ThX+neI3rQ320djzRABckNeE6e4Kkwzn+QdZsmA2SDvM9IU7boK1jVQlgUPp7zF5q3hbb8Rx7AadyTarBayUkCgNlrMqth+tmTMWttMqCPxJRGnhhvesAHIl55a28Kzz/2Oqa3J9zwzbyDIwlEXho0eAq3YXEPeBhl34k+7gOt/5Zdbh+yacFoxDh0LrshQgboAijcVVaXPeN0LsHEiVvYIzugwIvCkoFMPWoPj/kEGzPY6FCkVneDA7VoLTCoG8dlrN08Lf05/BGC7Wllm66pTNZC/cKXP+cjpQn1iEuiuPxnPldlMHx9sx2y/BRoft6oT/GzqkNy1NTY/xI+MfmxXnF5kwSbcTbzZQ9fZ8xjh/vmpPBgDNrxOEAT4N6OG7GQIhb9HEhXQCQ== example-key
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwpOyZjZ4gB0OTvmofH3llh6cBCWaEiEmHZWSkDXr8Bih6HcXVOtYMcFi/ZnUVGUBPw3ATNQBZUaVCYKeF+nDfKTJ9hmnlsyHxV2LeMsVg1o15Pb6f+QJuavEqtE6HI7mHyId4Z1quVTJXDWDW8OZEG7M3VktauqAn/e9UJvlL0bGmTFD1XkNcbRsWMRWkQgt2ozqlgrpPtvrg2/+bNucxX++VUjnsn+fGgAT07kbnrZwppGnAfjbYthxhv7GeSD0+Z0Lf1kiKy/bhUqXsZIuexOfF0YrRyUH1KBl8GCX2OLBYvXHyusByqsrOPiROqRdjX5PsK6HSAS0lk0niTt1p example-key-2
+ nodeSSHPrivateKeys: ssh-private-keys
loadBalancer:
- image: haproxy:2.3.2
# NOTE: nodeLabels not yet implemented.
diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml
index f54db91..0576add 100644
--- a/config/samples/kustomization.yaml
+++ b/config/samples/kustomization.yaml
@@ -1,4 +1,5 @@
resources:
- airship_v1beta1_sipcluster.yaml
- bmh
+- ssh_private_keys_secret.yaml
namespace: sipcluster-system
diff --git a/config/samples/ssh_private_keys_secret.yaml b/config/samples/ssh_private_keys_secret.yaml
new file mode 100644
index 0000000..da3c5df
--- /dev/null
+++ b/config/samples/ssh_private_keys_secret.yaml
@@ -0,0 +1,7 @@
+apiVersion: v1
+data:
+ key: RFVNTVlfREFUQQ==
+kind: Secret
+metadata:
+ name: ssh-private-keys
+type: Opaque
\ No newline at end of file
diff --git a/docs/api/sipcluster.md b/docs/api/sipcluster.md
index 436adfc..018e46a 100644
--- a/docs/api/sipcluster.md
+++ b/docs/api/sipcluster.md
@@ -94,6 +94,20 @@ BMCOpts
|
+
+
+nodeSSHPrivateKeys
+
+string
+
+ |
+
+ NodeSSHPrivateKeys holds the name of a Secret in the same namespace as the SIPCluster CR,
+whose key values each represent an ssh private key that can be used to access the cluster nodes.
+They are mounted into the jumphost with the secret keys serving as file names relative to a common
+directory, and then configured as identity files in the SSH config file of the default user.
+ |
+
diff --git a/go.sum b/go.sum
index 9d76ee4..d9a2c62 100644
--- a/go.sum
+++ b/go.sum
@@ -822,25 +822,19 @@ k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlm
k8s.io/apimachinery v0.19.2 h1:5Gy9vQpAGTKHPVOh5c4plE274X8D/6cuEiTO2zve7tc=
k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
k8s.io/apiserver v0.18.6/go.mod h1:Zt2XvTHuaZjBz6EFYzpp+X4hTmgWGy8AthNVnTdm3Wg=
-k8s.io/apiserver v0.18.6/go.mod h1:Zt2XvTHuaZjBz6EFYzpp+X4hTmgWGy8AthNVnTdm3Wg=
-k8s.io/apiserver v0.19.2 h1:xq2dXAzsAoHv7S4Xc/p7PKhiowdHV/PgdePWo3MxIYM=
k8s.io/apiserver v0.19.2/go.mod h1:FreAq0bJ2vtZFj9Ago/X0oNGC51GfubKK/ViOKfVAOA=
k8s.io/client-go v0.18.6/go.mod h1:/fwtGLjYMS1MaM5oi+eXhKwG+1UHidUEXRh6cNsdO0Q=
k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU=
k8s.io/client-go v0.19.2 h1:gMJuU3xJZs86L1oQ99R4EViAADUPMHHtS9jFshasHSc=
k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA=
k8s.io/code-generator v0.18.6/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
-k8s.io/code-generator v0.18.6/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
-k8s.io/code-generator v0.19.2 h1:7uaWJll6fyCPj2j3sfNN1AiY2gZU1VFN2dFR2uoxGWI=
k8s.io/code-generator v0.19.2/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
k8s.io/component-base v0.18.6/go.mod h1:knSVsibPR5K6EW2XOjEHik6sdU5nCvKMrzMt2D4In14=
-k8s.io/component-base v0.18.6/go.mod h1:knSVsibPR5K6EW2XOjEHik6sdU5nCvKMrzMt2D4In14=
k8s.io/component-base v0.19.2 h1:jW5Y9RcZTb79liEhW3XDVTW7MuvEGP0tQZnfSX6/+gs=
k8s.io/component-base v0.19.2/go.mod h1:g5LrsiTiabMLZ40AR6Hl45f088DevyGY+cCE2agEIVo=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14 h1:t4L10Qfx/p7ASH3gXCdIUtPbbIuegCoUJf3TMSFekjw=
k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
@@ -858,17 +852,13 @@ k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
k8s.io/utils v0.0.0-20200821003339-5e75c0163111/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20200912215256-4140de9c8800 h1:9ZNvfPvVIEsp/T1ez4GQuzCcCTEQWhovSofhqR73A6g=
k8s.io/utils v0.0.0-20200912215256-4140de9c8800/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
-sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9 h1:rusRLrDhjBp6aYtl9sGEvQJr6faoHoDLd0YcUBTZguI=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0=
sigs.k8s.io/controller-runtime v0.6.2/go.mod h1:vhcq/rlnENJ09SIRp3EveTaZ0yqH526hjf9iJdbUJ/E=
-sigs.k8s.io/controller-runtime v0.6.2/go.mod h1:vhcq/rlnENJ09SIRp3EveTaZ0yqH526hjf9iJdbUJ/E=
sigs.k8s.io/controller-runtime v0.7.0 h1:bU20IBBEPccWz5+zXpLnpVsgBYxqclaHu1pVDl/gEt8=
sigs.k8s.io/controller-runtime v0.7.0/go.mod h1:pJ3YBrJiAqMAZKi6UVGuE98ZrroV1p+pIhoHsMm9wdU=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
-sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA=
sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
diff --git a/images/jump-host/Dockerfile b/images/jump-host/Dockerfile
index 1dff3ed..7b1d4fe 100644
--- a/images/jump-host/Dockerfile
+++ b/images/jump-host/Dockerfile
@@ -10,7 +10,21 @@ COPY ./certs/* /usr/local/share/ca-certificates/
RUN update-ca-certificates
RUN apt-get update
-RUN apt-get install -y --no-install-recommends jq openssh-server python3-pip python3-setuptools
+RUN apt-get install -y --no-install-recommends \
+ bash-completion \
+ jq \
+ python3-pip \
+ python3-setuptools \
+ openssh-server \
+ openssh-client
+
+# uncomment (enable) bash completion config
+RUN START=$(sed -n '/# enable bash completion in interactive shells/=' /etc/bash.bashrc) && \
+ sed -i "$((START + 1)),$((START + 7))"' s/^##*//' /etc/bash.bashrc
+# disable bash completion based on /etc/hosts, /etc/known_hosts, etc.
+# so that only ssh config file entries are used
+ENV COMP_KNOWN_HOSTS_WITH_HOSTFILE=
+
RUN pip3 install --upgrade pip
RUN pip3 config set global.cert /etc/ssl/certs/ca-certificates.crt
diff --git a/pkg/api/v1/sipcluster_types.go b/pkg/api/v1/sipcluster_types.go
index b04c419..f87a543 100644
--- a/pkg/api/v1/sipcluster_types.go
+++ b/pkg/api/v1/sipcluster_types.go
@@ -85,6 +85,11 @@ type JumpHostService struct {
SIPClusterService `json:",inline"`
BMC *BMCOpts `json:"bmc,omitempty"`
SSHAuthorizedKeys []string `json:"sshAuthorizedKeys,omitempty"`
+ // NodeSSHPrivateKeys holds the name of a Secret in the same namespace as the SIPCluster CR,
+ // whose key values each represent an ssh private key that can be used to access the cluster nodes.
+ // They are mounted into the jumphost with the secret keys serving as file names relative to a common
+ // directory, and then configured as identity files in the SSH config file of the default user.
+ NodeSSHPrivateKeys string `json:"nodeSSHPrivateKeys"`
}
// SIPClusterStatus defines the observed state of SIPCluster
diff --git a/pkg/controllers/sipcluster_controller.go b/pkg/controllers/sipcluster_controller.go
index 72fa072..faf70f3 100644
--- a/pkg/controllers/sipcluster_controller.go
+++ b/pkg/controllers/sipcluster_controller.go
@@ -51,6 +51,7 @@ const (
// +kubebuilder:rbac:groups=airship.airshipit.org,resources=sipclusters/status,verbs=get;update;patch
// +kubebuilder:rbac:groups="metal3.io",resources=baremetalhosts,verbs=get;update;patch;list
+// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;
func (r *SIPClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
r.NamespacedName = req.NamespacedName
diff --git a/pkg/controllers/sipcluster_controller_test.go b/pkg/controllers/sipcluster_controller_test.go
index 480a00a..c065c57 100644
--- a/pkg/controllers/sipcluster_controller_test.go
+++ b/pkg/controllers/sipcluster_controller_test.go
@@ -41,7 +41,7 @@ const (
var _ = Describe("SIPCluster controller", func() {
AfterEach(func() {
- opts := []client.DeleteAllOfOption{client.InNamespace("default")}
+ opts := []client.DeleteAllOfOption{client.InNamespace(testNamespace)}
Expect(k8sClient.DeleteAllOf(context.Background(), &metal3.BareMetalHost{}, opts...)).Should(Succeed())
Expect(k8sClient.DeleteAllOf(context.Background(), &airshipv1.SIPCluster{}, opts...)).Should(Succeed())
Expect(k8sClient.DeleteAllOf(context.Background(), &corev1.Secret{}, opts...)).Should(Succeed())
@@ -71,7 +71,8 @@ var _ = Describe("SIPCluster controller", func() {
// Create SIP cluster
name := "subcluster-test1"
- sipCluster := testutil.CreateSIPCluster(name, testNamespace, 3, 4)
+ sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster(name, testNamespace, 3, 4)
+ Expect(k8sClient.Create(context.Background(), nodeSSHPrivateKeys)).Should(Succeed())
Expect(k8sClient.Create(context.Background(), sipCluster)).Should(Succeed())
// Poll BMHs until SIP has scheduled them to the SIP cluster
@@ -107,7 +108,8 @@ var _ = Describe("SIPCluster controller", func() {
// Create SIP cluster
name := "subcluster-test2"
- sipCluster := testutil.CreateSIPCluster(name, testNamespace, 3, 4)
+ sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster(name, testNamespace, 3, 4)
+ Expect(k8sClient.Create(context.Background(), nodeSSHPrivateKeys)).Should(Succeed())
Expect(k8sClient.Create(context.Background(), sipCluster)).Should(Succeed())
// Poll BMHs and validate they are not scheduled
@@ -153,7 +155,8 @@ var _ = Describe("SIPCluster controller", func() {
// Create SIP cluster
name := "subcluster-test4"
- sipCluster := testutil.CreateSIPCluster(name, testNamespace, 3, 4)
+ sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster(name, testNamespace, 3, 4)
+ Expect(k8sClient.Create(context.Background(), nodeSSHPrivateKeys)).Should(Succeed())
Expect(k8sClient.Create(context.Background(), sipCluster)).Should(Succeed())
// Poll BMHs and validate they are not scheduled
@@ -215,7 +218,8 @@ var _ = Describe("SIPCluster controller", func() {
// Create SIP cluster
name := "subcluster-test5"
- sipCluster := testutil.CreateSIPCluster(name, testNamespace, 1, 2)
+ sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster(name, testNamespace, 1, 2)
+ Expect(k8sClient.Create(context.Background(), nodeSSHPrivateKeys)).Should(Succeed())
Expect(k8sClient.Create(context.Background(), sipCluster)).Should(Succeed())
// Poll BMHs and validate they are not scheduled
@@ -276,7 +280,8 @@ var _ = Describe("SIPCluster controller", func() {
// Create SIP cluster
name := "subcluster-test6"
- sipCluster := testutil.CreateSIPCluster(name, testNamespace, 2, 1)
+ sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster(name, testNamespace, 2, 1)
+ Expect(k8sClient.Create(context.Background(), nodeSSHPrivateKeys)).Should(Succeed())
Expect(k8sClient.Create(context.Background(), sipCluster)).Should(Succeed())
// Poll BMHs and validate they are not scheduled
@@ -336,7 +341,7 @@ var _ = Describe("SIPCluster controller", func() {
// Create SIP cluster
name := "subcluster-test3"
- sipCluster := testutil.CreateSIPCluster(name, testNamespace, 1, 2)
+ sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster(name, testNamespace, 1, 2)
controlPlaneSpec := sipCluster.Spec.Nodes[airshipv1.VMControlPlane]
controlPlaneSpec.Scheduling = airshipv1.RackAntiAffinity
@@ -346,6 +351,7 @@ var _ = Describe("SIPCluster controller", func() {
workerSpec.Scheduling = airshipv1.RackAntiAffinity
sipCluster.Spec.Nodes[airshipv1.VMWorker] = workerSpec
+ Expect(k8sClient.Create(context.Background(), nodeSSHPrivateKeys)).Should(Succeed())
Expect(k8sClient.Create(context.Background(), sipCluster)).Should(Succeed())
// Poll BMHs and validate they are not scheduled
@@ -402,7 +408,7 @@ var _ = Describe("SIPCluster controller", func() {
// Create SIP cluster
name := "subcluster-test3"
- sipCluster := testutil.CreateSIPCluster(name, testNamespace, 2, 1)
+ sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster(name, testNamespace, 2, 1)
controlPlaneSpec := sipCluster.Spec.Nodes[airshipv1.VMControlPlane]
controlPlaneSpec.Scheduling = airshipv1.RackAntiAffinity
@@ -412,6 +418,7 @@ var _ = Describe("SIPCluster controller", func() {
workerSpec.Scheduling = airshipv1.RackAntiAffinity
sipCluster.Spec.Nodes[airshipv1.VMWorker] = workerSpec
+ Expect(k8sClient.Create(context.Background(), nodeSSHPrivateKeys)).Should(Succeed())
Expect(k8sClient.Create(context.Background(), sipCluster)).Should(Succeed())
// Poll BMHs and validate they are not scheduled
diff --git a/pkg/controllers/suite_test.go b/pkg/controllers/suite_test.go
index a0120f8..7e3ff8d 100644
--- a/pkg/controllers/suite_test.go
+++ b/pkg/controllers/suite_test.go
@@ -25,6 +25,7 @@ import (
metal3 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
+ corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
@@ -72,6 +73,9 @@ var _ = BeforeSuite(func(done Done) {
err = metal3.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
+ err = corev1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+
// +kubebuilder:scaffold:scheme
k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
diff --git a/pkg/services/jumphost.go b/pkg/services/jumphost.go
index 318f4ee..a39c0e5 100644
--- a/pkg/services/jumphost.go
+++ b/pkg/services/jumphost.go
@@ -15,8 +15,11 @@
package services
import (
+ "bytes"
+ "context"
"encoding/json"
"fmt"
+ "html/template"
"net/url"
"strings"
@@ -35,16 +38,23 @@ import (
const (
JumpHostServiceName = "jumphost"
- mountPathData = "/etc/opt/sip"
- mountPathScripts = "/opt/sip/bin"
+ subPathHosts = "hosts"
+ subPathSSHConfig = "ssh_config"
sshDir = "/home/ubuntu/.ssh"
authorizedKeysFile = "authorized_keys"
- mountPathSSH = sshDir + "/" + authorizedKeysFile
- nameAuthorizedKeysVolume = "authorized-keys"
- nameHostsVolume = "hosts"
- nameRebootVolume = "vm"
+ mountPathData = "/etc/opt/sip"
+ mountPathScripts = "/opt/sip/bin"
+ mountPathHosts = mountPathData + "/" + subPathHosts
+ mountPathSSHConfig = sshDir + "/config"
+ mountPathSSH = sshDir + "/" + authorizedKeysFile
+ mountPathNodeSSHPrivateKeys = mountPathData + "/" + nameNodeSSHPrivateKeysVolume
+
+ nameDataVolume = "data"
+ nameScriptsVolume = "scripts"
+ nameAuthorizedKeysVolume = "authorized-keys"
+ nameNodeSSHPrivateKeysVolume = "ssh-private-keys"
)
// JumpHost is an InfrastructureService that provides SSH and power-management capabilities for sub-clusters.
@@ -81,6 +91,8 @@ func (jh jumpHost) Deploy() error {
"app.kubernetes.io/instance": instance,
}
+ hostAliases := jh.generateHostAliases()
+
// TODO: Validate Service becomes ready.
service := jh.generateService(instance, labels)
jh.logger.Info("Applying service", "service", service.GetNamespace()+"/"+service.GetName())
@@ -90,8 +102,7 @@ func (jh jumpHost) Deploy() error {
return err
}
- // TODO: Validate Secret becomes ready.
- secret, err := jh.generateSecret(instance, labels)
+ secret, err := jh.generateSecret(instance, labels, hostAliases)
if err != nil {
return err
}
@@ -115,7 +126,7 @@ func (jh jumpHost) Deploy() error {
}
// TODO: Validate Deployment becomes ready.
- deployment := jh.generateDeployment(instance, labels)
+ deployment := jh.generateDeployment(instance, labels, hostAliases)
jh.logger.Info("Applying deployment", "deployment", deployment.GetNamespace()+"/"+deployment.GetName())
err = applyRuntimeObject(client.ObjectKey{Name: deployment.GetName(), Namespace: deployment.GetNamespace()},
deployment, jh.client)
@@ -126,7 +137,8 @@ func (jh jumpHost) Deploy() error {
return nil
}
-func (jh jumpHost) generateDeployment(instance string, labels map[string]string) *appsv1.Deployment {
+func (jh jumpHost) generateDeployment(instance string, labels map[string]string,
+ hostAliases []corev1.HostAlias) *appsv1.Deployment {
deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: instance,
@@ -166,19 +178,29 @@ func (jh jumpHost) generateDeployment(instance string, labels map[string]string)
},
},
VolumeMounts: []corev1.VolumeMount{
+ {
+ Name: nameDataVolume,
+ MountPath: mountPathHosts,
+ SubPath: subPathHosts,
+ },
+ {
+ Name: nameScriptsVolume,
+ MountPath: mountPathScripts,
+ },
+ {
+ Name: nameDataVolume,
+ MountPath: mountPathSSHConfig,
+ SubPath: subPathSSHConfig,
+ },
+ {
+ Name: nameNodeSSHPrivateKeysVolume,
+ MountPath: mountPathNodeSSHPrivateKeys,
+ },
{
Name: nameAuthorizedKeysVolume,
MountPath: mountPathSSH,
SubPath: authorizedKeysFile,
},
- {
- Name: nameHostsVolume,
- MountPath: mountPathData,
- },
- {
- Name: nameRebootVolume,
- MountPath: mountPathScripts,
- },
},
},
},
@@ -187,7 +209,7 @@ func (jh jumpHost) generateDeployment(instance string, labels map[string]string)
},
Volumes: []corev1.Volume{
{
- Name: nameHostsVolume,
+ Name: nameDataVolume,
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: instance,
@@ -213,24 +235,26 @@ func (jh jumpHost) generateDeployment(instance string, labels map[string]string)
},
},
{
- Name: nameRebootVolume,
+ Name: nameScriptsVolume,
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: instance,
},
DefaultMode: int32Ptr(0777),
- Items: []corev1.KeyToPath{
- {
- Key: nameRebootVolume,
- Path: nameRebootVolume,
- },
- },
+ },
+ },
+ },
+ {
+ Name: nameNodeSSHPrivateKeysVolume,
+ VolumeSource: corev1.VolumeSource{
+ Secret: &corev1.SecretVolumeSource{
+ SecretName: jh.config.NodeSSHPrivateKeys,
},
},
},
},
- HostAliases: jh.generateHostAliases(),
+ HostAliases: hostAliases,
},
},
},
@@ -281,18 +305,23 @@ func (jh jumpHost) generateConfigMap(instance string, labels map[string]string)
},
Data: map[string]string{
nameAuthorizedKeysVolume: strings.Join(jh.config.SSHAuthorizedKeys, "\n"),
- nameRebootVolume: fmt.Sprintf(rebootScript, mountPathData, nameHostsVolume),
+ "vm": fmt.Sprintf(rebootScript, mountPathHosts),
},
}, nil
}
-func (jh jumpHost) generateSecret(instance string, labels map[string]string) (*corev1.Secret, error) {
+func (jh jumpHost) generateSecret(instance string, labels map[string]string, hostAliases []corev1.HostAlias) (
+ *corev1.Secret, error) {
hostData, err := generateHostList(*jh.machines)
if err != nil {
return nil, err
}
+ sshConfig, err := jh.generateSSHConfig(hostAliases)
+ if err != nil {
+ return nil, err
+ }
- return &corev1.Secret{
+ secret := &corev1.Secret{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
Kind: "Secret",
@@ -303,11 +332,69 @@ func (jh jumpHost) generateSecret(instance string, labels map[string]string) (*c
Labels: labels,
},
Data: map[string][]byte{
- nameHostsVolume: hostData,
+ subPathHosts: hostData,
+ subPathSSHConfig: sshConfig,
},
- }, nil
+ }
+
+ return secret, nil
}
+func (jh jumpHost) generateSSHConfig(hostAliases []corev1.HostAlias) ([]byte, error) {
+ key := types.NamespacedName{
+ Namespace: jh.sipName.Namespace,
+ Name: jh.config.NodeSSHPrivateKeys,
+ }
+ secret := &corev1.Secret{}
+ if err := jh.client.Get(context.Background(), key, secret); err != nil {
+ return nil, err
+ }
+
+ identityFiles := []string{}
+ for k := range secret.Data {
+ identityFiles = append(identityFiles, mountPathNodeSSHPrivateKeys+"/"+k)
+ }
+ hostNames := []string{}
+ for _, hostAlias := range hostAliases {
+ hostNames = append(hostNames, hostAlias.Hostnames[0])
+ }
+
+ tmpl, err := template.New("ssh-config").Parse(sshConfigTemplate)
+ if err != nil {
+ return nil, err
+ }
+
+ data := sshConfigTemplateData{
+ IdentityFiles: identityFiles,
+ HostNames: hostNames,
+ }
+
+ w := bytes.NewBuffer([]byte{})
+ if err := tmpl.Execute(w, data); err != nil {
+ return nil, err
+ }
+
+ rendered := w.Bytes()
+ return rendered, nil
+}
+
+type sshConfigTemplateData struct {
+ IdentityFiles []string
+ HostNames []string
+}
+
+const sshConfigTemplate = `
+Host *
+{{- range .IdentityFiles }}
+ IdentityFile {{ . }}
+{{ end -}}
+
+{{- range .HostNames }}
+Host {{ . }}
+ HostName {{ . }}
+{{ end -}}
+`
+
func (jh jumpHost) generateHostAliases() []corev1.HostAlias {
hostAliases := []corev1.HostAlias{}
for _, machine := range jh.machines.Machines {
@@ -410,7 +497,7 @@ var rebootScript = `#!/bin/sh
# Support Infrastructure Provider (SIP) VM Utility
# DO NOT MODIFY: generated by SIP
-HOSTS_FILE="%s/%s"
+HOSTS_FILE="%s"
LIST_COMMAND="list"
REBOOT_COMMAND="reboot"
diff --git a/pkg/services/services_test.go b/pkg/services/services_test.go
index da9b6a1..2df11ac 100644
--- a/pkg/services/services_test.go
+++ b/pkg/services/services_test.go
@@ -27,6 +27,9 @@ const (
var bmh1 *metal3.BareMetalHost
var bmh2 *metal3.BareMetalHost
+var m1 *vbmh.Machine
+var m2 *vbmh.Machine
+
// Re-declared from services package for testing purposes
type host struct {
Name string `json:"name"`
@@ -54,7 +57,7 @@ var _ = Describe("Service Set", func() {
bmh1.Spec.BMC.CredentialsName = bmcSecret.Name
bmh2.Spec.BMC.CredentialsName = bmcSecret.Name
- m1 := &vbmh.Machine{
+ m1 = &vbmh.Machine{
BMH: *bmh1,
Data: &vbmh.MachineData{
IPOnInterface: map[string]string{
@@ -63,7 +66,7 @@ var _ = Describe("Service Set", func() {
},
}
- m2 := &vbmh.Machine{
+ m2 = &vbmh.Machine{
BMH: *bmh2,
Data: &vbmh.MachineData{
IPOnInterface: map[string]string{
@@ -91,8 +94,16 @@ var _ = Describe("Service Set", func() {
It("Deploys services", func() {
By("Getting machine IPs and creating secrets, pods, and nodeport service")
- sip := testutil.CreateSIPCluster("default", "default", 1, 1)
- set := services.NewServiceSet(logger, *sip, machineList, k8sClient)
+ sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("default", "default", 1, 1)
+ Expect(k8sClient.Create(context.Background(), nodeSSHPrivateKeys)).Should(Succeed())
+ machineList = &vbmh.MachineList{
+ Machines: map[string]*vbmh.Machine{
+ bmh1.GetName(): m1,
+ bmh2.GetName(): m2,
+ },
+ }
+
+ set := services.NewServiceSet(logger, *sipCluster, machineList, k8sClient)
serviceList, err := set.ServiceList()
Expect(serviceList).To(HaveLen(2))
@@ -103,12 +114,12 @@ var _ = Describe("Service Set", func() {
}
Eventually(func() error {
- return testDeployment(sip, *machineList)
+ return testDeployment(sipCluster, *machineList)
}, 5, 1).Should(Succeed())
})
It("Does not deploy a Jump Host when an invalid SSH key is provided", func() {
- sip := testutil.CreateSIPCluster("default", "default", 1, 1)
+ sip, _ := testutil.CreateSIPCluster("default", "default", 1, 1)
sip.Spec.Services.Auth = []airshipv1.SIPClusterService{}
sip.Spec.Services.LoadBalancer = []airshipv1.SIPClusterService{}
sip.Spec.Services.JumpHost[0].SSHAuthorizedKeys = []string{
diff --git a/pkg/vbmh/vbmh_test.go b/pkg/vbmh/vbmh_test.go
index a856d1f..af27f6e 100644
--- a/pkg/vbmh/vbmh_test.go
+++ b/pkg/vbmh/vbmh_test.go
@@ -124,7 +124,7 @@ var _ = Describe("MachineList", func() {
Log: ctrl.Log.WithName("controllers").WithName("SIPCluster"),
}
- sipCluster := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
+ sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
sipCluster.Spec.Services = airshipv1.SIPClusterServices{
LoadBalancer: []airshipv1.SIPClusterService{
{
@@ -137,6 +137,7 @@ var _ = Describe("MachineList", func() {
},
},
}
+ objsToApply = append(objsToApply, nodeSSHPrivateKeys)
k8sClient := mockClient.NewFakeClient(objsToApply...)
Expect(ml.ExtrapolateServiceAddresses(*sipCluster, k8sClient)).To(BeNil())
@@ -174,7 +175,7 @@ var _ = Describe("MachineList", func() {
Log: ctrl.Log.WithName("controllers").WithName("SIPCluster"),
}
- sipCluster := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
+ sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
sipCluster.Spec.Services = airshipv1.SIPClusterServices{
LoadBalancer: []airshipv1.SIPClusterService{
{
@@ -187,6 +188,7 @@ var _ = Describe("MachineList", func() {
},
},
}
+ objsToApply = append(objsToApply, nodeSSHPrivateKeys)
k8sClient := mockClient.NewFakeClient(objsToApply...)
Expect(ml.ExtrapolateBMCAuth(*sipCluster, k8sClient)).To(BeNil())
@@ -222,7 +224,7 @@ var _ = Describe("MachineList", func() {
Log: ctrl.Log.WithName("controllers").WithName("SIPCluster"),
}
- sipCluster := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
+ sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
sipCluster.Spec.Services = airshipv1.SIPClusterServices{
LoadBalancer: []airshipv1.SIPClusterService{
{
@@ -235,6 +237,7 @@ var _ = Describe("MachineList", func() {
},
},
}
+ objsToApply = append(objsToApply, nodeSSHPrivateKeys)
k8sClient := mockClient.NewFakeClient(objsToApply...)
Expect(ml.ExtrapolateBMCAuth(*sipCluster, k8sClient)).ToNot(BeNil())
})
@@ -274,7 +277,7 @@ var _ = Describe("MachineList", func() {
Log: ctrl.Log.WithName("controllers").WithName("SIPCluster"),
}
- sipCluster := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
+ sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
sipCluster.Spec.Services = airshipv1.SIPClusterServices{
LoadBalancer: []airshipv1.SIPClusterService{
{
@@ -287,6 +290,7 @@ var _ = Describe("MachineList", func() {
},
},
}
+ objsToApply = append(objsToApply, nodeSSHPrivateKeys)
k8sClient := mockClient.NewFakeClient(objsToApply...)
Expect(ml.ExtrapolateBMCAuth(*sipCluster, k8sClient)).ToNot(BeNil())
})
@@ -320,7 +324,7 @@ var _ = Describe("MachineList", func() {
Log: ctrl.Log.WithName("controllers").WithName("SIPCluster"),
}
- sipCluster := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
+ sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
sipCluster.Spec.Services = airshipv1.SIPClusterServices{
LoadBalancer: []airshipv1.SIPClusterService{
{
@@ -333,6 +337,7 @@ var _ = Describe("MachineList", func() {
},
},
}
+ objsToApply = append(objsToApply, nodeSSHPrivateKeys)
k8sClient := mockClient.NewFakeClient(objsToApply...)
Expect(ml.ExtrapolateServiceAddresses(*sipCluster, k8sClient)).ToNot(BeNil())
})
@@ -365,7 +370,7 @@ var _ = Describe("MachineList", func() {
Log: ctrl.Log.WithName("controllers").WithName("SIPCluster"),
}
- sipCluster := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
+ sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
sipCluster.Spec.Services = airshipv1.SIPClusterServices{
LoadBalancer: []airshipv1.SIPClusterService{
{
@@ -378,22 +383,24 @@ var _ = Describe("MachineList", func() {
},
},
}
+ objsToApply = append(objsToApply, nodeSSHPrivateKeys)
k8sClient := mockClient.NewFakeClient(objsToApply...)
Expect(ml.ExtrapolateServiceAddresses(*sipCluster, k8sClient)).ToNot(BeNil())
})
It("Should not retrieve the BMH IP if it has been previously extrapolated", func() {
// Store an IP address for each machine
- var objs []runtime.Object
+ var objectsToApply []runtime.Object
for _, machine := range machineList.Machines {
machine.Data.IPOnInterface = map[string]string{
"oam-ipv4": "32.68.51.139",
}
- objs = append(objs, &machine.BMH)
+ objectsToApply = append(objectsToApply, &machine.BMH)
}
- k8sClient := mockClient.NewFakeClient(objs...)
- sipCluster := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
+ sipCluster, nodeSSHPrivateKeys := testutil.CreateSIPCluster("subcluster-1", "default", 1, 3)
+ objectsToApply = append(objectsToApply, nodeSSHPrivateKeys)
+ k8sClient := mockClient.NewFakeClient(objectsToApply...)
Expect(machineList.ExtrapolateServiceAddresses(*sipCluster, k8sClient)).To(BeNil())
})
diff --git a/testutil/testutil.go b/testutil/testutil.go
index 6443fc0..5559aa2 100644
--- a/testutil/testutil.go
+++ b/testutil/testutil.go
@@ -23,6 +23,8 @@ const (
VinoFlavorLabel = "vino.airshipit.org/flavor"
+ sshPrivateKeyBase64 = "DUMMY_DATA"
+
networkDataContent = `
{
"links": [
@@ -207,59 +209,72 @@ func CreateBMH(node int, namespace string, role airshipv1.VMRole, rack int) (*me
}
// CreateSIPCluster initializes a SIPCluster with specific parameters for use in test cases.
-func CreateSIPCluster(name string, namespace string, controlPlanes int, workers int) *airshipv1.SIPCluster {
+func CreateSIPCluster(name string, namespace string, controlPlanes int, workers int) (
+ *airshipv1.SIPCluster, *corev1.Secret) {
+ sshPrivateKeySecretName := fmt.Sprintf("%s-ssh-private-key", name)
return &airshipv1.SIPCluster{
- TypeMeta: metav1.TypeMeta{
- Kind: "SIPCluster",
- APIVersion: "airship.airshipit.org/v1",
- },
- ObjectMeta: metav1.ObjectMeta{
- Name: name,
- Namespace: namespace,
- },
- Spec: airshipv1.SIPClusterSpec{
- Nodes: map[airshipv1.VMRole]airshipv1.NodeSet{
- airshipv1.VMControlPlane: {
- VMFlavor: "vino.airshipit.org/flavor=" + vinoFlavorMap[airshipv1.VMControlPlane],
- Scheduling: airshipv1.HostAntiAffinity,
- Count: &airshipv1.VMCount{
- Active: controlPlanes,
- Standby: 0,
- },
- },
- airshipv1.VMWorker: {
- VMFlavor: "vino.airshipit.org/flavor=" + vinoFlavorMap[airshipv1.VMWorker],
- Scheduling: airshipv1.HostAntiAffinity,
- Count: &airshipv1.VMCount{
- Active: workers,
- Standby: 0,
- },
- },
+ TypeMeta: metav1.TypeMeta{
+ Kind: "SIPCluster",
+ APIVersion: "airship.airshipit.org/v1",
},
- Services: airshipv1.SIPClusterServices{
- LoadBalancer: []airshipv1.SIPClusterService{
- {
- NodeInterface: "eno3",
- NodePort: 30000,
+ ObjectMeta: metav1.ObjectMeta{
+ Name: name,
+ Namespace: namespace,
+ },
+ Spec: airshipv1.SIPClusterSpec{
+ Nodes: map[airshipv1.VMRole]airshipv1.NodeSet{
+ airshipv1.VMControlPlane: {
+ VMFlavor: "vino.airshipit.org/flavor=" + vinoFlavorMap[airshipv1.VMControlPlane],
+ Scheduling: airshipv1.HostAntiAffinity,
+ Count: &airshipv1.VMCount{
+ Active: controlPlanes,
+ Standby: 0,
+ },
+ },
+ airshipv1.VMWorker: {
+ VMFlavor: "vino.airshipit.org/flavor=" + vinoFlavorMap[airshipv1.VMWorker],
+ Scheduling: airshipv1.HostAntiAffinity,
+ Count: &airshipv1.VMCount{
+ Active: workers,
+ Standby: 0,
+ },
},
},
- JumpHost: []airshipv1.JumpHostService{
- {
- SIPClusterService: airshipv1.SIPClusterService{
- Image: "quay.io/airshipit/jump-host",
- NodePort: 30001,
+ Services: airshipv1.SIPClusterServices{
+ LoadBalancer: []airshipv1.SIPClusterService{
+ {
NodeInterface: "eno3",
+ NodePort: 30000,
},
- SSHAuthorizedKeys: []string{
- "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCyaozS8kZRw2a1d0O4YXhxtJlDPThqIZilGCsXLbukIFOyMUmMTwQAtwWp5epwU1+5ponC2uBENB6xCCj3cl5Rd43d2/B6HxyAPQGKo6/zKYGAKW2nzYDxSWMl6NUSsiJAyXUA7ZlNZQe0m8PmaferlkQyLLZo3NJpizz6U6ZCtxvj43vEl7NYWnLUEIzGP9zMqltIGnD4vYrU9keVKKXSsp+DkApnbrDapeigeGATCammy2xRrUQDuOvGHsfnQbXr2j0onpTIh0PiLrXLQAPDg8UJRgVB+ThX+neI3rQ320djzRABckNeE6e4Kkwzn+QdZsmA2SDvM9IU7boK1jVQlgUPp7zF5q3hbb8Rx7AadyTarBayUkCgNlrMqth+tmTMWttMqCPxJRGnhhvesAHIl55a28Kzz/2Oqa3J9zwzbyDIwlEXho0eAq3YXEPeBhl34k+7gOt/5Zdbh+yacFoxDh0LrshQgboAijcVVaXPeN0LsHEiVvYIzugwIvCkoFMPWoPj/kEGzPY6FCkVneDA7VoLTCoG8dlrN08Lf05/BGC7Wllm66pTNZC/cKXP+cjpQn1iEuiuPxnPldlMHx9sx2y/BRoft6oT/GzqkNy1NTY/xI+MfmxXnF5kwSbcTbzZQ9fZ8xjh/vmpPBgDNrxOEAT4N6OG7GQIhb9HEhXQCQ== example-key", //nolint
- "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwpOyZjZ4gB0OTvmofH3llh6cBCWaEiEmHZWSkDXr8Bih6HcXVOtYMcFi/ZnUVGUBPw3ATNQBZUaVCYKeF+nDfKTJ9hmnlsyHxV2LeMsVg1o15Pb6f+QJuavEqtE6HI7mHyId4Z1quVTJXDWDW8OZEG7M3VktauqAn/e9UJvlL0bGmTFD1XkNcbRsWMRWkQgt2ozqlgrpPtvrg2/+bNucxX++VUjnsn+fGgAT07kbnrZwppGnAfjbYthxhv7GeSD0+Z0Lf1kiKy/bhUqXsZIuexOfF0YrRyUH1KBl8GCX2OLBYvXHyusByqsrOPiROqRdjX5PsK6HSAS0lk0niTt1p example-key-2", // nolint
+ },
+ JumpHost: []airshipv1.JumpHostService{
+ {
+ SIPClusterService: airshipv1.SIPClusterService{
+ Image: "quay.io/airshipit/jump-host",
+ NodePort: 30001,
+ NodeInterface: "eno3",
+ },
+ SSHAuthorizedKeys: []string{
+ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCyaozS8kZRw2a1d0O4YXhxtJlDPThqIZilGCsXLbukIFOyMUmMTwQAtwWp5epwU1+5ponC2uBENB6xCCj3cl5Rd43d2/B6HxyAPQGKo6/zKYGAKW2nzYDxSWMl6NUSsiJAyXUA7ZlNZQe0m8PmaferlkQyLLZo3NJpizz6U6ZCtxvj43vEl7NYWnLUEIzGP9zMqltIGnD4vYrU9keVKKXSsp+DkApnbrDapeigeGATCammy2xRrUQDuOvGHsfnQbXr2j0onpTIh0PiLrXLQAPDg8UJRgVB+ThX+neI3rQ320djzRABckNeE6e4Kkwzn+QdZsmA2SDvM9IU7boK1jVQlgUPp7zF5q3hbb8Rx7AadyTarBayUkCgNlrMqth+tmTMWttMqCPxJRGnhhvesAHIl55a28Kzz/2Oqa3J9zwzbyDIwlEXho0eAq3YXEPeBhl34k+7gOt/5Zdbh+yacFoxDh0LrshQgboAijcVVaXPeN0LsHEiVvYIzugwIvCkoFMPWoPj/kEGzPY6FCkVneDA7VoLTCoG8dlrN08Lf05/BGC7Wllm66pTNZC/cKXP+cjpQn1iEuiuPxnPldlMHx9sx2y/BRoft6oT/GzqkNy1NTY/xI+MfmxXnF5kwSbcTbzZQ9fZ8xjh/vmpPBgDNrxOEAT4N6OG7GQIhb9HEhXQCQ== example-key", //nolint
+ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwpOyZjZ4gB0OTvmofH3llh6cBCWaEiEmHZWSkDXr8Bih6HcXVOtYMcFi/ZnUVGUBPw3ATNQBZUaVCYKeF+nDfKTJ9hmnlsyHxV2LeMsVg1o15Pb6f+QJuavEqtE6HI7mHyId4Z1quVTJXDWDW8OZEG7M3VktauqAn/e9UJvlL0bGmTFD1XkNcbRsWMRWkQgt2ozqlgrpPtvrg2/+bNucxX++VUjnsn+fGgAT07kbnrZwppGnAfjbYthxhv7GeSD0+Z0Lf1kiKy/bhUqXsZIuexOfF0YrRyUH1KBl8GCX2OLBYvXHyusByqsrOPiROqRdjX5PsK6HSAS0lk0niTt1p example-key-2", // nolint
+ },
+ NodeSSHPrivateKeys: sshPrivateKeySecretName,
},
},
},
},
+ Status: airshipv1.SIPClusterStatus{},
},
- Status: airshipv1.SIPClusterStatus{},
- }
+ &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: sshPrivateKeySecretName,
+ Namespace: namespace,
+ },
+ Data: map[string][]byte{
+ "key": []byte(sshPrivateKeyBase64),
+ },
+ Type: corev1.SecretTypeOpaque,
+ }
}
// CreateBMCAuthSecret creates a K8s Secret that matches the Metal3.io BaremetalHost credential format for use in test