@@ -44,6 +44,10 @@ type Driver struct {
4444 UsePrivateNetwork bool
4545 DisablePublic4 bool
4646 DisablePublic6 bool
47+ PrimaryIPv4 string
48+ cachedPrimaryIPv4 * hcloud.PrimaryIP
49+ PrimaryIPv6 string
50+ cachedPrimaryIPv6 * hcloud.PrimaryIP
4751 Firewalls []string
4852 ServerLabels map [string ]string
4953 keyLabels map [string ]string
@@ -72,6 +76,8 @@ const (
7276 flagUsePrivateNetwork = "hetzner-use-private-network"
7377 flagDisablePublic4 = "hetzner-disable-public-4"
7478 flagDisablePublic6 = "hetzner-disable-public-6"
79+ flagPrimary4 = "hetzner-primary-ipv4"
80+ flagPrimary6 = "hetzner-primary-ipv6"
7581 flagDisablePublic = "hetzner-disable-public"
7682 flagFirewalls = "hetzner-firewalls"
7783 flagAdditionalKeys = "hetzner-additional-key"
@@ -189,6 +195,18 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
189195 Name : flagDisablePublic ,
190196 Usage : "Disable public ip (v4 & v6)" ,
191197 },
198+ mcnflag.StringFlag {
199+ EnvVar : "HETZNER_PRIMARY_IPV4" ,
200+ Name : flagPrimary4 ,
201+ Usage : "Existing primary IPv4 address" ,
202+ Value : "" ,
203+ },
204+ mcnflag.StringFlag {
205+ EnvVar : "HETZNER_PRIMARY_IPV6" ,
206+ Name : flagPrimary6 ,
207+ Usage : "Existing primary IPv6 address" ,
208+ Value : "" ,
209+ },
192210 mcnflag.StringSliceFlag {
193211 EnvVar : "HETZNER_FIREWALLS" ,
194212 Name : flagFirewalls ,
@@ -261,6 +279,8 @@ func (d *Driver) setConfigFromFlagsImpl(opts drivers.DriverOptions) error {
261279 d .UsePrivateNetwork = opts .Bool (flagUsePrivateNetwork ) || disablePublic
262280 d .DisablePublic4 = opts .Bool (flagDisablePublic4 ) || disablePublic
263281 d .DisablePublic6 = opts .Bool (flagDisablePublic6 ) || disablePublic
282+ d .PrimaryIPv4 = opts .String (flagPrimary4 )
283+ d .PrimaryIPv6 = opts .String (flagPrimary6 )
264284 d .Firewalls = opts .StringSlice (flagFirewalls )
265285 d .AdditionalKeys = opts .StringSlice (flagAdditionalKeys )
266286
@@ -297,6 +317,14 @@ func (d *Driver) setConfigFromFlagsImpl(opts drivers.DriverOptions) error {
297317 flagUsePrivateNetwork , flagDisablePublic )
298318 }
299319
320+ if d .DisablePublic4 && d .PrimaryIPv4 != "" {
321+ return d .flagFailure ("--%v and --%v are mutually exclusive" , flagPrimary4 , flagDisablePublic4 )
322+ }
323+
324+ if d .DisablePublic6 && d .PrimaryIPv6 != "" {
325+ return d .flagFailure ("--%v and --%v are mutually exclusive" , flagPrimary6 , flagDisablePublic6 )
326+ }
327+
300328 return nil
301329}
302330
@@ -375,6 +403,14 @@ func (d *Driver) PreCreateCheck() error {
375403 return fmt .Errorf ("could not create placement group: %w" , err )
376404 }
377405
406+ if _ , err := d .getPrimaryIPv4 (); err != nil {
407+ return fmt .Errorf ("could not resolve primary IPv4: %w" , err )
408+ }
409+
410+ if _ , err := d .getPrimaryIPv6 (); err != nil {
411+ return fmt .Errorf ("could not resolve primary IPv6: %w" , err )
412+ }
413+
378414 if d .UsePrivateNetwork && len (d .Networks ) == 0 {
379415 return errors .Errorf ("No private network attached." )
380416 }
@@ -495,11 +531,9 @@ func (d *Driver) makeCreateServerOptions() (*hcloud.ServerCreateOpts, error) {
495531 PlacementGroup : pgrp ,
496532 }
497533
498- if d .DisablePublic4 || d .DisablePublic6 {
499- srvopts .PublicNet = & hcloud.ServerCreatePublicNet {
500- EnableIPv4 : ! d .DisablePublic4 ,
501- EnableIPv6 : ! d .DisablePublic6 ,
502- }
534+ err = d .setPublicNetIfRequired (srvopts )
535+ if err != nil {
536+ return nil , err
503537 }
504538
505539 networks , err := d .createNetworks ()
@@ -537,6 +571,27 @@ func (d *Driver) makeCreateServerOptions() (*hcloud.ServerCreateOpts, error) {
537571 return & srvopts , nil
538572}
539573
574+ func (d * Driver ) setPublicNetIfRequired (srvopts hcloud.ServerCreateOpts ) error {
575+ pip4 , err := d .getPrimaryIPv4 ()
576+ if err != nil {
577+ return err
578+ }
579+ pip6 , err := d .getPrimaryIPv6 ()
580+ if err != nil {
581+ return err
582+ }
583+
584+ if d .DisablePublic4 || d .DisablePublic6 || pip4 != nil || pip6 != nil {
585+ srvopts .PublicNet = & hcloud.ServerCreatePublicNet {
586+ EnableIPv4 : ! d .DisablePublic4 ,
587+ EnableIPv6 : ! d .DisablePublic6 ,
588+ IPv4 : pip4 ,
589+ IPv6 : pip6 ,
590+ }
591+ }
592+ return nil
593+ }
594+
540595func (d * Driver ) createNetworks () ([]* hcloud.Network , error ) {
541596 networks := []* hcloud.Network {}
542597 for _ , networkIDorName := range d .Networks {
@@ -1094,3 +1149,52 @@ func (d *Driver) removeEmptyServerPlacementGroup(srv *hcloud.Server) error {
10941149 return nil
10951150 }
10961151}
1152+
1153+ func (d * Driver ) getPrimaryIPv4 () (* hcloud.PrimaryIP , error ) {
1154+ raw := d .PrimaryIPv4
1155+ if raw == "" {
1156+ return nil , nil
1157+ } else if d .cachedPrimaryIPv4 != nil {
1158+ return d .cachedPrimaryIPv4 , nil
1159+ }
1160+
1161+ ip , err := d .resolvePrimaryIP (raw )
1162+ d .cachedPrimaryIPv4 = ip
1163+ return ip , err
1164+ }
1165+
1166+ func (d * Driver ) getPrimaryIPv6 () (* hcloud.PrimaryIP , error ) {
1167+ raw := d .PrimaryIPv6
1168+ if raw == "" {
1169+ return nil , nil
1170+ } else if d .cachedPrimaryIPv6 != nil {
1171+ return d .cachedPrimaryIPv6 , nil
1172+ }
1173+
1174+ ip , err := d .resolvePrimaryIP (raw )
1175+ d .cachedPrimaryIPv6 = ip
1176+ return ip , err
1177+ }
1178+
1179+ func (d * Driver ) resolvePrimaryIP (raw string ) (* hcloud.PrimaryIP , error ) {
1180+ client := d .getClient ().PrimaryIP
1181+
1182+ var getter func (context.Context , string ) (* hcloud.PrimaryIP , * hcloud.Response , error )
1183+ if net .ParseIP (raw ) != nil {
1184+ getter = client .GetByIP
1185+ } else {
1186+ getter = client .Get
1187+ }
1188+
1189+ ip , _ , err := getter (context .Background (), raw )
1190+
1191+ if err != nil {
1192+ return nil , fmt .Errorf ("could not get primary IP: %w" , err )
1193+ }
1194+
1195+ if ip != nil {
1196+ return ip , nil
1197+ }
1198+
1199+ return nil , fmt .Errorf ("primary IP not found: %v" , raw )
1200+ }
0 commit comments