Skip to content

Commit

Permalink
feat: allow disabling the server public ips (#190)
Browse files Browse the repository at this point in the history
Allow disabling the server's public IPs, and use the first available IP
to connect to it (IPv4, IPv6, first private network IP).

Closes #188
  • Loading branch information
jooola authored Jun 5, 2024
1 parent c630760 commit 82e6cdb
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 38 deletions.
10 changes: 10 additions & 0 deletions .web-docs/components/builder/hcloud/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ servers that are launched within the Hetzner Cloud.
The builder does _not_ manage images. Once it creates an image, it is up to you
to use it or delete it.

The builder will connect to the server using the first available IP, in the following order:

- `public_ipv4`
- `public_ipv6`
- `private_ipv4`

## Configuration Reference

There are many configuration options available for the builder. They are
Expand Down Expand Up @@ -134,9 +140,13 @@ builder.
- `public_ipv4` (string) - ID, name or IP address of a pre-allocated Hetzner
Primary IPv4 address to use for the created server.

- `public_ipv4_disabled` (bool) - Disable the public ipv4 for the created server.

- `public_ipv6` (string) - ID, name or IP address of a pre-allocated Hetzner
Primary IPv6 address to use for the created server.

- `public_ipv4_disabled` (bool) - Disable the public ipv6 for the created server.

## Basic Example

Here is a basic example. It is completely valid as soon as you enter your own
Expand Down
9 changes: 6 additions & 3 deletions builder/hcloud/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,12 @@ type Config struct {
UserDataFile string `mapstructure:"user_data_file"`
SSHKeys []string `mapstructure:"ssh_keys"`
SSHKeysLabels map[string]string `mapstructure:"ssh_keys_labels"`
Networks []int64 `mapstructure:"networks"`
PublicIPv4 string `mapstructure:"public_ipv4"`
PublicIPv6 string `mapstructure:"public_ipv6"`

Networks []int64 `mapstructure:"networks"`
PublicIPv4 string `mapstructure:"public_ipv4"`
PublicIPv4Disabled bool `mapstructure:"public_ipv4_disabled"`
PublicIPv6 string `mapstructure:"public_ipv6"`
PublicIPv6Disabled bool `mapstructure:"public_ipv6_disabled"`

RescueMode string `mapstructure:"rescue"`

Expand Down
4 changes: 4 additions & 0 deletions builder/hcloud/config.hcl2spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

79 changes: 45 additions & 34 deletions builder/hcloud/step_create_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,34 +80,32 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu
UserData: userData,
Networks: networks,
Labels: c.ServerLabels,
PublicNet: &hcloud.ServerCreatePublicNet{
EnableIPv4: !c.PublicIPv4Disabled,
EnableIPv6: !c.PublicIPv6Disabled,
},
}

if c.PublicIPv4 != "" || c.PublicIPv6 != "" {
publicNetOpts := hcloud.ServerCreatePublicNet{
EnableIPv4: true,
EnableIPv6: true,
if !c.PublicIPv4Disabled && c.PublicIPv4 != "" {
publicIPv4, msg, err := getPrimaryIP(ctx, client, c.PublicIPv4)
if err != nil {
return errorHandler(state, ui, msg, err)
}
if publicIPv4.Type != hcloud.PrimaryIPTypeIPv4 {
return errorHandler(state, ui, "", fmt.Errorf("Primary ip %s is not an IPv4 address", c.PublicIPv4))
}
if c.PublicIPv4 != "" {
publicIPv4, msg, err := getPrimaryIP(ctx, client, c.PublicIPv4)
if err != nil {
return errorHandler(state, ui, msg, err)
}
if publicIPv4.Type != hcloud.PrimaryIPTypeIPv4 {
return errorHandler(state, ui, "", fmt.Errorf("Primary ip %s is not an IPv4 address", c.PublicIPv4))
}
publicNetOpts.IPv4 = publicIPv4
serverCreateOpts.PublicNet.IPv4 = publicIPv4
}

if !c.PublicIPv6Disabled && c.PublicIPv6 != "" {
publicIPv6, msg, err := getPrimaryIP(ctx, client, c.PublicIPv6)
if err != nil {
return errorHandler(state, ui, msg, err)
}
if c.PublicIPv6 != "" {
publicIPv6, msg, err := getPrimaryIP(ctx, client, c.PublicIPv6)
if err != nil {
return errorHandler(state, ui, msg, err)
}
if publicIPv6.Type != hcloud.PrimaryIPTypeIPv6 {
return errorHandler(state, ui, "", fmt.Errorf("Primary ip %s is not an IPv6 address", c.PublicIPv6))
}
publicNetOpts.IPv6 = publicIPv6
if publicIPv6.Type != hcloud.PrimaryIPTypeIPv6 {
return errorHandler(state, ui, "", fmt.Errorf("Primary ip %s is not an IPv6 address", c.PublicIPv6))
}
serverCreateOpts.PublicNet = &publicNetOpts
serverCreateOpts.PublicNet.IPv6 = publicIPv6
}

if c.UpgradeServerType != "" {
Expand All @@ -118,26 +116,39 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu
if err != nil {
return errorHandler(state, ui, "Could not create server", err)
}
state.Put(StateServerIP, serverCreateResult.Server.PublicNet.IPv4.IP.String())

// We use this in cleanup
s.serverId = serverCreateResult.Server.ID

// Store the server id for later
state.Put(StateServerID, serverCreateResult.Server.ID)
// instance_id is the generic term used so that users can have access to the
// instance id inside of the provisioners, used in step_provision.
state.Put(StateInstanceID, serverCreateResult.Server.ID)

if err := client.Action.WaitFor(ctx, serverCreateResult.Action); err != nil {
return errorHandler(state, ui, "Could not create server", err)
}
if err := client.Action.WaitFor(ctx, serverCreateResult.NextActions...); err != nil {
return errorHandler(state, ui, "Could not create server", err)
}

// Store server data for later
server := serverCreateResult.Server

state.Put(StateServerID, server.ID)
// instance_id is the generic term used so that users can have access to the
// instance id inside of the provisioners, used in step_provision.
state.Put(StateInstanceID, server.ID)

switch {
case !server.PublicNet.IPv4.IsUnspecified():
state.Put(StateServerIP, server.PublicNet.IPv4.IP.String())
case !server.PublicNet.IPv6.IsUnspecified():
state.Put(StateServerIP, server.PublicNet.IPv6.IP.String())
case len(server.PrivateNet) > 0:
state.Put(StateServerIP, server.PrivateNet[0].IP.String())
default:
return errorHandler(state, ui, "", fmt.Errorf("Could not find server ip"))
}

if c.UpgradeServerType != "" {
ui.Say("Upgrading server type...")
serverChangeTypeAction, _, err := client.Server.ChangeType(ctx, serverCreateResult.Server, hcloud.ServerChangeTypeOpts{
serverChangeTypeAction, _, err := client.Server.ChangeType(ctx, server, hcloud.ServerChangeTypeOpts{
ServerType: &hcloud.ServerType{Name: c.UpgradeServerType},
UpgradeDisk: false,
})
Expand All @@ -150,7 +161,7 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu
}

ui.Say("Starting server...")
serverPoweronAction, _, err := client.Server.Poweron(ctx, serverCreateResult.Server)
serverPoweronAction, _, err := client.Server.Poweron(ctx, server)
if err != nil {
return errorHandler(state, ui, "Could not start server", err)
}
Expand All @@ -162,12 +173,12 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu

if c.RescueMode != "" {
ui.Say("Enabling Rescue Mode...")
_, err := setRescue(ctx, client, serverCreateResult.Server, c.RescueMode, sshKeys)
_, err := setRescue(ctx, client, server, c.RescueMode, sshKeys)
if err != nil {
return errorHandler(state, ui, "Could not enable rescue mode", err)
}
ui.Say("Rebooting server...")
action, _, err := client.Server.Reset(ctx, serverCreateResult.Server)
action, _, err := client.Server.Reset(ctx, server)
if err != nil {
return errorHandler(state, ui, "Could not reboot server", err)
}
Expand Down
3 changes: 2 additions & 1 deletion builder/hcloud/step_create_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ func TestStepCreateServer(t *testing.T) {
assert.Equal(t, int64(114690387), int64(payload.Image.(float64)))
assert.Equal(t, "nbg1", payload.Location)
assert.Equal(t, "cpx11", payload.ServerType)
assert.True(t, payload.PublicNet.EnableIPv4)
assert.True(t, payload.PublicNet.EnableIPv6)
assert.Nil(t, payload.Networks)
assert.Nil(t, payload.PublicNet)
},
201, `{
"server": { "id": 8, "name": "dummy-server", "public_net": { "ipv4": { "ip": "1.2.3.4" }}},
Expand Down
10 changes: 10 additions & 0 deletions docs/builders/hcloud.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ servers that are launched within the Hetzner Cloud.
The builder does _not_ manage images. Once it creates an image, it is up to you
to use it or delete it.

The builder will connect to the server using the first available IP, in the following order:

- `public_ipv4`
- `public_ipv6`
- `private_ipv4`

## Configuration Reference

There are many configuration options available for the builder. They are
Expand Down Expand Up @@ -124,9 +130,13 @@ builder.
- `public_ipv4` (string) - ID, name or IP address of a pre-allocated Hetzner
Primary IPv4 address to use for the created server.

- `public_ipv4_disabled` (bool) - Disable the public ipv4 for the created server.

- `public_ipv6` (string) - ID, name or IP address of a pre-allocated Hetzner
Primary IPv6 address to use for the created server.

- `public_ipv4_disabled` (bool) - Disable the public ipv6 for the created server.

## Basic Example

Here is a basic example. It is completely valid as soon as you enter your own
Expand Down

0 comments on commit 82e6cdb

Please sign in to comment.