Giter Site home page Giter Site logo

go-ovh's Introduction

go-ovh

Lightweight Go wrapper around OVHcloud's APIs. Handles all the hard work including credential creation and requests signing.

GoDoc Build Status Coverage Status Go Report Card

package main

import (
	"fmt"
	"github.com/ovh/go-ovh/ovh"
)

// PartialMe holds the first name of the currently logged-in user.
// Visit https://api.ovh.com/console/#/me#GET for the full definition
type PartialMe struct {
	Firstname string `json:"firstname"`
}

// Instantiate an OVH client and get the firstname of the currently logged-in user.
// Visit https://api.ovh.com/createToken/index.cgi?GET=/me to get your credentials.
func main() {
	var me PartialMe

	client, _ := ovh.NewClient(
		"ovh-eu",
		YOUR_APPLICATION_KEY,
		YOUR_APPLICATION_SECRET,
		YOUR_CONSUMER_KEY,
	)
	client.Get("/me", &me)
	fmt.Printf("Welcome %s!\n", me.Firstname)
}

Installation

The Golang wrapper has been tested with Golang 1.18+. It may worker with older versions although it has not been tested.

To use it, just include it to your import and run go get:

import (
	...
	"github.com/ovh/go-ovh/ovh"
)

Configuration

The straightforward way to use OVHcloud's API keys is to embed them directly in the application code. While this is very convenient, it lacks of elegance and flexibility.

Alternatively it is suggested to use configuration files or environment variables so that the same code may run seamlessly in multiple environments. Production and development for instance.

This wrapper will first look for direct instanciation parameters then OVH_ENDPOINT, OVH_APPLICATION_KEY, OVH_APPLICATION_SECRET and OVH_CONSUMER_KEY environment variables. If either of these parameter is not provided, it will look for a configuration file of the form:

[default]
; general configuration: default endpoint
endpoint=ovh-eu

[ovh-eu]
; configuration specific to 'ovh-eu' endpoint
application_key=my_app_key
application_secret=my_application_secret
consumer_key=my_consumer_key

Depending on the API you want to use, you may set the endpoint to:

  • ovh-eu for OVHcloud Europe API
  • ovh-us for OVHcloud US API
  • ovh-ca for OVHcloud Canada API
  • soyoustart-eu for So you Start Europe API
  • soyoustart-ca for So you Start Canada API
  • kimsufi-eu for Kimsufi Europe API
  • kimsufi-ca for Kimsufi Canada API
  • Or any arbitrary URL to use in a test for example

The client will successively attempt to locate this configuration file in

  1. Current working directory: ./ovh.conf
  2. Current user's home directory ~/.ovh.conf
  3. System wide configuration /etc/ovh.conf

This lookup mechanism makes it easy to overload credentials for a specific project or user.

Register your app

OVHcloud's API, like most modern APIs is designed to authenticate both an application and a user, without requiring the user to provide a password. Your application will be identified by its "application secret" and "application key" tokens.

Hence, to use the API, you must first register your application and then ask your user to authenticate on a specific URL. Once authenticated, you'll have a valid "consumer key" which will grant your application on specific APIs.

The user may choose the validity period of his authorization. The default period is 24h. He may also revoke an authorization at any time. Hence, your application should be prepared to receive 403 HTTP errors and prompt the user to re-authenticate.

This process is detailed in the following section. Alternatively, you may only need to build an application for a single user. In this case you may generate all credentials at once. See below.

Use the API on behalf of a user

Visit https://eu.api.ovh.com/createApp and create your app You'll get an application key and an application secret. To use the API you'll need a consumer key.

The consumer key has two types of restriction:

  • path: eg. only the GET method on /me
  • time: eg. expire in 1 day

Then, get a consumer key. Here's an example on how to generate one.

First, create a 'ovh.conf' file in the current directory with the application key and application secret. You can add the consumer key once generated. For alternate configuration method, please see the configuration section.

[ovh-eu]
application_key=my_app_key
application_secret=my_application_secret
; consumer_key=my_consumer_key

Then, you may use a program like this example to create a consumer key for the application:

package main

import (
	"fmt"

	"github.com/ovh/go-ovh/ovh"
)

func main() {
	// Create a client using credentials from config files or environment variables
	client, err := ovh.NewEndpointClient("ovh-eu")
	if err != nil {
		fmt.Printf("Error: %q\n", err)
		return
	}
	ckReq := client.NewCkRequest()

	// Allow GET method on /me
	ckReq.AddRules(ovh.ReadOnly, "/me")

	// Allow GET method on /xdsl and all its sub routes
	ckReq.AddRecursiveRules(ovh.ReadOnly, "/xdsl")

	// Run the request
	response, err := ckReq.Do()
	if err != nil {
		fmt.Printf("Error: %q\n", err)
		return
	}

	// Print the validation URL and the Consumer key
	fmt.Printf("Generated consumer key: %s\n", response.ConsumerKey)
	fmt.Printf("Please visit %s to validate it\n", response.ValidationURL)
}

Use the API for a single user

Alternatively, you may generate all creadentials at once, including the consumer key. You will typically want to do this when writing automation scripts for a single projects.

If this case, you may want to directly go to https://eu.api.ovh.com/createToken/ to generate the 3 tokens at once. Make sure to save them in one of the 'ovh.conf' configuration file. Please see the configuration section.

ovh.conf should look like:

[ovh-eu]
application_key=my_app_key
application_secret=my_application_secret
consumer_key=my_consumer_key

Use the lib

These examples assume valid credentials are available in the configuration.

GET

package main

import (
	"fmt"

	"github.com/ovh/go-ovh/ovh"
)

func main() {
	client, err := ovh.NewEndpointClient("ovh-eu")
	if err != nil {
		fmt.Printf("Error: %q\n", err)
		return
	}

	// Get all the xdsl services
	xdslServices := []string{}
	if err := client.Get("/xdsl/", &xdslServices); err != nil {
		fmt.Printf("Error: %q\n", err)
		return
	}

	// xdslAccess represents a xdsl access returned by the API
	type xdslAccess struct {
		Name   string `json:"accessName"`
		Status string `json:"status"`
		Pairs  int	`json:"pairsNumber"`
		// Insert the other properties here
	}

	// Get the details of each service
	for i, serviceName := range xdslServices {
		access := xdslAccess{}
		url := "/xdsl/" + serviceName

		if err := client.Get(url, &access); err != nil {
			fmt.Printf("Error: %q\n", err)
			return
		}
		fmt.Printf("#%d : %+v\n", i+1, access)
	}
}

PUT

package main

import (
	"fmt"

	"github.com/ovh/go-ovh/ovh"
)

func main() {
	client, err := ovh.NewEndpointClient("ovh-eu")
	if err != nil {
		fmt.Printf("Error: %q\n", err)
		return
	}

	// Params
	type AccessPutParams struct {
		Description string `json:"description"`
	}

	// Update the description of the service
	params := &AccessPutParams{Description: "My awesome access"}
	if err := client.Put("/xdsl/xdsl-yourservice", params, nil); err != nil {
		fmt.Printf("Error: %q\n", err)
		return
	}

	fmt.Println("Description updated")
}

Use v1 and v2 API versions

When using OVHcloud APIs (not So you Start or Kimsufi ones), you are given the opportunity to aim for two API versions. For the European API, for example:

Calling client.Get, you can target the API version you want:

client, _ := ovh.NewEndpointClient("ovh-eu")

// Call to https://eu.api.ovh.com/v1/xdsl/xdsl-yourservice
client.Get("/v1/xdsl/xdsl-yourservice", nil)

// Call to https://eu.api.ovh.com/v2/xdsl/xdsl-yourservice
client.Get("/v2/xdsl/xdsl-yourservice", nil)

// Legacy call to https://eu.api.ovh.com/1.0/xdsl/xdsl-yourservice
client.Get("/xdsl/xdsl-yourservice", nil)

API Documentation

Create a client

  • Use ovh.NewClient() to have full controll over ther authentication
  • Use ovh.NewEndpointClient() to create a client for a specific API and use credentials from config files or environment
  • Use ovh.NewDefaultClient() to create a client unsing endpoint and credentials from config files or environment

Query

Each HTTP verb has its own Client method. Some API methods supports unauthenticated calls. For these methods, you may want to use the *UnAuth variant of the Client which will bypass request signature.

Each helper accepts a method and resType argument. method is the full URI, including the query string, and resType is a reference to an object in which the json response will be unserialized.

Additionally, Post, Put and their UnAuth variant accept a reqBody which is a reference to a json serializable object or nil.

Alternatively, you may directly use the low level CallAPI method.

  • Use client.Get() for GET requests
  • Use client.Post() for POST requests
  • Use client.Put() for PUT requests
  • Use client.Delete() for DELETE requests

Or, for unauthenticated requests:

  • Use client.GetUnAuth() for GET requests
  • Use client.PostUnAuth() for POST requests
  • Use client.PutUnAuth() for PUT requests
  • Use client.DeleteUnAuth() for DELETE requests

Request consumer keys

Consumer keys may be restricted to a subset of the API. This allows to delegate the API to manage only a specific server or domain name for example. This is called "scoping" a consumer key.

Rules are simple. They combine an HTTP verb (GET, POST, PUT or DELETE) with a pattern. A pattern is a plain API method and may contain the '*' wilcard to match "anything". Just like glob on a Unix machine.

While this is simple and may be managed directly with the API as-is, this can be cumbersome to do and we recommend using the CkRequest helper. It basically manages the list of authorizations for you and the actual request.

example: Grant on all /sms and identity

client, err := ovh.NewEndpointClient("ovh-eu")
if err == nil {
    // Do something
}
req := client.NewCkRequest()
req.AddRules(ovh.ReadOnly, "/me")
req.AddRecursiveRulesRules(ovh.ReadWrite, "/sms")
pendingCk, err := req.Do()

This example will generate a request for:

  • GET /me
  • GET /sms
  • GET /sms/*
  • POST /sms
  • POST /sms/*
  • PUT /sms
  • PUT /sms/*
  • DELETE /sms
  • DELETE /sms/*

Which would be tedious to do by hand...

Create a CkRequest:

req := client.NewCkRequest()

Request access on a specific path and method (advanced):

// Use this method for fine-grain access control. In most case, you'll
// want to use the methods below.
req.AddRule("VERB", "PATTERN")

Request access on specific path:

// This will generate all patterns for GET PATH
req.AddRules(ovh.ReadOnly, "/PATH")

// This will generate all patterns for PATH for all HTTP verbs
req.AddRules(ovh.ReadWrite, "/PATH")

// This will generate all patterns for PATH for all HTTP verbs, except DELETE
req.AddRules(ovh.ReadWriteSafe, "/PATH")

Request access on path and all sub-path:

// This will generate all patterns for GET PATH
req.AddRecursiveRules(ovh.ReadOnly, "/PATH")

// This will generate all patterns for PATH for all HTTP verbs
req.AddRecursiveRules(ovh.ReadWrite, "/PATH")

// This will generate all patterns for PATH for all HTTP verbs, except DELETE
req.AddRecusriveRules(ovh.ReadWriteSafe, "/PATH")

Create key:

pendingCk, err := req.Do()

This will initiate the consumer key validation process and return both a consumer key and a validation URL. The consumer key is automatically added to the client which was used to create the request. It may be used as soon as the user has authenticated the request on the validation URL.

pendingCk contains 3 fields:

  • ValidationURL the URL the user needs to visit to activate the consumer key
  • ConsumerKey the new consumer key. It won't be active until validation
  • State the consumer key state. Always "pendingValidation" at this stage

Hacking

This wrapper uses standard Go tools, so you should feel at home with it. Here is a quick outline of what it may look like.

Get the sources

go get github.com/ovh/go-ovh/ovh
cd $GOPATH/src/github.com/ovh/go-ovh/ovh
go get

You've developed a new cool feature ? Fixed an annoying bug ? We'd be happy to hear from you ! See CONTRIBUTING.md for more informations

Run the tests

Simply run go test. Since we all love quality, please note that we do not accept contributions lowering coverage.

# Run all tests, with coverage
go test -cover

# Validate code quality
golint ./...
go vet ./...

Supported APIs

OVHcloud Europe

OVHcloud US

OVHcloud Canada

So you Start Europe

So you Start Canada

Kimsufi Europe

Kimsufi Canada

License

3-Clause BSD

go-ovh's People

Contributors

akuracy avatar antleblanc avatar deathiop avatar geoffreybauduin avatar gregdel avatar ldez avatar marcaudefroy avatar pablito-perez avatar pouulet avatar rbeuque74 avatar thbkrkr avatar yadutaf 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-ovh's Issues

OVH client expose endpoint

I need to access the endpoint of a created client. Without it I've to pass the endpoint through my functions separately.

This can be done in a backward compatible way by adding a method to get the endpoint.

http.Client.Timeout gets always overriden by the defaultTimeout when using ovh.Client

Hello,

I have a problem with the Timeout field on the ovh.Client struct. While it may be convenient for simple use case, it introduces an unexpected behaviour: it always overrides the http.Client.Timeout, with a default values of 3 minutes (when using the ovh.Client "constructor").

For example, with the following code, despite creating a client with a 30s timeout, the actual timeout will be of 3 minutes.

httpClient := http.Client{Timeout: 30*time.Second}

client, _ := ovh.NewClient(
		"ovh-eu",
		YOUR_APPLICATION_KEY,
		YOUR_APPLICATION_SECRET,
		YOUR_CONSUMER_KEY,
	)

client.Client = http.Client

Now let's say, I want to wrap ovh.Client.

type MyClient struct {
	ovh.Client

	// other fields
}

// Here I only want to expose httpClient because this is a great abstraction and one that is
// conventionally use to allows users of a library to customize everything regarding the HTTP stuff.
func NewClient(httpClient http.Client) *MyClient {
	client := ovh.NewClient(...)
	
	client.Client = httpClient
	
	// This is weird
	if httpClient.Timeout {
		client.Timeout = httpClient.Timeout
	}
	
	// other stuff
	// ...
	
	return client
}

I feel like ovh.Client.Timeout creates more problems that it solves. So in case of a potential v2 (because removing the field would be a breaking chance), I think this field should be removed.

Inconsistency between documentation and code for config discovery

Hello,

I noticed that in a "recent" commit this line changed from /etc/ovh.conf to /etc/ovh.com https://github.com/ovh/go-ovh/blob/master/ovh/configuration.go#L14. Is this on purpose or just a typo when remaking the code around it?

This comes from the following commit: 39b6ccf

The readme.md still mention /etc/ovh.conf https://github.com/ovh/go-ovh/blob/master/README.md?plain=1#L96

I will adapt our code to use the other name for now, but it would be nice to have this clarified/fixed.

Kr,

API error on GET

I've been trying to get the API working for some time now, to use the /ip route in my Go code. While I did manage to get it working for some time, it suddenly started to refuse my requests, responding with a 401: "You must login first".

I don't understand why this is the case, as my API authorization is still perfectly valid, and I have checked that the ovh.Client properly got all the information from the ovh.conf file- So what else is there to do to authentify?

For additionnal information, here is the code I am using to initialize and request on the API:

client, err := ovh.NewEndpointClient("ovh-eu")
if err != nil {
    return err
}
var res = make([]string, 0)
if err = checker.client.Get("/ip", &res); err != nil {
    return err
}
fmt.Println(res)

I did try using Ping(), just in case the problem came from reaching the API, but it didn't return any errors. Is it a problem on my end?
Thank you.

Panic if client creation has failed

Hi,

We add an issue when a client is not create but we still want to try a request.

It goes to panic :
/opt/code/gocode/src/github.com/ovh/go-ovh/ovh/ovh.go:282 +0x180
github.com/ovh/go-ovh/ovh.(*Client).CallAPIWithContext(0x0, 0x169c200, 0xc0000a2040, 0x15bec22, 0x4, 0xc0008c1ea0, 0xa, 0x1578140, 0xc0043ceb80, 0x14fd520, ...)

We patch it with this:

if c == nil {
	return nil, fmt.Errorf("Client is not valid")
}

It allows to return an error without failing.

Looking you're feedback.

in ovh.go
`
// NewRequest returns a new HTTP request
func (c *Client) NewRequest(method, path string, reqBody interface{}, needAuth bool) (*http.Request, error) {
var body []byte
var err error

if reqBody != nil {
	body, err = json.Marshal(reqBody)
	if err != nil {
		return nil, err
	}
}
**if c == nil {
	return nil, fmt.Errorf("Client is not valid")
}**
target := fmt.Sprintf("%s%s", c.endpoint, path)
req, err := http.NewRequest(method, target, bytes.NewReader(body))
if err != nil {
	return nil, err
}

// Inject headers
if body != nil {
	req.Header.Add("Content-Type", "application/json;charset=utf-8")
}
req.Header.Add("X-Ovh-Application", c.AppKey)
req.Header.Add("Accept", "application/json")

// Inject signature. Some methods do not need authentication, especially /time,
// /auth and some /order methods are actually broken if authenticated.
if needAuth {
	timeDelta, err := c.TimeDelta()
	if err != nil {
		return nil, err
	}

	timestamp := getLocalTime().Add(-timeDelta).Unix()

	req.Header.Add("X-Ovh-Timestamp", strconv.FormatInt(timestamp, 10))
	req.Header.Add("X-Ovh-Consumer", c.ConsumerKey)

	h := sha1.New()
	h.Write([]byte(fmt.Sprintf("%s+%s+%s+%s%s+%s+%d",
		c.AppSecret,
		c.ConsumerKey,
		method,
		getEndpointForSignature(c),
		path,
		body,
		timestamp,
	)))
	req.Header.Add("X-Ovh-Signature", fmt.Sprintf("$1$%x", h.Sum(nil)))
}

// Send the request with requested timeout
c.Client.Timeout = c.Timeout

return req, nil

}
`

"accept-encoding: gzip" not accepted ?

I tried the new ovh_cloud_project_kube_nodepool terraform thing, but it didn't work, and looking at the trace logs, the problem seems that go-ovh (or a library it uses) adds a Accept-Encoding: gzip header to the request:

POST /1.0/cloud/project/***/kube/***/nodepool HTTP/1.1
Host: eu.api.ovh.com
User-Agent: Go-http-client/1.1
Content-Length: 125
Accept: application/json
Content-Type: application/json;charset=utf-8
X-Ovh-Application: ***
X-Ovh-Consumer: ***
X-Ovh-Signature: $1$28***
X-Ovh-Timestamp: 1614860696
Accept-Encoding: gzip

{
 "desiredNodes": 2,
 "maxNodes": 0,
 "minNodes": 0,
 "flavorName": "R2-15",
 "name": "my_pool",
 "monthlyBilled": false,
 "antiAffinity": false
}
-----------------------------------------------------

2021/03/04 13:24:57 [TRACE] dag/walk: vertex "provider[\"registry.terraform.io/ovh/ovh\"] (close)" is waiting for "ovh_cloud_project_kube_nodepool.pool-dev"
2021-03-04T13:24:59.479+0100 [DEBUG] plugin.terraform-provider-ovh_v0.11.0: 2021/03/04 13:24:59 [ERROR] OVH API Response error: &errors.errorString{s:"gzip: invalid header"}

I'm not sure I pinpointed the right culprit, though.

Simple POST example

Hello,
I have been trying in vain to use go-ovh to make a new CNAME record via the OVH API

I have taken a simple first example :
POST to : /domain/zone/mydomain.com/refresh

I have tried both :

Client.Post("/domain/zone/mydomain.com/refresh", "", "")

and

Client.Post("/domain/zone/mydomain.com/refresh", "{}", "{}")

But each time I am greeted with : Error 400: "Invalid JSON received"

Not sure if this is a bug or my mis interpritation of how to use the lib.

Could you please point me in the right direction.

Thank you in advance.

user: Current not implemented on linux/amd64 when cross compilation

go-ovh uses user.Current() to get the current user home directory (https://github.com/ovh/go-ovh/blob/master/ovh/configuration.go#L22).

os/user relies on cgo, and cgo is disabled for cross compiling thus it can be an issue to use go-ovh with cross compilation.

Currently I encounter this issue with github.com/terraform-providers/terraform-provider-ovh .

I propose to implement a workaround by reading the environment variable $HOME if an error occurred by calling user.Current().

Failure when a configuration file is not accessible for permission reason

When used in a sandboxed environment, the lack of permission on a configuration file leads to an error.

My use-case is Traefik running as a sandboxed systemd service, using Lego for Lets Encrypt TLS certificates generation.
When using the OVH provider, which uses this module, I have an error regarding permission on /home/traefik/.ovh.conf:

cannot get ACME client ovh: cannot load configuration: open /home/traefik/.ovh.conf: permission denied

The home directory is not accessible (ProtectHome=true), thus the loadConfig method produce an error, even using the ini.LooseLoad method.

This behaviour seems to have been introduced in this commit: 39b6ccfa1451f02114c45cd6abce2d0879a4da4f

Idiomatic GO client

We are interested in using OVH api to create VMs using the GO client. But I saw that OVH's client library does not really wrap all the api methods and objects in an idiomatic GO client. By looking at the json schema docs, it seemed like it should be possible to write an auto generated library. Does OVH has any plans to do that? Is OVH using any api spec language. I checked swagger 2.0. It did not work.

Thanks.

CloudQuery Source Plugin?

Hi Team, hopefully this is right place to ask, if not, I'd appreciate if you can direct me.

I'm the founder of cloudquery.io, a high performance open source ELT framework.

Our users are interested in an OVH plugin, but as we cannot maintain all the plugins ourselves, I was curious if this would be an interesting collaboration, where we would help implement an initial source plugin, and you will help maintain it.

This will give your users the ability to sync OVH APIs (Assets, metadata, config...) to any of their datalakes/data-warehouses/databases easily using any of the growing list of CQ destination plugins.

Best,
Yevgeny

Pull request Travis CI config error

When I submit the Pull Request, Travis CI cannot build because the golint generate a error when run it:

`$ gimme version
v1.3.0
$ go version
go version go1.6 linux/amd64
go.env
$ go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/travis/gopath"
GORACE=""
GOROOT="/home/travis/.gimme/versions/go1.6.linux.amd64"
GOTOOLDIR="/home/travis/.gimme/versions/go1.6.linux.amd64/pkg/tool/linux_amd64"
GO15VENDOREXPERIMENT="1"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"
CXX="g++"
CGO_ENABLED="1"
before_install.1
4.75s$ go get github.com/axw/gocov/gocov
before_install.2
2.38s$ go get github.com/mattn/goveralls
2.34s$ go get github.com/golang/lint/golint

golang.org/x/tools/go/internal/gcimporter

../../../golang.org/x/tools/go/internal/gcimporter/iimport.go:77: undefined: io.SeekCurrent
../../../golang.org/x/tools/go/internal/gcimporter/iimport.go:80: undefined: io.SeekCurrent
../../../golang.org/x/tools/go/internal/gcimporter/iimport.go:156: undefined: io.SeekCurrent
../../../golang.org/x/tools/go/internal/gcimporter/iimport.go:187: r.declReader.Reset undefined (type bytes.Reader has no field or method Reset)
../../../golang.org/x/tools/go/internal/gcimporter/iimport.go:226: r.declReader.Reset undefined (type bytes.Reader has no field or method Reset)
The command "go get github.com/golang/lint/golint" failed and exited with 2 during .
Your build has been stopped.`

I would like make a pull request of a bugfix.

tests fail on s390x

Hello! I'm packaging this library for Fedora, and I noticed that the test suite fails on s390x. I'm not using this architecture myself, but it is one of the architectures that Fedora builds for.

error
+ go test -buildmode pie -compiler gc -ldflags '-extldflags '\''-Wl,-z,relro -Wl,--as-needed  -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld '\'''
--- FAIL: TestAllAPIMethods (4.06s)
    ovh_test.go:248: Request: GET /some/resource. Authenticated=true
    ovh_test.go:250: 	HEADER: key=X-Ovh-Signature, value=[$1$8a21169b341aa23e82192e07457ca978006b1ba9]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Timestamp, value=[1457018875]
    ovh_test.go:250: 	HEADER: key=Accept-Encoding, value=[gzip]
    ovh_test.go:250: 	HEADER: key=User-Agent, value=[Go-http-client/1.1]
    ovh_test.go:250: 	HEADER: key=Accept, value=[application/json]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Application, value=[TDPKJdwZwAQPwKX2]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Consumer, value=[5mBuy6SUQcRw2ZUxg0cG68BoDKpED4KY]
    ovh_test.go:210: context cancelFunc called
    ovh_test.go:248: Request: GET /some/resource. Authenticated=false
    ovh_test.go:250: 	HEADER: key=X-Ovh-Application, value=[TDPKJdwZwAQPwKX2]
    ovh_test.go:250: 	HEADER: key=Accept-Encoding, value=[gzip]
    ovh_test.go:250: 	HEADER: key=User-Agent, value=[Go-http-client/1.1]
    ovh_test.go:250: 	HEADER: key=Accept, value=[application/json]
    ovh_test.go:210: context cancelFunc called
    ovh_test.go:248: Request: DELETE /some/resource. Authenticated=true
    ovh_test.go:250: 	HEADER: key=X-Ovh-Timestamp, value=[1457018875]
    ovh_test.go:250: 	HEADER: key=Accept-Encoding, value=[gzip]
    ovh_test.go:250: 	HEADER: key=User-Agent, value=[Go-http-client/1.1]
    ovh_test.go:250: 	HEADER: key=Accept, value=[application/json]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Application, value=[TDPKJdwZwAQPwKX2]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Consumer, value=[5mBuy6SUQcRw2ZUxg0cG68BoDKpED4KY]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Signature, value=[$1$f4571312a04a4c75188509e75c40581ca6bb6d7a]
    ovh_test.go:210: context cancelFunc called
    ovh_test.go:248: Request: DELETE /some/resource. Authenticated=false
    ovh_test.go:250: 	HEADER: key=User-Agent, value=[Go-http-client/1.1]
    ovh_test.go:250: 	HEADER: key=Accept, value=[application/json]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Application, value=[TDPKJdwZwAQPwKX2]
    ovh_test.go:250: 	HEADER: key=Accept-Encoding, value=[gzip]
    ovh_test.go:210: context cancelFunc called
    ovh_test.go:248: Request: POST /some/resource. Authenticated=true
    ovh_test.go:250: 	HEADER: key=Content-Type, value=[application/json;charset=utf-8]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Consumer, value=[5mBuy6SUQcRw2ZUxg0cG68BoDKpED4KY]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Signature, value=[$1$6549d84e65be72f4ec0d7b6d7eaa19554a265990]
    ovh_test.go:250: 	HEADER: key=Accept-Encoding, value=[gzip]
    ovh_test.go:250: 	HEADER: key=Content-Length, value=[35]
    ovh_test.go:250: 	HEADER: key=Accept, value=[application/json]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Application, value=[TDPKJdwZwAQPwKX2]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Timestamp, value=[1457018875]
    ovh_test.go:250: 	HEADER: key=User-Agent, value=[Go-http-client/1.1]
    ovh_test.go:210: context cancelFunc called
    ovh_test.go:248: Request: POST /some/resource. Authenticated=false
    ovh_test.go:250: 	HEADER: key=Accept, value=[application/json]
    ovh_test.go:250: 	HEADER: key=Content-Type, value=[application/json;charset=utf-8]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Application, value=[TDPKJdwZwAQPwKX2]
    ovh_test.go:250: 	HEADER: key=Accept-Encoding, value=[gzip]
    ovh_test.go:250: 	HEADER: key=User-Agent, value=[Go-http-client/1.1]
    ovh_test.go:250: 	HEADER: key=Content-Length, value=[35]
    ovh_test.go:210: context cancelFunc called
    ovh_test.go:248: Request: PUT /some/resource. Authenticated=true
    ovh_test.go:250: 	HEADER: key=X-Ovh-Application, value=[TDPKJdwZwAQPwKX2]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Consumer, value=[5mBuy6SUQcRw2ZUxg0cG68BoDKpED4KY]
    ovh_test.go:250: 	HEADER: key=User-Agent, value=[Go-http-client/1.1]
    ovh_test.go:250: 	HEADER: key=Content-Length, value=[35]
    ovh_test.go:250: 	HEADER: key=Accept, value=[application/json]
    ovh_test.go:250: 	HEADER: key=Content-Type, value=[application/json;charset=utf-8]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Signature, value=[$1$983e2a9a213c99211edd0b32715ac1ace1a6a0ea]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Timestamp, value=[1457018875]
    ovh_test.go:250: 	HEADER: key=Accept-Encoding, value=[gzip]
    ovh_test.go:210: context cancelFunc called
    ovh_test.go:248: Request: PUT /some/resource. Authenticated=false
    ovh_test.go:250: 	HEADER: key=User-Agent, value=[Go-http-client/1.1]
    ovh_test.go:250: 	HEADER: key=Content-Length, value=[35]
    ovh_test.go:250: 	HEADER: key=Accept, value=[application/json]
    ovh_test.go:250: 	HEADER: key=Content-Type, value=[application/json;charset=utf-8]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Application, value=[TDPKJdwZwAQPwKX2]
    ovh_test.go:250: 	HEADER: key=Accept-Encoding, value=[gzip]
    ovh_test.go:210: context cancelFunc called
    ovh_test.go:248: Request: GET /some/resource. Authenticated=true
    ovh_test.go:250: 	HEADER: key=User-Agent, value=[Go-http-client/1.1]
    ovh_test.go:250: 	HEADER: key=Accept, value=[application/json]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Application, value=[TDPKJdwZwAQPwKX2]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Consumer, value=[5mBuy6SUQcRw2ZUxg0cG68BoDKpED4KY]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Signature, value=[$1$8a21169b341aa23e82192e07457ca978006b1ba9]
    ovh_test.go:250: 	HEADER: key=X-Ovh-Timestamp, value=[1457018875]
    ovh_test.go:250: 	HEADER: key=Accept-Encoding, value=[gzip]
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
	panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x2aa36163dc6]
goroutine 83 [running]:
testing.tRunner.func1(0xc0001a3600)
	/usr/lib/golang/src/testing/testing.go:792 +0x3be
panic(0x2aa362b1a60, 0x2aa3653ab30)
	/usr/lib/golang/src/runtime/panic.go:513 +0x1d4
github.com/ovh/go-ovh/ovh.APIMethodTester(0xc0001a3600, 0x2aa36169ade, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100000000000002)
	/builddir/build/BUILD/go-ovh-ba5adb4cf0148a3dbdbd30586f075266256a77b1/_build/src/github.com/ovh/go-ovh/ovh/ovh_test.go:248 +0x706
github.com/ovh/go-ovh/ovh.TestAllAPIMethods(0xc0001a3600)
	/builddir/build/BUILD/go-ovh-ba5adb4cf0148a3dbdbd30586f075266256a77b1/_build/src/github.com/ovh/go-ovh/ovh/ovh_test.go:295 +0x3f4
testing.tRunner(0xc0001a3600, 0x2aa363139b0)
	/usr/lib/golang/src/testing/testing.go:827 +0xcc
created by testing.(*T).Run
	/usr/lib/golang/src/testing/testing.go:878 +0x372
exit status 2
FAIL	github.com/ovh/go-ovh/ovh	8.331s

(full build log)

I tried this outside of the go packaging macros, and it still failed, albeit with a slightly different error.

$ go env | grep -e GOOS -e GOARCH
GOARCH="s390x"
GOOS="linux"
$ go get github.com/ovh/go-ovh/ovh
$ cd go/src/github.com/ovh/go-ovh/ovh/
$ go test
fork/exec /tmp/go-build729930756/b001/ovh.test: exec format error
FAIL	github.com/ovh/go-ovh/ovh	0.000s

The same steps pass on x86_64.

$ go env | grep -e GOOS -e GOARCH
GOARCH="amd64"
GOOS="linux"
$ go get github.com/ovh/go-ovh/ovh
$ cd go/src/github.com/ovh/go-ovh/ovh/
$ go test
PASS
ok  	github.com/ovh/go-ovh/ovh	32.048s

Better mutex usage on getTimeDelta

Current implementation of getTimeDelta is NOT ok.
We are using a variable outside of the mutex, mutex is blocking read call which can definitely impact throughput.

A better implementation would be to use a sync.Once to make sure that unicity is respected, and remove all the mutex sitation

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.