diff --git a/lxc.go b/lxc.go index 3f68cf1..b99b3cb 100644 --- a/lxc.go +++ b/lxc.go @@ -13,6 +13,8 @@ import ( "github.com/platform-engineering-labs/formae/pkg/plugin/resource" ) +const MAX_NETWORK_COUNT = 10 + func parseLXCProperties(data json.RawMessage) (*LXCProperties, error) { var props LXCProperties if err := json.Unmarshal(data, &props); err != nil { @@ -83,6 +85,10 @@ func (p *Plugin) CreateLXC(ctx context.Context, req *resource.CreateRequest) (*r urlparams.Add("onboot", strconv.Itoa(props.OnBoot)) } + for i := range min(MAX_NETWORK_COUNT, len(props.Networks)) { + urlparams.Add(fmt.Sprintf("net%d", i), props.Networks[i]) + } + data, err := authenticatedRequest(http.MethodPost, config.URL+"/api2/json/nodes/"+config.NODE+"/lxc", createAuthorizationString(username, token), urlparams) if err != nil { @@ -158,6 +164,39 @@ func (p *Plugin) ReadLXC(ctx context.Context, req *resource.ReadRequest) (*resou lxcdata := props.Data + networks := []string{} + + if len(lxcdata.Net0) > 0 { + networks = append(networks, lxcdata.Net0) + } + if len(lxcdata.Net1) > 0 { + networks = append(networks, lxcdata.Net1) + } + if len(lxcdata.Net2) > 0 { + networks = append(networks, lxcdata.Net2) + } + if len(lxcdata.Net3) > 0 { + networks = append(networks, lxcdata.Net3) + } + if len(lxcdata.Net4) > 0 { + networks = append(networks, lxcdata.Net4) + } + if len(lxcdata.Net5) > 0 { + networks = append(networks, lxcdata.Net5) + } + if len(lxcdata.Net6) > 0 { + networks = append(networks, lxcdata.Net6) + } + if len(lxcdata.Net7) > 0 { + networks = append(networks, lxcdata.Net7) + } + if len(lxcdata.Net8) > 0 { + networks = append(networks, lxcdata.Net8) + } + if len(lxcdata.Net9) > 0 { + networks = append(networks, lxcdata.Net9) + } + properties := LXCProperties{ VMID: req.NativeID, Hostname: lxcdata.Hostname, @@ -165,6 +204,7 @@ func (p *Plugin) ReadLXC(ctx context.Context, req *resource.ReadRequest) (*resou Cores: lxcdata.Cores, Memory: lxcdata.Memory, OnBoot: lxcdata.OnBoot, + Networks: networks, } propsJSON, err := json.Marshal(properties) @@ -264,6 +304,16 @@ func (p *Plugin) UpdateLXC(ctx context.Context, req *resource.UpdateRequest) (*r urlparams.Add("onboot", strconv.Itoa(desir.OnBoot)) } + toDelete := []string{} + for i := range min(MAX_NETWORK_COUNT, len(desir.Networks)) { + if i < len(desir.Networks) { + urlparams.Add(fmt.Sprintf("net%d", i), desir.Networks[i]) + } else { + toDelete = append(toDelete, fmt.Sprintf("net%d", i)) + } + } + urlparams.Add("delete", strings.Join(toDelete, ",")) + _, err = authenticatedRequest("PUT", config.URL+"/api2/json/nodes/"+config.NODE+"/lxc/"+desir.VMID+"/config", createAuthorizationString(username, token), urlparams) if err != nil { diff --git a/proxmox_test.go b/proxmox_test.go index 1352d2d..601b060 100644 --- a/proxmox_test.go +++ b/proxmox_test.go @@ -34,6 +34,7 @@ func TestCreate(t *testing.T) { "password": "password", "cores": 1, "memory": 512, + "networks": []string{"name=test,hwaddr=BC:24:11:FD:90:BF,bridge=internal,firewall=1"}, } propertiesJSON, err := json.Marshal(properties) @@ -103,6 +104,7 @@ func TestRead(t *testing.T) { require.Equal(t, mem_num, props["memory"], "memory should match") const onboot float64 = 0 require.Equal(t, onboot, props["onboot"], "memory should match") + require.NotEqual(t, nil, props["networks"], "network should be defined") } func TestUpdate(t *testing.T) { @@ -116,6 +118,7 @@ func TestUpdate(t *testing.T) { "ostemplate": "local:vztmpl/alpine-3.22-default_20250617_amd64.tar.xz", "cores": 1, "memory": 512, + "networks": []string{"name=test,hwaddr=BC:24:11:FD:90:BF,bridge=internal,firewall=1"}, }) desiredProperties, _ := json.Marshal(map[string]any{ @@ -126,6 +129,7 @@ func TestUpdate(t *testing.T) { "cores": 2, "memory": 1024, "onboot": 1, + "networks": []string{"name=test1,hwaddr=BC:24:11:FD:90:BF,bridge=internal,firewall=1"}, }) req := &resource.UpdateRequest{ diff --git a/schema/pkl/proxmox.pkl b/schema/pkl/proxmox.pkl index ad0b068..fda5b83 100644 --- a/schema/pkl/proxmox.pkl +++ b/schema/pkl/proxmox.pkl @@ -49,4 +49,7 @@ class LXC extends formae.Resource { @formae.FieldHint {} sshkeys: Listing? + @formae.FieldHint {} + networks: Listing? + } diff --git a/testdata/resource-update.pkl b/testdata/resource-update.pkl index 28f5303..0a687d9 100644 --- a/testdata/resource-update.pkl +++ b/testdata/resource-update.pkl @@ -34,5 +34,8 @@ forma { cores = 2 memory = 1024 onboot = 1 + networks = new Listing { + "name=first,hwaddr=BC:24:11:FD:90:BF,bridge=internal" + } } } diff --git a/testdata/resource.pkl b/testdata/resource.pkl index 7883e89..14b384b 100644 --- a/testdata/resource.pkl +++ b/testdata/resource.pkl @@ -36,5 +36,9 @@ forma { password = "abcd" cores = 1 memory = 512 + networks = new Listing { + "name=first,hwaddr=BC:24:11:FD:90:BF,bridge=internal" + "name=second,hwaddr=BC:24:11:FD:90:BF,bridge=internal" + } } } diff --git a/types.go b/types.go index 9b06965..f0f6b8c 100644 --- a/types.go +++ b/types.go @@ -17,6 +17,7 @@ type LXCProperties struct { Memory int `json:"memory"` OnBoot int `json:"onboot"` SSHKeys []string `json:"sshkeys"` + Networks []string `json:"networks"` } type ReadRequest struct { @@ -75,6 +76,16 @@ type StatusLXCConfig struct { Description string `json:"description"` Digest string `json:"digest"` OnBoot int `json:"onboot"` + Net0 string `json:"net0,omitempty"` + Net1 string `json:"net1,omitempty"` + Net2 string `json:"net2,omitempty"` + Net3 string `json:"net3,omitempty"` + Net4 string `json:"net4,omitempty"` + Net5 string `json:"net5,omitempty"` + Net6 string `json:"net6,omitempty"` + Net7 string `json:"net7,omitempty"` + Net8 string `json:"net8,omitempty"` + Net9 string `json:"net9,omitempty"` } type StatusLXCConfigResponse struct {