This describes an approach to allow third party Go codebases to drive Juju. At a high level it works like this.
- Create a connector instance, telling it how to connect to a Juju controller and how to provide authentication credentials.
- Use this connector instance to obtain a connection instance.
- Get a facade client from the connection.
- Use the facade client to make one or more requests.
The github.com/juju/juju/api/connector
package defines a Connector
interface.
type Connector interface {
Connect(...api.DialOption) (api.Connection, error)
}
Its only purpose is to create instances of api.Connection
that can later be
used to talk to a given Juju controller with a given set of credentials. There
are currently two ways to obtain a Connector
instance.
If one knows the how to contact a controller, and some juju user credentials (e.g. username and password) it is possible to obtain a connector like this.
import "github.com/juju/juju/api/connector"
connr, err := connector.NewSimple(connector.SimpleConfig{
ControllerAddresses: []string{"10.225.205.241:17070"},
CaCert: "...",
Username: "jujuuser",
Password: "password1",
})
There can be an error if the config is not valid (e.g. no controller address specified).
If a client store is available on the filesystem, it is possible to use it to configure the connector.
import "github.com/juju/juju/api/connector"
connr, err := connector.NewClientStore(connector.ClientStoreConfig{
ControllerName: "overlord",
})
The above will try to find a client store in the default location. It is also
possible to pass in a custom value (see clientstoreconnector.go
for details).
Once we have a connector, it's easy to make requests.
import (
"encoding/json"
"github.com/juju/juju/api/connector"
"github.com/juju/juju/api/client/client"
)
// Get a connection
conn, err := connr.Connect()
if err != nil {
log.Fatalf("Error opening connection: %s", err)
}
defer conn.Close()
// Get a Client facade client
client := apiclient.NewClient(conn)
// Call the Status endpoint of the client facade
status, err := client.Status(nil)
if err != nil {
log.Fatalf("Error requesting status: %s", err)
}
// Print to stdout.
b, err := json.MarshalIndent(status, "", " ")
if err != nil {
log.Fatalf("Error marshalling response: %s", err)
}
fmt.Printf("%s\n", b)
- When to reuse a connection, when to create a new one?
- How to find the useful endpoints?