mirror of https://github.com/hashicorp/consul
Mitchell Hashimoto
7 years ago
5 changed files with 240 additions and 53 deletions
@ -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