1package v2 2 3import ( 4 "context" 5 "net" 6 7 apiv2 "github.com/exoscale/egoscale/v2/api" 8 papi "github.com/exoscale/egoscale/v2/internal/public-api" 9) 10 11// PrivateNetworkLease represents a managed Private Network lease. 12type PrivateNetworkLease struct { 13 InstanceID *string 14 IPAddress *net.IP 15} 16 17// PrivateNetwork represents a Private Network. 18type PrivateNetwork struct { 19 Description *string 20 EndIP *net.IP 21 ID *string `req-for:"update"` 22 Name *string `req-for:"create"` 23 Netmask *net.IP 24 StartIP *net.IP 25 Leases []*PrivateNetworkLease 26 27 c *Client 28 zone string 29} 30 31func privateNetworkFromAPI(client *Client, zone string, p *papi.PrivateNetwork) *PrivateNetwork { 32 return &PrivateNetwork{ 33 Description: p.Description, 34 EndIP: func() (v *net.IP) { 35 if p.EndIp != nil { 36 ip := net.ParseIP(*p.EndIp) 37 v = &ip 38 } 39 return 40 }(), 41 ID: p.Id, 42 Name: p.Name, 43 Netmask: func() (v *net.IP) { 44 if p.Netmask != nil { 45 ip := net.ParseIP(*p.Netmask) 46 v = &ip 47 } 48 return 49 }(), 50 StartIP: func() (v *net.IP) { 51 if p.StartIp != nil { 52 ip := net.ParseIP(*p.StartIp) 53 v = &ip 54 } 55 return 56 }(), 57 Leases: func() (v []*PrivateNetworkLease) { 58 if p.Leases != nil { 59 v = make([]*PrivateNetworkLease, len(*p.Leases)) 60 for i, lease := range *p.Leases { 61 v[i] = &PrivateNetworkLease{ 62 InstanceID: lease.InstanceId, 63 IPAddress: func() *net.IP { ip := net.ParseIP(*lease.Ip); return &ip }(), 64 } 65 } 66 } 67 return 68 }(), 69 70 c: client, 71 zone: zone, 72 } 73} 74 75func (p PrivateNetwork) get(ctx context.Context, client *Client, zone, id string) (interface{}, error) { 76 return client.GetPrivateNetwork(ctx, zone, id) 77} 78 79// UpdateInstanceIPAddress updates the IP address of a Compute instance attached to the managed Private Network. 80func (p *PrivateNetwork) UpdateInstanceIPAddress(ctx context.Context, instance *Instance, ip net.IP) error { 81 resp, err := p.c.UpdatePrivateNetworkInstanceIpWithResponse( 82 apiv2.WithZone(ctx, p.zone), 83 *p.ID, 84 papi.UpdatePrivateNetworkInstanceIpJSONRequestBody{ 85 Instance: papi.Instance{Id: instance.ID}, 86 Ip: func() *string { 87 s := ip.String() 88 return &s 89 }(), 90 }) 91 if err != nil { 92 return err 93 } 94 95 _, err = papi.NewPoller(). 96 WithTimeout(p.c.timeout). 97 WithInterval(p.c.pollInterval). 98 Poll(ctx, p.c.OperationPoller(p.zone, *resp.JSON200.Id)) 99 100 return err 101} 102 103// CreatePrivateNetwork creates a Private Network in the specified zone. 104func (c *Client) CreatePrivateNetwork( 105 ctx context.Context, 106 zone string, 107 privateNetwork *PrivateNetwork, 108) (*PrivateNetwork, error) { 109 if err := validateOperationParams(privateNetwork, "create"); err != nil { 110 return nil, err 111 } 112 113 resp, err := c.CreatePrivateNetworkWithResponse( 114 apiv2.WithZone(ctx, zone), 115 papi.CreatePrivateNetworkJSONRequestBody{ 116 Description: privateNetwork.Description, 117 EndIp: func() (ip *string) { 118 if privateNetwork.EndIP != nil { 119 v := privateNetwork.EndIP.String() 120 return &v 121 } 122 return 123 }(), 124 Name: *privateNetwork.Name, 125 Netmask: func() (ip *string) { 126 if privateNetwork.Netmask != nil { 127 v := privateNetwork.Netmask.String() 128 return &v 129 } 130 return 131 }(), 132 StartIp: func() (ip *string) { 133 if privateNetwork.StartIP != nil { 134 v := privateNetwork.StartIP.String() 135 return &v 136 } 137 return 138 }(), 139 }) 140 if err != nil { 141 return nil, err 142 } 143 144 res, err := papi.NewPoller(). 145 WithTimeout(c.timeout). 146 WithInterval(c.pollInterval). 147 Poll(ctx, c.OperationPoller(zone, *resp.JSON200.Id)) 148 if err != nil { 149 return nil, err 150 } 151 152 return c.GetPrivateNetwork(ctx, zone, *res.(*papi.Reference).Id) 153} 154 155// ListPrivateNetworks returns the list of existing Private Networks in the specified zone. 156func (c *Client) ListPrivateNetworks(ctx context.Context, zone string) ([]*PrivateNetwork, error) { 157 list := make([]*PrivateNetwork, 0) 158 159 resp, err := c.ListPrivateNetworksWithResponse(apiv2.WithZone(ctx, zone)) 160 if err != nil { 161 return nil, err 162 } 163 164 if resp.JSON200.PrivateNetworks != nil { 165 for i := range *resp.JSON200.PrivateNetworks { 166 list = append(list, privateNetworkFromAPI(c, zone, &(*resp.JSON200.PrivateNetworks)[i])) 167 } 168 } 169 170 return list, nil 171} 172 173// GetPrivateNetwork returns the Private Network corresponding to the specified ID in the specified zone. 174func (c *Client) GetPrivateNetwork(ctx context.Context, zone, id string) (*PrivateNetwork, error) { 175 resp, err := c.GetPrivateNetworkWithResponse(apiv2.WithZone(ctx, zone), id) 176 if err != nil { 177 return nil, err 178 } 179 180 return privateNetworkFromAPI(c, zone, resp.JSON200), nil 181} 182 183// FindPrivateNetwork attempts to find a Private Network by name or ID in the specified zone. 184// In case the identifier is a name and multiple resources match, an ErrTooManyFound error is returned. 185func (c *Client) FindPrivateNetwork(ctx context.Context, zone, v string) (*PrivateNetwork, error) { 186 res, err := c.ListPrivateNetworks(ctx, zone) 187 if err != nil { 188 return nil, err 189 } 190 191 var found *PrivateNetwork 192 for _, r := range res { 193 if *r.ID == v { 194 return c.GetPrivateNetwork(ctx, zone, *r.ID) 195 } 196 197 // Historically, the Exoscale API allowed users to create multiple Private Networks sharing a common name. 198 // This function being expected to return one resource at most, in case the specified identifier is a name 199 // we have to check that there aren't more that one matching result before returning it. 200 if *r.Name == v { 201 if found != nil { 202 return nil, apiv2.ErrTooManyFound 203 } 204 found = r 205 } 206 } 207 208 if found != nil { 209 return c.GetPrivateNetwork(ctx, zone, *found.ID) 210 } 211 212 return nil, apiv2.ErrNotFound 213} 214 215// UpdatePrivateNetwork updates the specified Private Network in the specified zone. 216func (c *Client) UpdatePrivateNetwork(ctx context.Context, zone string, privateNetwork *PrivateNetwork) error { 217 if err := validateOperationParams(privateNetwork, "update"); err != nil { 218 return err 219 } 220 221 resp, err := c.UpdatePrivateNetworkWithResponse( 222 apiv2.WithZone(ctx, zone), 223 *privateNetwork.ID, 224 papi.UpdatePrivateNetworkJSONRequestBody{ 225 Description: privateNetwork.Description, 226 EndIp: func() (ip *string) { 227 if privateNetwork.EndIP != nil { 228 v := privateNetwork.EndIP.String() 229 return &v 230 } 231 return 232 }(), 233 Name: privateNetwork.Name, 234 Netmask: func() (ip *string) { 235 if privateNetwork.Netmask != nil { 236 v := privateNetwork.Netmask.String() 237 return &v 238 } 239 return 240 }(), 241 StartIp: func() (ip *string) { 242 if privateNetwork.StartIP != nil { 243 v := privateNetwork.StartIP.String() 244 return &v 245 } 246 return 247 }(), 248 }) 249 if err != nil { 250 return err 251 } 252 253 _, err = papi.NewPoller(). 254 WithTimeout(c.timeout). 255 WithInterval(c.pollInterval). 256 Poll(ctx, c.OperationPoller(zone, *resp.JSON200.Id)) 257 if err != nil { 258 return err 259 } 260 261 return nil 262} 263 264// DeletePrivateNetwork deletes the specified Private Network in the specified zone. 265func (c *Client) DeletePrivateNetwork(ctx context.Context, zone, id string) error { 266 resp, err := c.DeletePrivateNetworkWithResponse(apiv2.WithZone(ctx, zone), id) 267 if err != nil { 268 return err 269 } 270 271 _, err = papi.NewPoller(). 272 WithTimeout(c.timeout). 273 WithInterval(c.pollInterval). 274 Poll(ctx, c.OperationPoller(zone, *resp.JSON200.Id)) 275 if err != nil { 276 return err 277 } 278 279 return nil 280} 281