Code Monkey home page Code Monkey logo

linodego's Introduction

linodego

Tests Release GoDoc Go Report Card

Go client for Linode REST v4 API

Installation

go get -u github.com/linode/linodego

Documentation

See godoc for a complete reference.

The API generally follows the naming patterns prescribed in the OpenAPIv3 document for Linode APIv4.

Deviations in naming have been made to avoid using "Linode" and "Instance" redundantly or inconsistently.

A brief summary of the features offered in this API client are shown here.

Examples

General Usage

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/linode/linodego"
	"golang.org/x/oauth2"
)

func main() {
	apiKey, ok := os.LookupEnv("LINODE_TOKEN")
	if !ok {
		log.Fatal("Could not find LINODE_TOKEN, please assert it is set.")
	}
	tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: apiKey})

	oauth2Client := &http.Client{
		Transport: &oauth2.Transport{
			Source: tokenSource,
		},
	}

	linodeClient := linodego.NewClient(oauth2Client)
	linodeClient.SetDebug(true)

	res, err := linodeClient.GetInstance(context.Background(), 4090913)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%v", res)
}

Pagination

Auto-Pagination Requests

kernels, err := linodego.ListKernels(context.Background(), nil)
// len(kernels) == 218

Or, use a page value of "0":

opts := linodego.NewListOptions(0,"")
kernels, err := linodego.ListKernels(context.Background(), opts)
// len(kernels) == 218

Single Page

opts := linodego.NewListOptions(2,"")
// or opts := linodego.ListOptions{PageOptions: &linodego.PageOptions{Page: 2}, PageSize: 500}
kernels, err := linodego.ListKernels(context.Background(), opts)
// len(kernels) == 100

ListOptions are supplied as a pointer because the Pages and Results values are set in the supplied ListOptions.

// opts.Results == 218

Filtering

f := linodego.Filter{}
f.AddField(linodego.Eq, "mine", true)
fStr, err := f.MarshalJSON()
if err != nil {
    log.Fatal(err)
}
opts := linodego.NewListOptions(0, string(fStr))
stackscripts, err := linodego.ListStackscripts(context.Background(), opts)

Error Handling

Getting Single Entities

linode, err := linodego.GetInstance(context.Background(), 555) // any Linode ID that does not exist or is not yours
// linode == nil: true
// err.Error() == "[404] Not Found"
// err.Code == "404"
// err.Message == "Not Found"

Lists

For lists, the list is still returned as [], but err works the same way as on the Get request.

linodes, err := linodego.ListInstances(context.Background(), linodego.NewListOptions(0, "{\"foo\":bar}"))
// linodes == []
// err.Error() == "[400] [X-Filter] Cannot filter on foo"

Otherwise sane requests beyond the last page do not trigger an error, just an empty result:

linodes, err := linodego.ListInstances(context.Background(), linodego.NewListOptions(9999, ""))
// linodes == []
// err = nil

Response Caching

By default, certain endpoints with static responses will be cached into memory. Endpoints with cached responses are identified in their accompanying documentation.

The default cache entry expiry time is 15 minutes. Certain endpoints may override this value to allow for more frequent refreshes (e.g. client.GetRegion(...)). The global cache expiry time can be customized using the client.SetGlobalCacheExpiration(...) method.

Response caching can be globally disabled or enabled for a client using the client.UseCache(...) method.

The global cache can be cleared and refreshed using the client.InvalidateCache() method.

Writes

When performing a POST or PUT request, multiple field related errors will be returned as a single error, currently like:

// err.Error() == "[400] [field1] foo problem; [field2] bar problem; [field3] baz problem"

Tests

Run make testunit to run the unit tests.

Run make testint to run the integration tests. The integration tests use fixtures.

To update the test fixtures, run make fixtures. This will record the API responses into the fixtures/ directory. Be careful about committing any sensitive account details. An attempt has been made to sanitize IP addresses and dates, but no automated sanitization will be performed against fixtures/*Account*.yaml, for example.

To prevent disrupting unaffected fixtures, target fixture generation like so: make ARGS="-run TestListVolumes" fixtures.

Discussion / Help

Join us at #linodego on the gophers slack

License

MIT License

linodego's People

Contributors

0xch4z avatar adammohammed avatar amisiorek-akamai avatar asauber avatar chiefy avatar cliedeman avatar dazwilkin avatar dependabot[bot] avatar displague avatar ellisbenjamin avatar erikzilber avatar ezilber-akamai avatar ezilber-akamai-zz avatar felicianotech avatar irishgordo avatar jcallahan-akamai avatar jfrederickson avatar jnschaeffer avatar jriddle-linode avatar jskobos avatar lbgarber avatar lgarber-akamai avatar luthermonson avatar phillc avatar sgmac avatar stvnjacobs avatar tjhop avatar yec-akamai avatar ykim-1 avatar zliang-akamai avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

linodego's Issues

[Bug]: unable to empty ip_ranges for VPC interface once set

go Version

go version go1.21.7 linux/amd64

API Wrapper Version

github.com/linode/linodego v1.29.0

Code Snippet

package main

import (
	"context"
	"fmt"

	"github.com/linode/linodego"
	"golang.org/x/oauth2"

	"log"
	"net/http"
	"os"
)

func main() {
	apiKey, ok := os.LookupEnv("LINODE_TOKEN")
	if !ok {
		log.Fatal("Could not find LINODE_TOKEN, please assert it is set.")
	}
	tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: apiKey})

	oauth2Client := &http.Client{
		Transport: &oauth2.Transport{
			Source: tokenSource,
		},
	}

	linodeClient := linodego.NewClient(oauth2Client)
	linodeClient.SetDebug(true)

	// Get instance configs
	instance, _ := linodeClient.GetInstance(context.Background(), 55519453)
	configs, err := linodeClient.ListInstanceConfigs(context.Background(), instance.ID, &linodego.ListOptions{})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%v", configs[0].Interfaces)

	// Try clearing ip_ranges
	interfaceUpdateOptions := linodego.InstanceConfigInterfaceUpdateOptions{
		IPRanges: []string{},
	}

	// Set empty ip_ranges for VPC interface
	for _, iface := range configs[0].Interfaces {
		if iface.VPCID != nil && iface.IPv4.VPC != "" {
			resp, err := linodeClient.UpdateInstanceConfigInterface(context.Background(), instance.ID, configs[0].ID, iface.ID, interfaceUpdateOptions)
			if err != nil {
				log.Fatal(err)
			}
			fmt.Printf("Deleted routes. New output: %v", resp)
			if len(resp.IPRanges) > 0 {
				log.Printf("\nFailed to empty ip_ranges")
			}
		}
	}
}

Expected Behavior

ip_ranges should be set to empty:

{
   "ip_ranges": []
}

Actual Behavior

ip_ranges of interface connected to VPC never gets cleared.

Output sample:

Initial instance state:

==============================================================================
2024/03/02 05:23:05.324915 DEBUG RESTY 
==============================================================================
~~~ REQUEST ~~~
GET  /v4/linode/instances/55519453/configs  HTTP/1.1
HOST   : api.linode.com
HEADERS:
        Accept: application/json
        Content-Type: application/json
        User-Agent: linodego/v1.29.0 https://github.com/linode/linodego
BODY   :
***** NO CONTENT *****
------------------------------------------------------------------------------
~~~ RESPONSE ~~~
STATUS       : 200 OK
PROTO        : HTTP/1.1
RECEIVED AT  : 2024-03-02T05:23:05.324753224Z
TIME DURATION: 126.797914ms
BODY         :
{
   "data": [
      {
         "id": 58641324,
         "label": "My Ubuntu 22.04 LTS Disk Profile",
         "helpers": {
            "updatedb_disabled": true,
            "distro": true,
            "modules_dep": true,
            "network": true,
            "devtmpfs_automount": true
         },
         "kernel": "linode/grub2",
         "comments": "",
         "memory_limit": 0,
         "created": "2024-03-01T19:14:27",
         "updated": "2024-03-01T19:14:27",
         "root_device": "/dev/sda",
         "devices": {
            "sda": {
               "disk_id": 109845040,
               "volume_id": null
            },
            "sdb": {
               "disk_id": 109845041,
               "volume_id": null
            },
            "sdc": null,
            "sdd": null,
            "sde": null,
            "sdf": null,
            "sdg": null,
            "sdh": null
         },
         "initrd": null,
         "run_level": "default",
         "virt_mode": "paravirt",
         "interfaces": [
            {
               "id": 1137088,
               "purpose": "public",
               "primary": true,
               "active": true,
               "ipam_address": null,
               "label": null,
               "vpc_id": null,
               "subnet_id": null,
               "ipv4": null,
               "ipv6": null,
               "ip_ranges": null
            },
            {
               "id": 1137089,
               "purpose": "vpc",
               "primary": false,
               "active": true,
               "ipam_address": null,
               "label": null,
               "vpc_id": 26186,
               "subnet_id": 26443,
               "ipv4": {
                  "vpc": "10.0.0.5",
                  "nat_1_1": null
               },
               "ipv6": null,
               "ip_ranges": [
                  "10.192.3.0/24"
               ]
            }
         ]
      }
   ],
   "page": 1,
   "pages": 1,
   "results": 1
}

Logs of request sent to set ip_ranges to empty:

==============================================================================
~~~ REQUEST ~~~
PUT  /v4/linode/instances/55519453/configs/58641324/interfaces/1137089  HTTP/1.1
HOST   : api.linode.com
HEADERS:
        Accept: application/json
        Content-Type: application/json
        User-Agent: linodego/v1.29.0 https://github.com/linode/linodego
BODY   :
{}
------------------------------------------------------------------------------
~~~ RESPONSE ~~~
STATUS       : 200 OK
PROTO        : HTTP/1.1
RECEIVED AT  : 2024-03-02T05:23:05.495718273Z
TIME DURATION: 170.160593ms
BODY         :
{
   "id": 1137089,
   "purpose": "vpc",
   "primary": false,
   "active": true,
   "ipam_address": null,
   "label": null,
   "vpc_id": 26186,
   "subnet_id": 26443,
   "ipv4": {
      "vpc": "10.0.0.5",
      "nat_1_1": null
   },
   "ipv6": null,
   "ip_ranges": [
      "10.192.3.0/24"
   ]
}

Ideally, in the end when we call linodeClient.UpdateInstanceConfigInterface() with empty ip_ranges, it should have set ip_ranges to empty and response should not contain ip_ranges still set.

I can delete an item from ip_ranges if there are more than 1 entries, its just that I am unable to set it to empty.

Steps to Reproduce

  1. Bring up a linode in VPC.
  2. Try setting ip_ranges for the VPC interface.
  3. Once set, try clearing the ip_ranges.

If one has a linode in VPC, they can use the above script to reproduce the issue. Just change the instance id with your instance id and set atleast one ip subnet in ip_ranges.

[Bug]: `updateHostURL()` forces HTTPS for connections, affecting `SetBaseURL()`, `SetAPIVersion()`, etc

go Version

go version go1.17.2 darwin/amd64

API Wrapper Version

github.com/linode/linodego v1.2.0

Code Snippet

client := linodego.NewClient(nil)
client.SetBaseURL("http://api.notprod.linode.com") // this method calls c.updateHostURL(), which forcibly sets HTTPS on the URL

Expected Behavior

Base URL should be set to http://api.notprod.linode.com

Actual Behavior

Base URL is set to https://api.notprod.linode.com

Steps to Reproduce

  • Create clientt
  • Do anything that calls updateHostURL()
  • updateHostURL() calls the resty SetBaseURL on our behalf, forcing https

Action functions that return `bool, error` should just return `error`

There are no cases where the request to perform some action in the Linode API should return false, nil on a successful API request.

Code using the current convention tends to look like:

if ok, err := client.DoThing(); err != nil {
  return fmt.Errorf("Error doing thing: %s", err)
} else ! ok {
  return fmt.Errorf("Error doing thing: thing didn't happen")
}

For example, RebootInstance has no meaningful false state.

Convert all of these endpoints to simply return error. If there is a case where false makes sense, consider if it should just be denoted with:

  • a code comment
    // BootInstance will not return an error if the Instance is already booted
    I'm not sure about that, just an example
  • or with a simulated error
    return fmt.Errorf("Instance was already booted")

Another option may be to use a linodego.Error, introducing a Code (<100) to indicate that there was no API error but the request was not performed for a predictable reason as returned by the API (usually in the case of a a non HTTP 200, non HTTP 4xx response).

settleBoolResponseOrError can likely be removed as a result of this refactor.

git grep 'bool, error' | sed -E -e 's/func \(c \*Client\)//' -e 's/\(.*//':

  • instance_snapshots.go: EnableInstanceBackups
  • instance_snapshots.go: CancelInstanceBackups
  • instance_snapshots.go: RestoreInstanceBackup
  • instances.go: BootInstance
  • instances.go: RebootInstance
  • instances.go: MutateInstance
  • instances.go: RescueInstance
  • instances.go: ResizeInstance
  • instances.go: ShutdownInstance
  • volumes.go: DetachVolume
  • volumes.go: ResizeVolume

This should be denoted as a "BREAKING CHANGE" in the git commit.

[Feature]: Helper functions for working with errors

Description

While working with the Linode API, I found myself repeating a pattern for checking which specific error was returned from a failed API request.

The pattern is roughly the following:

  • Call a method on linodego.Client;
  • Unwrap the error to a *linodego.Error with errors.As;
  • If the unwrapping is successful, and has an expected error code, handle it.
client := linodego.NewClient(http.DefaultClient)

var lerr *linodego.Error
if err := client.DeleteObjectStorageKey(context.Background(), 12345); errors.As(err, &lerr) && lerr.StatusCode() == http.StatusNotFound {
    // Handle the expected error.
} else if err != nil {
    // I have an unexpected error, this is usually a simple...
    return fmt.Errorf("delete object storage key: %w", err)
}

Performing a simple if err != nil does not work in all situations, since I may want to "successfully fail" past (in this case) the DeleteObjectStorageKey request returning a 404 Not Found response.

Proposal

Addition of two, new functions to the module:

  • ErrHasStatus(err error, codes ...int) bool — uses errors.As to try and unwrap err to a *linodego.Error, before checking to see if linodego.Error.StatusCode matches any of the provided codes.
  • IsNotFound(err error) bool — a thin wrapper over ErrHasStatusCodes(err, http.StatusNotFound) for checking if the error returned from an API call is a 404 Not Found, but providing a convenience for (what in my experience has been) a common use-case. However, if this isn't considered a valuable or useful addition, that's totally okay. 😄

Example Code

IsNotFound

client := linodego.NewClient(http.DefaultClient)
if err := client.DeleteObjectStorageKey(context.Background(), 12345); linodego.IsNotFound(err) {
    // I am expecting this, but I have more stuff to do further down.
} else if err != nil {
    // handle the unexpected error accordingly
}

ErrHasStatus

client := linodego.NewClient(http.DefaultClient)
instance, err := client.GetInstance(context.Background(), 123456)
if linodego.ErrHasStatus(err, http.StatusUnauthorized) {
    // oops, forgot my wallet at home
} else if linodego.ErrHasStatus(err, http.StatusTooManyRequests) {
    // cool my jets
} else if err != nil {
    // welp, didn't expect this...
}

Remove dependency on k8s.io/client-go

General:

  • Have you removed all sensitive information, including but not limited to access keys and passwords?
  • Have you checked to ensure there aren't other open or closed Pull Requests for the same bug/feature/question?

We would like to see linodego remove its dependency on k8s.io/client-go package here: https://github.com/linode/linodego/tree/master/internal/kubernetes

This causes problem as users may depend on a different version of k8s.io/client-go. And k8s.io/client-go breaks its compatibility every 3 months.

Proposed Solution:
Please expose that package as its own Go module. You can do that keeping that package in this git repo or in separate repos.

[Feature]: Retry on HTTP2 GOAWAY frames

Description

Occasionally, we'll see errors like this when using Linode-based service discovery in Prometheus:

ts=2022-12-01T15:21:15.357Z caller=refresh.go:99 level=error component="discovery manager scrape" discovery=linode msg="Unable to refresh target groups" err="[002] http2: server sent GOAWAY and closed the connection; LastStreamID=1999, ErrCode=NO_ERR
Dec 01 10:21:15 prometheus2 prometheus[2268065]: 2022/12/01 10:21:15.357289 ERROR RESTY http2: server sent GOAWAY and closed the connection; LastStreamID=1999, ErrCode=NO_ERROR, debug="", Attempt 1

What happens is that the connection to api.linode.com gets reset because we hit the maximum number of requests for a persistent connection (e.g. nginx defaults to 1000) and the connection is closed by the server after it sends a GOAWAY. We should be able to safely retry these by opening a new connection.

There's likely more nuance to it, though. As I believe you need to validate the accompanying error with the GOAWAY frame and check on the last stream ID rather than just naively retrying every time.

Some helpful resources I've found are:

Example Code

// retries.go
func requestGOAWAYRetryCondition(_ *resty.Response, e error) bool {
	return errors.As(e, &http2.GoAwayError{})
}

go modules: retract v1.0.0

General:

  • Have you removed all sensitive information, including but not limited to access keys and passwords?
  • Have you checked to ensure there aren't other open or closed Pull Requests for the same bug/feature/question?

Bug Reporting

When you will update your minimal version to go 1.16

go 1.15

Could you add the following retract directives:

retract (
    v1.0.0 // add explanation
)

Thank you.

consider putting resources in their own packages

An API consumer may not want to import eveything linodego offers if they are only concerned with DNS.

Consider how each of these CRUD resources could implement a common interface and register themselves with the client.

The base client could implement the current interface of providing all the CRUD functions for every request, but consumers of the API wouldn't be forced to use that client (does this mean moving the client into a separate package too?)

[Feature]: add /v4/vpcs/:id/ips endpoints

Description

Recently, new endpoints (/v4/vpcs/:id/ips) are added to linode API. We would like these to be added to linodego as well. I do see /vpc/ips, but its missing /vpc/%s/ips for targetting a particular VPC.

curl --location 'https://api.linode.com/v4/vpcs/41220/ips' \
--header 'Authorization: Bearer $TOKEN'

{
    "data": [
        {
            "address": "10.0.0.2",
            "address_range": null,
            "vpc_id": 41220,
            "subnet_id": 41184,
            "region": "us-ord",
            "linode_id": 56323907,
            "config_id": 59467054,
            "interface_id": 1248353,
            "active": true,
            "nat_1_1": null,
            "gateway": "10.0.0.1",
            "prefix": 8,
            "subnet_mask": "255.0.0.0"
        },
        {
            "address": "10.0.0.3",
            "address_range": null,
            "vpc_id": 41220,
            "subnet_id": 41184,
            "region": "us-ord",
            "linode_id": 56323949,
            "config_id": 59467106,
            "interface_id": 1248358,
            "active": true,
            "nat_1_1": null,
            "gateway": "10.0.0.1",
            "prefix": 8,
            "subnet_mask": "255.0.0.0"
        },
        ...
}

We would like to have the ability to call individual VPCs and get all ips in them in one api call.

Example Code

No response

Add a generic `WaitFor` that can handle status or event based waiting

Add a generic WaitFor that can handle status or event based waiting. This should allow for concurrency when performing actions on different Linodes, as the Linode API only blocks against actions on the same Linode.

At the very least, this issue could be addressed with Linode, Image, Snapshot, and BlockStorage status polling helpers.

Also add README and godoc instructions on how to use this (and the WaitForEventFinished) feature.

Consider refactoring WaitForEventFinished "seen" filter optimization

General:

When using the Linode Terraform Provider, I would intermittently get boot and disk errors while specifying an explicit disk configuration for my instance.

On the Terraform side, this manifests in errors like:
Error booting Linode instance xxxxxxxx: [002] Get "https://api.linode.com/v4/account/events?page=1": context deadline exceeded
Error waiting for Linode instance xxxxxxxx disk: [002] Get "https://api.linode.com/v4/account/events?page=1": context deadline exceeded

Digging through this code a bit I came across the comment added in Pull Request #76

		// With potentially 1000+ events coming back, we should filter on something
		// Warning: This optimization has the potential to break if users are clearing
		// events before we see them.
		"seen": false,

While Terraform was running, I was actively working in the Linode Manager and clicking on the notification bell icon to follow along with the progress. After finding that comment in the code, I finally realized that reading the notification events was causing the errors. If I run the Terraform without clicking on the notification bell icon, the instance will then be successfully created.

I am not sure what possible options there might be for refactoring the "seen" filter optimization, but I wanted to at least open an issue incase others run in to the same errors I was seeing.

Expected Behavior:

The Linode Terraform Provider (linodego) can be used while also using the Linode Manager and viewing notification events (the bell icon).

Actual Behavior:

Viewing notification events in the Linode Manager (the bell icon) can cause the Linode Terraform Provider (linodego) to error while waiting for events.

Steps to Reproduce the Problem:

  1. Start an API call that utilizes WaitForEventFinished
  2. View notification events in the Linode Manager (the bell icon)
  3. API call will timeout

Environment Specifications

macOS: 10.14.6
Terraform: 0.13.0
Linode Terraform Provider: 1.12.4

Implement domain and domain record create/update/delete

  • Copy Create/Update/Delete methods from the template.go to domains.go and domain_records.go
  • replace "Template" with "Domain" or "DomainRecord", (likewise for lowercase) (apply as though #28 and #29 were completed)
    consult nodebalancers.go and nodebalancer_configs.go for help
  • Add domains_test.go and domain_records_test.go (see volumes_test.go as an example)
  • run make fixtures ARGS="-run TestUpdateDomain" etc to create fixtures for each public function added (adding tests for GetDomain, ListDomains would be nice) (avoid committing person domain names or other personal Linode resource details)
  • confirm make test passes

StackScriptData should be map[string]interface{}

Valid StackScript UDF interpreted by the API may contain JSON integer and boolean values. These values would currently be mapped to empty strings instead.

Since StackScriptData is map[string]string, a potential work-around would be to use string equivalent values where integers and booleans are expected: "123", "on", "yes", "true", etc.

Not possible to disable AllowAutoDiskResize in type InstanceResizeOptions struct

In instances.go, line 154-170

// InstanceResizeOptions
type InstanceResizeOptions struct {
	Type string `json:"type"`

	// When enabled, an instance resize will also resize a data disk if the instance has no more than one data disk and one swap disk
	AllowAutoDiskResize bool `json:"allow_auto_disk_resize,omitempty"`
}

Since AllowAutoDiskResize is a bool, assigning this a false value will result in the field registering as empty, and then being omitted. Since the API defaults to allowing automatic disk resizing, this makes it impossible to disable automatic disk resizing.

@jnschaeffer

Diff coverage should be a PR requirement

In #89 (comment), there are currently no tests being added for new code. The overall project coverage drops by 1.5% but the diff (new code) includes 0% test coverage.

All PRs should require some level of test coverage. Project maintainers can always override these limitations and force a merge.

[Bug]: SetAPIVersion does not respect custom BaseURLs

go Version

go version go1.17.2 darwin/amd64

API Wrapper Version

github.com/linode/linodego v1.1.0

Code Snippet

client := linodego.NewClient(nil)
client.SetBaseURL("api.notprod.linode.com")
client.SetAPIVersion("v4beta") // this method calls c.resty.SetBaseURL, overwriting the baseURL

Expected Behavior

To create a new client and have it point to https://api.notprod.linode.com/v4beta.

Actual Behavior

It points to https://api.linode.com/v4beta

Steps to Reproduce

  • Create a client
  • Set the desired base URL first
  • Set the desired API version
  • The base URL set will no longer be respected

Remove JSON Tags for types

The JSON tags matter for create and update options, but do not matter for any of the types representing the resources.

Properly Handle 429 Reponse

Thanks for the library!

An issue I found when using the new Terraform provider for linode (which leverages this package) is that I hit API limits pretty quickly. The initial suggestion from your support team when I asked for an API limit increase was to decrease parallelism. As a short-term, that is fine, and I can target resources individually. However, I would love to see Linode-Go handle this issue directly by implementing retry / backup logic in the request workflow.

A quick walk through of the code (I am not a Go developer, so take this with a grain of salt) makes it look like you could use the Retry logic in Go-Resty in a straight forward way. See here: https://github.com/go-resty/resty#retries

Reconsider use of slices of pointers

These structures don't benefit from being slices of pointers. The ListThing endpoints were converted to slices of structs a few months ago.

  • Consistency with the other endpoints
  • Avoids inadvertent mutations
  • Easier to work with?
$ git grep '\[\]\*'
instance_ips.go:        Public  []*InstanceIP `json:"public"`
instance_ips.go:        Private []*InstanceIP `json:"private"`
instance_ips.go:        Shared  []*InstanceIP `json:"shared"`
instance_ips.go:        Global    []*IPv6Range `json:"global"`
instance_snapshots.go:  Automatic []*InstanceSnapshot             `json:"automatic"`
instance_snapshots.go:  Disks    []*InstanceSnapshotDisk `json:"disks"`
instances.go:   IPv4            []*net.IP       `json:"ipv4"`
nodebalancer.go:        Configs            []*NodeBalancerConfigCreateOptions `json:"configs,omitempty"`

Adding labels to LKE node pools

I know that has nothing to do with lindoego directly but I hope you forward this to whoever is in charge on this issue, LKE currently does not support adding kubernetes labels to node pools. This very little feature is basically one of the very main reasons of having separate node pools in the first place. AFAIK almost all managed k8s solutions support adding kubernetes labels to node pools/groups using the cloud vendor APIs.

panics occurring when API is down

Hi.

We had some panics using this package coinciding with the recent Linode API service issues.
https://status.linode.com/incidents/ych41x06fm8k

Jun 18 01:10:40 023c8ee9f52b runtime.log: panic: runtime error: invalid memory address or nil pointer dereference
Jun 18 01:10:40 023c8ee9f52b runtime.log: [signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x865f9a]
Jun 18 01:10:40 023c8ee9f52b runtime.log: goroutine 43 [running]:
Jun 18 01:10:40 023c8ee9f52b runtime.log: github.com/linode/linodego.(*Client).listHelper(0xc000338000, 0xbcb4e0, 0xc000024090, 0xa26300, 0xc00049da40, 0xc00065d660, 0x9e40e0, 0xc000211301)
Jun 18 01:10:40 023c8ee9f52b runtime.log:     /app/src/github.com/linode/linodego/pagination.go:91 +0x5fa
Jun 18 01:10:40 023c8ee9f52b runtime.log: github.com/linode/linodego.(*Client).ListRegions(0xc000338000, 0xbcb4e0, 0xc000024090, 0xc00065d660, 0xc0004dd5a8, 0xfff2a8, 0xc0002c0900, 0x0, 0x0)
Jun 18 01:10:40 023c8ee9f52b runtime.log:     /app/src/github.com/linode/linodego/regions.go:37 +0x78
Jun 18 01:10:40 023c8ee9f52b runtime.log: main.(*LinodeHost).getRegions(0xc0002ac390, 0xc000397500, 0x9, 0x11, 0x0, 0x0)
Jun 18 00:38:57 023c8ee9f52b runtime.log: panic: runtime error: invalid memory address or nil pointer dereference
Jun 18 00:38:57 023c8ee9f52b runtime.log: [signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x8699f2]
Jun 18 00:38:57 023c8ee9f52b runtime.log: goroutine 101 [running]:
Jun 18 00:38:57 023c8ee9f52b runtime.log: github.com/linode/linodego.(*Client).listHelper(0xc0003b4000, 0xbcb4c0, 0xc000024090, 0xa25b00, 0xc000530f60, 0xc0005611d8, 0x48ae19, 0xc0005610d8)
Jun 18 00:38:57 023c8ee9f52b runtime.log:     /app/src/github.com/linode/linodego/pagination.go:73 +0x4022
Jun 18 00:38:57 023c8ee9f52b runtime.log: github.com/linode/linodego.(*Client).ListImages(0xc0003b4000, 0xbcb4c0, 0xc000024090, 0xc0005611d8, 0x9e0b20, 0xc000074958, 0x182, 0x9e0b20, 0x10001e0)
Jun 18 00:38:57 023c8ee9f52b runtime.log:     /app/src/github.com/linode/linodego/images.go:80 +0x81

I believe this to be due to the API outages that occurred.

A key point to note is that we're using package version v0.7.1

Thanks

Rename repo

Per conversations with @displague we are going to rename the project to linodego

This change will require updating package refs.

linodego cannot be used with k8s go client v0.18 projects

Simply linodego cannot be used with the latest k8s go client v0.18 since they added ctx argument to everything. The command go get github.com/linode/linodego fails and this is what I get

# github.com/linode/linodego/pkg/condition
../../../go/pkg/mod/github.com/linode/[email protected]/pkg/condition/lke.go:20:47: not enough arguments in call to clientset.CoreV1().Nodes().List
        have ("k8s.io/apimachinery/pkg/apis/meta/v1".ListOptions)
        want (context.Context, "k8s.io/apimachinery/pkg/apis/meta/v1".ListOptions)

I think it's just one incidence in your code that should be updated so it's not a lot of work to do like many other projects. It should not take a couple of minutes.

P.S. Golang's dependency management, just like most aspects of Golang, is simply a joke. Kim Kardashian can come up with a better alternative.

examples are not rendering in go doc

Example() renders in the docs, but the per-function examples do not.

go vet

# github.com/chiefy/linodego_test
./account_test.go:11: ExampleGetAccount refers to unknown identifier: GetAccount
./example_nodebalancers_test.go:11: ExampleCreateNodeBalancer refers to unknown identifier: CreateNodeBalancer
./example_nodebalancers_test.go:54: ExampleCreateNodeBalancerConfig refers to unknown identifier: CreateNodeBalancerConfig
./example_nodebalancers_test.go:129: ExampleCreateNodeBalancerNode refers to unknown identifier: CreateNodeBalancerNode
./example_stackscripts_test.go:13: ExampleCreateStackscript refers to unknown identifier: CreateStackscript
./example_test.go:19: ExampleListTypes_all refers to unknown identifier: ListTypes
./example_test.go:40: ExampleGetType_missing refers to unknown identifier: GetType
./example_test.go:62: ExampleListKernels_all refers to unknown identifier: ListKernels
./example_test.go:79: ExampleListKernels_allWithOpts refers to unknown identifier: ListKernels
./example_test.go:102: ExampleListKernels_filtered refers to unknown identifier: ListKernels
./example_test.go:121: ExampleListKernels_page1 refers to unknown identifier: ListKernels
./example_test.go:145: ExampleGetKernel_specific refers to unknown identifier: GetKernel
./example_test.go:172: ExampleGetImage_missing refers to unknown identifier: GetImage
./example_test.go:191: ExampleListImages_all refers to unknown identifier: ListImages
./example_test.go:212: ExampleListImages_notfound refers to unknown identifier: ListImages
./example_test.go:230: ExampleListImages_badfilter refers to unknown identifier: ListImages
./example_test.go:248: ExampleListLongviewSubscriptions_page1 refers to unknown identifier: ListLongviewSubscriptions
./example_test.go:264: ExampleListStackscripts_page1 refers to unknown identifier: ListStackscripts

can not add a tag to a domain record

General:

  • Have you removed all sensitive information, including but not limited to access keys and passwords?
  • Have you checked to ensure there aren't other open or closed Pull Requests for the same bug/feature/question?

Feature Requests:

  • Have you explained your rationale for why this feature is needed?
  • Have you offered a proposed implementation/solution?

Bug Reporting

CreateDomainRecord() fails if the Tag field in DomainRecordCreateOptions is set

Expected Behavior

The record is added to the domain and the tag is set on that record

Actual Behavior

CreateDomainRecord returns an error 400 [tag] tag is not valid.

Steps to Reproduce the Problem

			// create the record
			recCreateOpts := linodego.DomainRecordCreateOptions{
				Type: linodego.RecordTypeA,
				Name: "www",
				// TTLSec: 60,
				Target: "127.0.0.1",
			}
			recCreateOpts.Tag = strCopy("eezhee") // can't assign a constant
			newRecord, err := m.api.CreateDomainRecord(context.Background(), domainDetails.ID, recCreateOpts)
			if err != nil {
				return vmInfo, err
			}

Environment Specifications

MacOS 11. Golang 1.15

Screenshots, Code Blocks, and Logs

Additional Notes

  • If you comment out the line that assigns a value to Tag, the call to CreateDomainRecord works
  • if I debug into the linodego code, this is what the API request sends to the server "{"type":"A","name":"www","target":"127.0.0.1","tag":"eezhee"}". Note, the tag eezhee was created with CreateTag earlier so it exists on the server.

The Linode Community is a great place to get additional support.

Implement Tag field in NodeBalancer, Domains, and Volumes

Implement Tag changes introduced in API 4.0.10, 4.0.9, and 4.0.8:

  • The NodeBalancer object now includes “tags”, an array of strings (GET, PUT)

  • POST /nodebalancers now accepts “tags”, an array of strings

  • The Domain object now includes “tags”, an array of strings (GET, PUT)

  • POST /domains now accepts “tags”, an array of strings

  • The Volume object now includes “tags”, an array of strings (GET, PUT)

  • POST /volumes now accepts “tags”, an array of strings

  • GET /tags/:tag now returns tagged NodeBalancer, Domains, and Volumes in addition to Linodes

  • POST /tags now accepts NodeBalancers, Domains, and Volumes

[Feature]: return VPC ips as well in InstanceIPv4Response

Description

Hi, currently one can list ip's configured on a linode using GetInstanceIPAddresses . However, this only returns public, private, shared and reserved. https://github.com/linode/linodego/blob/v1.30.0/instance_ips.go#L17-L22. To get VPC specific ip-addresses, one has to send another request to get instance configs and then parse through them. It would be nice to get vpc addresses in response of GetInstanceIPAddresses only.

linode-cli and linode API does provide this:

➜  ~ linode-cli linodes ips-list 56229162 --json | jq .
[
  {
    "ipv4": {
      "public": [
        {
          "address": "172.234.213.108",
          "gateway": "172.234.213.1",
          "subnet_mask": "255.255.255.0",
          "prefix": 24,
          "type": "ipv4",
          "public": true,
          "rdns": "172-234-213-108.ip.linodeusercontent.com",
          "linode_id": 56229162,
          "region": "us-ord",
          "vpc_nat_1_1": null
        }
      ],
      "private": [
        {
          "address": "192.168.138.178",
          "gateway": null,
          "subnet_mask": "255.255.128.0",
          "prefix": 17,
          "type": "ipv4",
          "public": false,
          "rdns": null,
          "linode_id": 56229162,
          "region": "us-ord"
        }
      ],
      "shared": [],
      "reserved": [],
      "vpc": [
        {
          "address": "10.0.0.6",
          "vpc_id": 47539,
          "subnet_id": 47373,
          "region": "us-ord",
          "linode_id": 56229162,
          "gateway": "10.0.0.1",
          "prefix": 8,
          "subnet_mask": "255.0.0.0"
        },
        {
          "address": null,
          "vpc_id": 47539,
          "subnet_id": 47373,
          "region": "us-ord",
          "linode_id": 56229162,
          "gateway": "10.0.0.1",
          "prefix": 8,
          "subnet_mask": "255.0.0.0"
        }
      ]
    },
    "ipv6": {...}
]

Can we have something like:

// InstanceIPv4Response contains the details of all IPv4 addresses associated with an Instance
type InstanceIPv4Response struct {
	Public   []*InstanceIP `json:"public"`
	Private  []*InstanceIP `json:"private"`
	Shared   []*InstanceIP `json:"shared"`
	Reserved []*InstanceIP `json:"reserved"`
        VPC      []*InstanceIP `json:"vpc"`
}

Example Code

No response

template.go needs to sync up with other changes

  • update template.go to include Create/Update options

  • update template.go to include instructions
    For file:

    replace "Template" with "Domain" or "DomainRecord", (likewise for lowercase) ...

    For types:

    use pointers where ever null'able fields whose 0 or "" values would have a different meaning to the API

  • update template.go to include UpdateOptions and CreateOptions funcs

  • mention that client.go and resources.go need to new items for each added Thing

  • template.go should have commented out examples of the code sub resources would use

Go 1.11 modules: go-resty/resty import path

As per go-resty/resty#178, the import path should be import "gopkg.in/resty.v1" instead of github.com/go-resty/resty which is being used right now. It causes problem with Go 1.11 modules system with following error:

go: github.com/go-resty/[email protected]: parsing go.mod: unexpected module path "gopkg.in/resty.v1"
go: error loading module requirements

It would be great if this is fixed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.