@@ -30,6 +30,8 @@ import (
3030 "k8s.io/kops/upup/pkg/fi/fitasks"
3131)
3232
33+ const testSSHPublicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCySdqIU+FhCWl3BNrAvPaOe5VfL2aCARUWwy91ZP+T7LBwFa9lhdttfjp/VX1D1/PVwntn2EhN079m8c2kfdmiZ/iCHqrLyIGSd+BOiCz0lT47znvANSfxYjLUuKrWWWeaXqerJkOsAD4PHchRLbZGPdbfoBKwtb/WT4GMRQmb9vmiaZYjsfdPPM9KkWI9ECoWFGjGehA8D+iYIPR711kRacb1xdYmnjHqxAZHFsb5L8wDWIeAyhy49cBD+lbzTiioq2xWLorXuFmXh6Do89PgzvHeyCLY6816f/kCX6wIFts8A2eaEHFL4rAOsuh6qHmSxGCR9peSyuRW8DxV725x justin@test"
34+
3335func TestInstanceModelBuilderBuildUsesIGSubnetAndAllowsZeroSize (t * testing.T ) {
3436 cluster := & kops.Cluster {
3537 ObjectMeta : metav1.ObjectMeta {Name : "example.k8s.local" },
@@ -51,30 +53,36 @@ func TestInstanceModelBuilderBuildUsesIGSubnetAndAllowsZeroSize(t *testing.T) {
5153 MinSize : new (int32 ),
5254 },
5355 }
56+ secondIG := & kops.InstanceGroup {
57+ ObjectMeta : metav1.ObjectMeta {Name : "nodes-us-east-2" },
58+ Spec : kops.InstanceGroupSpec {
59+ Role : kops .InstanceGroupRoleNode ,
60+ Subnets : []string {"subnet-a" },
61+ MachineType : "g6-standard-2" ,
62+ Image : "linode/ubuntu24.04" ,
63+ MinSize : new (int32 ),
64+ },
65+ }
5466 context := newLinodeInstanceModelBuilderContext (cluster )
5567 addBootstrapPrerequisites (context )
68+ modelContext := contextModel (cluster , []* kops.InstanceGroup {ig , secondIG })
69+ modelContext .SSHPublicKeys = [][]byte {[]byte (testSSHPublicKey )}
5670
5771 networkBuilder := & NetworkModelBuilder {
58- LinodeModelContext : & LinodeModelContext {KopsModelContext : contextModel ( cluster , [] * kops. InstanceGroup { ig }) },
72+ LinodeModelContext : & LinodeModelContext {KopsModelContext : modelContext },
5973 Lifecycle : fi .LifecycleSync ,
6074 }
6175 if err := networkBuilder .Build (context ); err != nil {
6276 t .Fatalf ("NetworkModelBuilder.Build returned error: %v" , err )
6377 }
6478
65- publicKey := fi .Resource (fi .NewStringResource (testSSHPublicKey ))
66- context .AddTask (& linodetasks.SSHKey {
67- Name : new ("example - k8s - local - default "),
68- Lifecycle : fi .LifecycleSync ,
69- PublicKey : & publicKey ,
70- })
71-
7279 builder := & InstanceModelBuilder {
73- LinodeModelContext : & LinodeModelContext {KopsModelContext : contextModel ( cluster , [] * kops. InstanceGroup { ig }) },
80+ LinodeModelContext : & LinodeModelContext {KopsModelContext : modelContext },
7481 Lifecycle : fi .LifecycleSync ,
82+ SSHKeyLifecycle : fi .LifecycleSync ,
7583 BootstrapScriptBuilder : & model.BootstrapScriptBuilder {
7684 Lifecycle : fi .LifecycleSync ,
77- KopsModelContext : contextModel ( cluster , [] * kops. InstanceGroup { ig }) ,
85+ KopsModelContext : modelContext ,
7886 },
7987 }
8088
@@ -101,6 +109,23 @@ func TestInstanceModelBuilderBuildUsesIGSubnetAndAllowsZeroSize(t *testing.T) {
101109 if got , want := len (instanceTask .AuthorizedKeys ), 1 ; got != want {
102110 t .Fatalf ("unexpected authorized key count: got %d, want %d" , got , want )
103111 }
112+ secondInstanceTask := findInstanceTask (t , context , secondIG .Name )
113+ if got , want := len (secondInstanceTask .AuthorizedKeys ), 1 ; got != want {
114+ t .Fatalf ("unexpected second instance authorized key count: got %d, want %d" , got , want )
115+ }
116+ if instanceTask .AuthorizedKeys [0 ] != secondInstanceTask .AuthorizedKeys [0 ] {
117+ t .Fatalf ("expected instance groups to share the SSH key task" )
118+ }
119+
120+ sshKeyTaskCount := 0
121+ for _ , task := range context .Tasks {
122+ if _ , ok := task .(* linodetasks.SSHKey ); ok {
123+ sshKeyTaskCount ++
124+ }
125+ }
126+ if got , want := sshKeyTaskCount , 1 ; got != want {
127+ t .Fatalf ("unexpected SSH key task count: got %d, want %d" , got , want )
128+ }
104129}
105130
106131func TestInstanceModelBuilderBuildRejectsMultipleIGSubnets (t * testing.T ) {
@@ -190,13 +215,6 @@ func TestInstanceModelBuilderBuildDerivesPrivateSubnetInterfacePolicy(t *testing
190215 t .Fatalf ("NetworkModelBuilder.Build returned error: %v" , err )
191216 }
192217
193- publicKey := fi .Resource (fi .NewStringResource (testSSHPublicKey ))
194- context .AddTask (& linodetasks.SSHKey {
195- Name : new ("example - k8s - local - default "),
196- Lifecycle : fi .LifecycleSync ,
197- PublicKey : & publicKey ,
198- })
199-
200218 builder := & InstanceModelBuilder {
201219 LinodeModelContext : & LinodeModelContext {KopsModelContext : contextModel (cluster , []* kops.InstanceGroup {ig })},
202220 Lifecycle : fi .LifecycleSync ,
@@ -216,6 +234,122 @@ func TestInstanceModelBuilderBuildDerivesPrivateSubnetInterfacePolicy(t *testing
216234 }
217235}
218236
237+ func TestInstanceModelBuilderBuildSSHKeyTaskWithPublicKey (t * testing.T ) {
238+ sshKeyName := "custom.ssh:key"
239+ cluster := & kops.Cluster {
240+ ObjectMeta : metav1.ObjectMeta {Name : "example.k8s.local" },
241+ Spec : kops.ClusterSpec {SSHKeyName : new (sshKeyName )},
242+ }
243+ b := & InstanceModelBuilder {
244+ LinodeModelContext : & LinodeModelContext {KopsModelContext : & model.KopsModelContext {
245+ IAMModelContext : iam.IAMModelContext {Cluster : cluster },
246+ SSHPublicKeys : [][]byte {[]byte (testSSHPublicKey )},
247+ }},
248+ SSHKeyLifecycle : fi .LifecycleSync ,
249+ }
250+ context := & fi.CloudupModelBuilderContext {Tasks : map [string ]fi.CloudupTask {}}
251+
252+ if _ , err := b .buildSSHKeyTask (context ); err != nil {
253+ t .Fatalf ("buildSSHKeyTask returned error: %v" , err )
254+ }
255+
256+ if got , want := len (context .Tasks ), 1 ; got != want {
257+ t .Fatalf ("unexpected task count: got %d, want %d" , got , want )
258+ }
259+
260+ for _ , task := range context .Tasks {
261+ sshKey , ok := task .(* linodetasks.SSHKey )
262+ if ! ok {
263+ t .Fatalf ("expected SSHKey task, got %T" , task )
264+ }
265+ if got , want := fi .ValueOf (sshKey .Name ), linode .NormalizeLinodeLabel (sshKeyName ); got != want {
266+ t .Fatalf ("unexpected SSH key name: got %q, want %q" , got , want )
267+ }
268+ if sshKey .PublicKey == nil {
269+ t .Fatalf ("expected SSH public key resource" )
270+ }
271+ publicKey , err := fi .ResourceAsString (* sshKey .PublicKey )
272+ if err != nil {
273+ t .Fatalf ("ResourceAsString returned error: %v" , err )
274+ }
275+ if got , want := publicKey , testSSHPublicKey ; got != want {
276+ t .Fatalf ("unexpected SSH public key: got %q, want %q" , got , want )
277+ }
278+ if got , want := sshKey .Lifecycle , fi .LifecycleSync ; got != want {
279+ t .Fatalf ("unexpected lifecycle: got %q, want %q" , got , want )
280+ }
281+ }
282+ }
283+
284+ func TestInstanceModelBuilderBuildSSHKeyTaskWithExistingKeyName (t * testing.T ) {
285+ sshKeyName := "existing.ssh:key"
286+ cluster := & kops.Cluster {
287+ ObjectMeta : metav1.ObjectMeta {Name : "example.k8s.local" },
288+ Spec : kops.ClusterSpec {SSHKeyName : new (sshKeyName )},
289+ }
290+ b := & InstanceModelBuilder {
291+ LinodeModelContext : & LinodeModelContext {KopsModelContext : & model.KopsModelContext {
292+ IAMModelContext : iam.IAMModelContext {Cluster : cluster },
293+ }},
294+ SSHKeyLifecycle : fi .LifecycleSync ,
295+ }
296+ context := & fi.CloudupModelBuilderContext {Tasks : map [string ]fi.CloudupTask {}}
297+
298+ if _ , err := b .buildSSHKeyTask (context ); err != nil {
299+ t .Fatalf ("buildSSHKeyTask returned error: %v" , err )
300+ }
301+
302+ if got , want := len (context .Tasks ), 1 ; got != want {
303+ t .Fatalf ("unexpected task count: got %d, want %d" , got , want )
304+ }
305+
306+ for _ , task := range context .Tasks {
307+ sshKey , ok := task .(* linodetasks.SSHKey )
308+ if ! ok {
309+ t .Fatalf ("expected SSHKey task, got %T" , task )
310+ }
311+ if got , want := fi .ValueOf (sshKey .Name ), linode .NormalizeLinodeLabel (sshKeyName ); got != want {
312+ t .Fatalf ("unexpected SSH key name: got %q, want %q" , got , want )
313+ }
314+ if sshKey .PublicKey != nil {
315+ t .Fatalf ("expected existing key task to omit public key data" )
316+ }
317+ }
318+ }
319+
320+ func TestInstanceModelBuilderBuildSSHKeyTaskTruncatesLongGeneratedName (t * testing.T ) {
321+ cluster := & kops.Cluster {
322+ ObjectMeta : metav1.ObjectMeta {Name : strings .Repeat ("a" , 32 )},
323+ }
324+ b := & InstanceModelBuilder {
325+ LinodeModelContext : & LinodeModelContext {KopsModelContext : & model.KopsModelContext {
326+ IAMModelContext : iam.IAMModelContext {Cluster : cluster },
327+ SSHPublicKeys : [][]byte {[]byte (testSSHPublicKey )},
328+ }},
329+ SSHKeyLifecycle : fi .LifecycleSync ,
330+ }
331+ context := & fi.CloudupModelBuilderContext {Tasks : map [string ]fi.CloudupTask {}}
332+
333+ if _ , err := b .buildSSHKeyTask (context ); err != nil {
334+ t .Fatalf ("buildSSHKeyTask returned error: %v" , err )
335+ }
336+
337+ prefix := linode .NormalizeLinodeLabel ("kubernetes." + cluster .ObjectMeta .Name )
338+ for _ , task := range context .Tasks {
339+ sshKey , ok := task .(* linodetasks.SSHKey )
340+ if ! ok {
341+ t .Fatalf ("expected SSHKey task, got %T" , task )
342+ }
343+ name := fi .ValueOf (sshKey .Name )
344+ if got , want := len (name ), 64 ; got != want {
345+ t .Fatalf ("unexpected SSH key name length: got %d, want %d" , got , want )
346+ }
347+ if ! strings .HasPrefix (name , prefix + "-" ) {
348+ t .Fatalf ("unexpected SSH key name prefix: got %q, want prefix %q" , name , prefix + "-" )
349+ }
350+ }
351+ }
352+
219353func contextModel (cluster * kops.Cluster , instanceGroups []* kops.InstanceGroup ) * model.KopsModelContext {
220354 return & model.KopsModelContext {
221355 IAMModelContext : iam.IAMModelContext {Cluster : cluster },
0 commit comments