mirror of https://github.com/hashicorp/consul
api: starting intention endpoints, reorganize files slightly
parent
263e2c7cf7
commit
663a12d96b
|
@ -1,37 +1,5 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CARootList is the structure for the results of listing roots.
|
|
||||||
type CARootList struct {
|
|
||||||
ActiveRootID string
|
|
||||||
Roots []*CARoot
|
|
||||||
}
|
|
||||||
|
|
||||||
// CARoot is a single CA within Connect.
|
|
||||||
type CARoot struct {
|
|
||||||
ID string
|
|
||||||
Name string
|
|
||||||
RootCert string
|
|
||||||
Active bool
|
|
||||||
CreateIndex uint64
|
|
||||||
ModifyIndex uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
type IssuedCert struct {
|
|
||||||
SerialNumber string
|
|
||||||
CertPEM string
|
|
||||||
PrivateKeyPEM string
|
|
||||||
Service string
|
|
||||||
ServiceURI string
|
|
||||||
ValidAfter time.Time
|
|
||||||
ValidBefore time.Time
|
|
||||||
CreateIndex uint64
|
|
||||||
ModifyIndex uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect can be used to work with endpoints related to Connect, the
|
// Connect can be used to work with endpoints related to Connect, the
|
||||||
// feature for securely connecting services within Consul.
|
// feature for securely connecting services within Consul.
|
||||||
type Connect struct {
|
type Connect struct {
|
||||||
|
@ -42,24 +10,3 @@ type Connect struct {
|
||||||
func (c *Client) Connect() *Connect {
|
func (c *Client) Connect() *Connect {
|
||||||
return &Connect{c}
|
return &Connect{c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CARoots queries the list of available roots.
|
|
||||||
func (h *Connect) CARoots(q *QueryOptions) (*CARootList, *QueryMeta, error) {
|
|
||||||
r := h.c.newRequest("GET", "/v1/connect/ca/roots")
|
|
||||||
r.setQueryOptions(q)
|
|
||||||
rtt, resp, err := requireOK(h.c.doRequest(r))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
qm := &QueryMeta{}
|
|
||||||
parseQueryMeta(resp, qm)
|
|
||||||
qm.RequestTime = rtt
|
|
||||||
|
|
||||||
var out CARootList
|
|
||||||
if err := decodeBody(resp, &out); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return &out, qm, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CARootList is the structure for the results of listing roots.
|
||||||
|
type CARootList struct {
|
||||||
|
ActiveRootID string
|
||||||
|
Roots []*CARoot
|
||||||
|
}
|
||||||
|
|
||||||
|
// CARoot represents a root CA certificate that is trusted.
|
||||||
|
type CARoot struct {
|
||||||
|
// ID is a globally unique ID (UUID) representing this CA root.
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// Name is a human-friendly name for this CA root. This value is
|
||||||
|
// opaque to Consul and is not used for anything internally.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// RootCert is the PEM-encoded public certificate.
|
||||||
|
RootCert string
|
||||||
|
|
||||||
|
// Active is true if this is the current active CA. This must only
|
||||||
|
// be true for exactly one CA. For any method that modifies roots in the
|
||||||
|
// state store, tests should be written to verify that multiple roots
|
||||||
|
// cannot be active.
|
||||||
|
Active bool
|
||||||
|
|
||||||
|
CreateIndex uint64
|
||||||
|
ModifyIndex uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssuedCert is a certificate that has been issued by a Connect CA.
|
||||||
|
type IssuedCert struct {
|
||||||
|
// SerialNumber is the unique serial number for this certificate.
|
||||||
|
// This is encoded in standard hex separated by :.
|
||||||
|
SerialNumber string
|
||||||
|
|
||||||
|
// CertPEM and PrivateKeyPEM are the PEM-encoded certificate and private
|
||||||
|
// key for that cert, respectively. This should not be stored in the
|
||||||
|
// state store, but is present in the sign API response.
|
||||||
|
CertPEM string `json:",omitempty"`
|
||||||
|
PrivateKeyPEM string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Service is the name of the service for which the cert was issued.
|
||||||
|
// ServiceURI is the cert URI value.
|
||||||
|
Service string
|
||||||
|
ServiceURI string
|
||||||
|
|
||||||
|
// ValidAfter and ValidBefore are the validity periods for the
|
||||||
|
// certificate.
|
||||||
|
ValidAfter time.Time
|
||||||
|
ValidBefore time.Time
|
||||||
|
|
||||||
|
CreateIndex uint64
|
||||||
|
ModifyIndex uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// CARoots queries the list of available roots.
|
||||||
|
func (h *Connect) CARoots(q *QueryOptions) (*CARootList, *QueryMeta, error) {
|
||||||
|
r := h.c.newRequest("GET", "/v1/connect/ca/roots")
|
||||||
|
r.setQueryOptions(q)
|
||||||
|
rtt, resp, err := requireOK(h.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
qm := &QueryMeta{}
|
||||||
|
parseQueryMeta(resp, qm)
|
||||||
|
qm.RequestTime = rtt
|
||||||
|
|
||||||
|
var out CARootList
|
||||||
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return &out, qm, nil
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Intention defines an intention for the Connect Service Graph. This defines
|
||||||
|
// the allowed or denied behavior of a connection between two services using
|
||||||
|
// Connect.
|
||||||
|
type Intention struct {
|
||||||
|
// ID is the UUID-based ID for the intention, always generated by Consul.
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// Description is a human-friendly description of this intention.
|
||||||
|
// It is opaque to Consul and is only stored and transferred in API
|
||||||
|
// requests.
|
||||||
|
Description string
|
||||||
|
|
||||||
|
// SourceNS, SourceName are the namespace and name, respectively, of
|
||||||
|
// the source service. Either of these may be the wildcard "*", but only
|
||||||
|
// the full value can be a wildcard. Partial wildcards are not allowed.
|
||||||
|
// The source may also be a non-Consul service, as specified by SourceType.
|
||||||
|
//
|
||||||
|
// DestinationNS, DestinationName is the same, but for the destination
|
||||||
|
// service. The same rules apply. The destination is always a Consul
|
||||||
|
// service.
|
||||||
|
SourceNS, SourceName string
|
||||||
|
DestinationNS, DestinationName string
|
||||||
|
|
||||||
|
// SourceType is the type of the value for the source.
|
||||||
|
SourceType IntentionSourceType
|
||||||
|
|
||||||
|
// Action is whether this is a whitelist or blacklist intention.
|
||||||
|
Action IntentionAction
|
||||||
|
|
||||||
|
// DefaultAddr, DefaultPort of the local listening proxy (if any) to
|
||||||
|
// make this connection.
|
||||||
|
DefaultAddr string
|
||||||
|
DefaultPort int
|
||||||
|
|
||||||
|
// Meta is arbitrary metadata associated with the intention. This is
|
||||||
|
// opaque to Consul but is served in API responses.
|
||||||
|
Meta map[string]string
|
||||||
|
|
||||||
|
// CreatedAt and UpdatedAt keep track of when this record was created
|
||||||
|
// or modified.
|
||||||
|
CreatedAt, UpdatedAt time.Time
|
||||||
|
|
||||||
|
CreateIndex uint64
|
||||||
|
ModifyIndex uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntentionAction is the action that the intention represents. This
|
||||||
|
// can be "allow" or "deny" to whitelist or blacklist intentions.
|
||||||
|
type IntentionAction string
|
||||||
|
|
||||||
|
const (
|
||||||
|
IntentionActionAllow IntentionAction = "allow"
|
||||||
|
IntentionActionDeny IntentionAction = "deny"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IntentionSourceType is the type of the source within an intention.
|
||||||
|
type IntentionSourceType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// IntentionSourceConsul is a service within the Consul catalog.
|
||||||
|
IntentionSourceConsul IntentionSourceType = "consul"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Intentions returns the list of intentions.
|
||||||
|
func (h *Connect) Intentions(q *QueryOptions) ([]*Intention, *QueryMeta, error) {
|
||||||
|
r := h.c.newRequest("GET", "/v1/connect/intentions")
|
||||||
|
r.setQueryOptions(q)
|
||||||
|
rtt, resp, err := requireOK(h.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
qm := &QueryMeta{}
|
||||||
|
parseQueryMeta(resp, qm)
|
||||||
|
qm.RequestTime = rtt
|
||||||
|
|
||||||
|
var out []*Intention
|
||||||
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return out, qm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntentionCreate will create a new intention. The ID in the given
|
||||||
|
// structure must be empty and a generate ID will be returned on
|
||||||
|
// success.
|
||||||
|
func (c *Connect) IntentionCreate(ixn *Intention, q *WriteOptions) (string, *WriteMeta, error) {
|
||||||
|
r := c.c.newRequest("POST", "/v1/connect/intentions")
|
||||||
|
r.setWriteOptions(q)
|
||||||
|
r.obj = ixn
|
||||||
|
rtt, resp, err := requireOK(c.c.doRequest(r))
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
wm := &WriteMeta{}
|
||||||
|
wm.RequestTime = rtt
|
||||||
|
|
||||||
|
var out struct{ ID string }
|
||||||
|
if err := decodeBody(resp, &out); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
return out.ID, wm, nil
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAPI_ConnectIntentionCreate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
require := require.New(t)
|
||||||
|
c, s := makeClient(t)
|
||||||
|
defer s.Stop()
|
||||||
|
|
||||||
|
connect := c.Connect()
|
||||||
|
|
||||||
|
// Create
|
||||||
|
ixn := testIntention()
|
||||||
|
id, _, err := connect.IntentionCreate(ixn, nil)
|
||||||
|
require.Nil(err)
|
||||||
|
require.NotEmpty(id)
|
||||||
|
|
||||||
|
// List it
|
||||||
|
list, _, err := connect.Intentions(nil)
|
||||||
|
require.Nil(err)
|
||||||
|
require.Len(list, 1)
|
||||||
|
|
||||||
|
actual := list[0]
|
||||||
|
ixn.ID = id
|
||||||
|
ixn.CreatedAt = actual.CreatedAt
|
||||||
|
ixn.UpdatedAt = actual.UpdatedAt
|
||||||
|
ixn.CreateIndex = actual.CreateIndex
|
||||||
|
ixn.ModifyIndex = actual.ModifyIndex
|
||||||
|
require.Equal(ixn, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testIntention() *Intention {
|
||||||
|
return &Intention{
|
||||||
|
SourceNS: "eng",
|
||||||
|
SourceName: "api",
|
||||||
|
DestinationNS: "eng",
|
||||||
|
DestinationName: "db",
|
||||||
|
Action: IntentionActionAllow,
|
||||||
|
SourceType: IntentionSourceConsul,
|
||||||
|
Meta: map[string]string{},
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue