Skip to content

Commit

Permalink
Fix: Resetting always_pxe flag as per user configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
codinja1188 committed Aug 28, 2023
1 parent 6bfc089 commit f0b6944
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 52 deletions.
4 changes: 2 additions & 2 deletions docs/metal_device_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ metal device create -p <project_id> (-m <metro> | -f <facility>) -P <plan> -H <h

```
-a, --always-pxe Sets whether the device always PXE boots on reboot.
-b, --billing-cycle string Billing cycle (default "hourly")
-b, --billing-cycle string Billing cycle (default "hourly")
-c, --customdata string Custom data to be included with your device's metadata.
-f, --facility string Code of the facility where the device will be created
-r, --hardware-reservation-id string The UUID of a hardware reservation, if you are provisioning a server from your reserved hardware.
Expand All @@ -39,7 +39,7 @@ metal device create -p <project_id> (-m <metro> | -f <facility>) -P <plan> -H <h
-s, --spot-instance Provisions the device as a spot instance.
--spot-price-max float Sets the maximum spot market price for the device: --spot-price-max=1.2
-t, --tags strings Tag or list of tags for the device: --tags="tag1,tag2".
-T, --termination-time string Device termination time: --termination-time="15:04:05"
-T, --termination-time string Device termination time: --termination-time="2023-08-24T15:04:05Z"
-u, --userdata string Userdata for device initialization. Can not be used with --userdata-file.
--userdata-file string Path to a userdata file for device initialization. Can not be used with --userdata.
```
Expand Down
86 changes: 74 additions & 12 deletions internal/devices/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ package devices

import (
"context"
"encoding/json"
"fmt"
"os"
"time"

metal "github.com/equinix-labs/metal-go/metal/v1"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -75,18 +77,25 @@ func (c *Client) Create() *cobra.Command {
}
userdata = string(userdataRaw)
}
// var endDt time.Time

// if terminationTime != "" {
// parsedTime, err := time.Parse(time.RFC3339, terminationTime)
// if err != nil {
// return fmt.Errorf("could not parse time %q: %w", terminationTime, err)
// }
// endDt = parsedTime
// }
var endDt time.Time

if terminationTime != "" {
parsedTime, err := time.Parse(time.RFC3339, terminationTime)
if err != nil {
return fmt.Errorf("could not parse time %q: %w", terminationTime, err)
}
endDt = parsedTime
}

formatedCdata := make(map[string]interface{})
if customdata != "" {
err := json.Unmarshal([]byte(customdata), &formatedCdata)
if err != nil {
return fmt.Errorf("could not convert customedata :%w", err)
}
}
var facilityArgs []string

// var deviceCreateInFacilityInput *metal.DeviceCreateInFacilityInput
var request metal.ApiCreateDeviceRequest
if facility != "" {
facilityArgs = append(facilityArgs, facility)
Expand All @@ -97,10 +106,38 @@ func (c *Client) Create() *cobra.Command {
Plan: plan,
OperatingSystem: operatingSystem,
Hostname: &hostname,
Userdata: &userdata,
Tags: tags,
},
}

if billingCycle != "" {
facilityDeviceRequest.DeviceCreateInFacilityInput.SetBillingCycle(billingCycle)
}
if alwaysPXE {
facilityDeviceRequest.DeviceCreateInFacilityInput.SetAlwaysPxe(alwaysPXE)
}

if ipxescripturl != "" {
facilityDeviceRequest.DeviceCreateInFacilityInput.SetIpxeScriptUrl(ipxescripturl)
}
if publicIPv4SubnetSize != 0 {
facilityDeviceRequest.DeviceCreateInFacilityInput.SetPublicIpv4SubnetSize(int32(publicIPv4SubnetSize))
}
if terminationTime != "" {
facilityDeviceRequest.DeviceCreateInFacilityInput.SetTerminationTime(endDt)
}
facilityDeviceRequest.DeviceCreateInFacilityInput.SetSpotInstance(spotInstance)
facilityDeviceRequest.DeviceCreateInFacilityInput.SetSpotPriceMax(float32(spotPriceMax))
facilityDeviceRequest.DeviceCreateInFacilityInput.SetCustomdata(formatedCdata)

if hardwareReservationID != "" {
facilityDeviceRequest.DeviceCreateInFacilityInput.SetHardwareReservationId(hardwareReservationID)
}

request = c.Service.CreateDevice(context.Background(), projectID).CreateDeviceRequest(facilityDeviceRequest).Include(nil).Exclude(nil)
}

if metro != "" {

metroDeviceRequest := metal.CreateDeviceRequest{
Expand All @@ -109,9 +146,34 @@ func (c *Client) Create() *cobra.Command {
Plan: plan,
OperatingSystem: operatingSystem,
Hostname: &hostname,
Userdata: &userdata,
Tags: tags,
},
}
if billingCycle != "" {
metroDeviceRequest.DeviceCreateInMetroInput.SetBillingCycle(billingCycle)
}

if alwaysPXE {
metroDeviceRequest.DeviceCreateInMetroInput.SetAlwaysPxe(alwaysPXE)
}

if ipxescripturl != "" {
metroDeviceRequest.DeviceCreateInMetroInput.SetIpxeScriptUrl(ipxescripturl)
}
if publicIPv4SubnetSize != 0 {
metroDeviceRequest.DeviceCreateInMetroInput.SetPublicIpv4SubnetSize(int32(publicIPv4SubnetSize))
}
if terminationTime != "" {
metroDeviceRequest.DeviceCreateInMetroInput.SetTerminationTime(endDt)
}
metroDeviceRequest.DeviceCreateInMetroInput.SetSpotInstance(spotInstance)
metroDeviceRequest.DeviceCreateInMetroInput.SetSpotPriceMax(float32(spotPriceMax))
metroDeviceRequest.DeviceCreateInMetroInput.SetCustomdata(formatedCdata)

if hardwareReservationID != "" {
metroDeviceRequest.DeviceCreateInMetroInput.SetHardwareReservationId(hardwareReservationID)
}
request = c.Service.CreateDevice(context.Background(), projectID).CreateDeviceRequest(metroDeviceRequest).Include(nil).Exclude(nil)
}
device, _, err := request.Execute()
Expand Down Expand Up @@ -145,11 +207,11 @@ func (c *Client) Create() *cobra.Command {
createDeviceCmd.Flags().StringSliceVarP(&tags, "tags", "t", []string{}, `Tag or list of tags for the device: --tags="tag1,tag2".`)
createDeviceCmd.Flags().IntVarP(&publicIPv4SubnetSize, "public-ipv4-subnet-size", "S", 0, "Size of the public IPv4 subnet.")
createDeviceCmd.Flags().StringVarP(&hardwareReservationID, "hardware-reservation-id", "r", "", "The UUID of a hardware reservation, if you are provisioning a server from your reserved hardware.")
createDeviceCmd.Flags().StringVarP(&billingCycle, "billing-cycle", "b", "hourly", "Billing cycle")
createDeviceCmd.Flags().StringVarP(&billingCycle, "billing-cycle", "b", "hourly", "Billing cycle ")
createDeviceCmd.Flags().BoolVarP(&alwaysPXE, "always-pxe", "a", false, "Sets whether the device always PXE boots on reboot.")
createDeviceCmd.Flags().BoolVarP(&spotInstance, "spot-instance", "s", false, "Provisions the device as a spot instance.")
createDeviceCmd.Flags().Float64VarP(&spotPriceMax, "spot-price-max", "", 0, `Sets the maximum spot market price for the device: --spot-price-max=1.2`)
createDeviceCmd.Flags().StringVarP(&terminationTime, "termination-time", "T", "", `Device termination time: --termination-time="15:04:05"`)
createDeviceCmd.Flags().StringVarP(&terminationTime, "termination-time", "T", "", `Device termination time: --termination-time="2023-08-24T15:04:05Z"`)

return createDeviceCmd
}
5 changes: 2 additions & 3 deletions internal/devices/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ func (c *Client) Update() *cobra.Command {
customdata string
deviceID string
)

// updateDeviceCmd represents the updateDevice command
updateDeviceCmd := &cobra.Command{
Use: `update -i <device_id> [-H <hostname>] [-d <description>] [--locked <boolean>] [-t <tags>] [-u <userdata>] [-c <customdata>] [-s <ipxe_script_url>] [--always-pxe]`,
Expand Down Expand Up @@ -76,7 +75,7 @@ func (c *Client) Update() *cobra.Command {
}

if alwaysPXE {
deviceUpdate.AlwaysPxe = &alwaysPXE
deviceUpdate.SetAlwaysPxe(alwaysPXE)
}

if ipxescripturl != "" {
Expand Down Expand Up @@ -114,7 +113,7 @@ func (c *Client) Update() *cobra.Command {
updateDeviceCmd.Flags().BoolVarP(&alwaysPXE, "always-pxe", "a", false, "Sets the device to always iPXE on reboot.")
updateDeviceCmd.Flags().StringVarP(&ipxescripturl, "ipxe-script-url", "s", "", "Add or update the URL of the iPXE script.")
updateDeviceCmd.Flags().StringVarP(&customdata, "customdata", "c", "", "Adds or updates custom data to be included with your device's metadata.")

_ = updateDeviceCmd.MarkFlagRequired("id")

return updateDeviceCmd
}
101 changes: 101 additions & 0 deletions test/e2e/devices/devicecreateflagstest/device_create_flags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package devicecreateflagstest

import (
"io"
"os"
"regexp"
"strings"
"testing"

root "github.com/equinix/metal-cli/internal/cli"
"github.com/equinix/metal-cli/internal/devices"
outputPkg "github.com/equinix/metal-cli/internal/outputs"
"github.com/equinix/metal-cli/test/helper"
"github.com/spf13/cobra"
)

func TestCli_Devices_Create_Flags(t *testing.T) {
var projectId, deviceId string
var err error
var resp bool
subCommand := "device"
consumerToken := ""
apiURL := ""
Version := "metal"
rootClient := root.NewClient(consumerToken, apiURL, Version)
type fields struct {
MainCmd *cobra.Command
Outputer outputPkg.Outputer
}
tests := []struct {
name string
fields fields
want *cobra.Command
cmdFunc func(*testing.T, *cobra.Command)
}{
{
name: "create_device",
fields: fields{
MainCmd: devices.NewClient(rootClient, outputPkg.Outputer(&outputPkg.Standard{})).NewCommand(),
Outputer: outputPkg.Outputer(&outputPkg.Standard{}),
},
want: &cobra.Command{},
cmdFunc: func(t *testing.T, c *cobra.Command) {
root := c.Root()
projectId, err = helper.CreateTestProject("metal-cli-create-flags-pro")
if err != nil {
t.Error(err)
}
if len(projectId) != 0 {

root.SetArgs([]string{subCommand, "create", "-p", projectId, "-P", "m3.small.x86", "-m", "da", "-H", "metal-cli-create-flags-dev", "--operating-system", "custom_ipxe", "--always-pxe=true", "--ipxe-script-url", "https://boot.netboot.xyz/"})
rescueStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
if err := root.Execute(); err != nil {
t.Error(err)
}
w.Close()
out, _ := io.ReadAll(r)
os.Stdout = rescueStdout
if !strings.Contains(string(out[:]), "metal-cli-create-flags-dev") &&
!strings.Contains(string(out[:]), "Ubuntu 20.04 LTS") &&
!strings.Contains(string(out[:]), "queued") {
t.Error("expected output should include metal-cli-create-flags-dev, Ubuntu 20.04 LTS, and queued strings in the out string ")
}
name := "metal-cli-create-flags-dev"
idNamePattern := `(?m)^\| ([a-zA-Z0-9-]+) +\| *` + name + ` *\|`

// Find the match of the ID and NAME pattern in the table string
match := regexp.MustCompile(idNamePattern).FindStringSubmatch(string(out[:]))

// Extract the ID from the match
if len(match) > 1 {
deviceId = strings.TrimSpace(match[1])
resp, err = helper.IsDeviceStateActive(deviceId)
if err == nil && resp == true {
err = helper.CleanTestDevice(deviceId)
if err != nil {
t.Error(err)
}
err = helper.CleanTestProject(projectId)
if err != nil {
t.Error(err)
}
} else {
t.Error(err)
}
}
}
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rootCmd := rootClient.NewCommand()
rootCmd.AddCommand(tt.fields.MainCmd)
tt.cmdFunc(t, tt.fields.MainCmd)
})
}
}
32 changes: 23 additions & 9 deletions test/e2e/devices/devicecreatetest/device_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ import (
"github.com/spf13/cobra"
)

func TestCli_Devices_create(t *testing.T) {
func TestCli_Devices_Create(t *testing.T) {
var projectId, deviceId string
var err error
var resp bool
subCommand := "device"
consumerToken := ""
apiURL := ""
Expand All @@ -40,10 +42,13 @@ func TestCli_Devices_create(t *testing.T) {
want: &cobra.Command{},
cmdFunc: func(t *testing.T, c *cobra.Command) {
root := c.Root()
projectId = helper.Create_test_project("create-device-pro")
projectId, err = helper.CreateTestProject("metal-cli-create-pro")
if err != nil {
t.Error(err)
}
if len(projectId) != 0 {

root.SetArgs([]string{subCommand, "create", "-p", projectId, "-P", "c3.small.x86", "-m", "da", "-O", "ubuntu_20_04", "-H", "create-device-dev"})
root.SetArgs([]string{subCommand, "create", "-p", projectId, "-P", "m3.small.x86", "-m", "da", "-O", "ubuntu_20_04", "-H", "metal-cli-create-dev"})
rescueStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
Expand All @@ -53,12 +58,12 @@ func TestCli_Devices_create(t *testing.T) {
w.Close()
out, _ := io.ReadAll(r)
os.Stdout = rescueStdout
if !strings.Contains(string(out[:]), "create-device-dev") &&
if !strings.Contains(string(out[:]), "metal-cli-create-dev") &&
!strings.Contains(string(out[:]), "Ubuntu 20.04 LTS") &&
!strings.Contains(string(out[:]), "queued") {
t.Error("expected output should include create-device-dev, Ubuntu 20.04 LTS, and queued strings in the out string ")
t.Error("expected output should include metal-cli-create-dev, Ubuntu 20.04 LTS, and queued strings in the out string ")
}
name := "create-device-dev"
name := "metal-cli-create-dev"
idNamePattern := `(?m)^\| ([a-zA-Z0-9-]+) +\| *` + name + ` *\|`

// Find the match of the ID and NAME pattern in the table string
Expand All @@ -67,9 +72,18 @@ func TestCli_Devices_create(t *testing.T) {
// Extract the ID from the match
if len(match) > 1 {
deviceId = strings.TrimSpace(match[1])
if helper.Is_Device_state_active(deviceId) {
helper.Clean_test_device(deviceId)
helper.Clean_test_project(projectId)
resp, err = helper.IsDeviceStateActive(deviceId)
if err == nil && resp == true {
err = helper.CleanTestDevice(deviceId)
if err != nil {
t.Error(err)
}
err = helper.CleanTestProject(projectId)
if err != nil {
t.Error(err)
}
} else {
t.Error(err)
}
}
}
Expand Down
29 changes: 23 additions & 6 deletions test/e2e/devices/devicegettest/device_get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import (
"github.com/spf13/cobra"
)

func TestCli_Devices_get(t *testing.T) {
func TestCli_Devices_Get(t *testing.T) {
var projectId, deviceId string
var err error
var resp bool
subCommand := "device"
consumerToken := ""
apiURL := ""
Expand All @@ -39,8 +41,14 @@ func TestCli_Devices_get(t *testing.T) {
want: &cobra.Command{},
cmdFunc: func(t *testing.T, c *cobra.Command) {
root := c.Root()
projectId = helper.Create_test_project("get-by-device-id-pro")
deviceId = helper.Create_test_device(projectId, "get-by-device-id-dev")
projectId, err = helper.CreateTestProject("metal-cli-get-pro")
if err != nil {
t.Error(err)
}
deviceId, err = helper.CreateTestDevice(projectId, "metal-cli-get-dev")
if err != nil {
t.Error(err)
}
root.SetArgs([]string{subCommand, "get", "-i", deviceId})
rescueStdout := os.Stdout
r, w, _ := os.Pipe()
Expand All @@ -55,9 +63,18 @@ func TestCli_Devices_get(t *testing.T) {
t.Error("expected output should include " + deviceId)
}
if len(projectId) != 0 && len(deviceId) != 0 {
if helper.Is_Device_state_active(deviceId) {
helper.Clean_test_device(deviceId)
helper.Clean_test_project(projectId)
resp, err = helper.IsDeviceStateActive(deviceId)
if err == nil && resp == true {
err = helper.CleanTestDevice(deviceId)
if err != nil {
t.Error(err)
}
err = helper.CleanTestProject(projectId)
if err != nil {
t.Error(err)
}
} else {
t.Error(err)
}
}
},
Expand Down
Loading

0 comments on commit f0b6944

Please sign in to comment.