Add HashiCorp Nomad provider (#483)
* provider: adding Nomad provider * updating CONTRIBUTING.md with Nomad provider * updated README.md by adding the Nomad provider * fix typo * adding nomad/api and nomad/testutil deps * adding Nomad binary dependency for provider tests * fixed the nomad binary download command step and added tolerations to the nomad provider. * adding nomad provider demo gif * adding my name to authors * adding two missing go-rootcerts files after dep ensure * delete pod comment
This commit is contained in:
committed by
Robbie Zhang
parent
5796be449b
commit
a46e1dd2ce
281
vendor/github.com/hashicorp/nomad/acl/acl.go
generated
vendored
Normal file
281
vendor/github.com/hashicorp/nomad/acl/acl.go
generated
vendored
Normal file
@@ -0,0 +1,281 @@
|
||||
package acl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
iradix "github.com/hashicorp/go-immutable-radix"
|
||||
)
|
||||
|
||||
// ManagementACL is a singleton used for management tokens
|
||||
var ManagementACL *ACL
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
ManagementACL, err = NewACL(true, nil)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to setup management ACL: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
// capabilitySet is a type wrapper to help managing a set of capabilities
|
||||
type capabilitySet map[string]struct{}
|
||||
|
||||
func (c capabilitySet) Check(k string) bool {
|
||||
_, ok := c[k]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (c capabilitySet) Set(k string) {
|
||||
c[k] = struct{}{}
|
||||
}
|
||||
|
||||
func (c capabilitySet) Clear() {
|
||||
for cap := range c {
|
||||
delete(c, cap)
|
||||
}
|
||||
}
|
||||
|
||||
// ACL object is used to convert a set of policies into a structure that
|
||||
// can be efficiently evaluated to determine if an action is allowed.
|
||||
type ACL struct {
|
||||
// management tokens are allowed to do anything
|
||||
management bool
|
||||
|
||||
// namespaces maps a namespace to a capabilitySet
|
||||
namespaces *iradix.Tree
|
||||
|
||||
agent string
|
||||
node string
|
||||
operator string
|
||||
quota string
|
||||
}
|
||||
|
||||
// maxPrivilege returns the policy which grants the most privilege
|
||||
// This handles the case of Deny always taking maximum precedence.
|
||||
func maxPrivilege(a, b string) string {
|
||||
switch {
|
||||
case a == PolicyDeny || b == PolicyDeny:
|
||||
return PolicyDeny
|
||||
case a == PolicyWrite || b == PolicyWrite:
|
||||
return PolicyWrite
|
||||
case a == PolicyRead || b == PolicyRead:
|
||||
return PolicyRead
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// NewACL compiles a set of policies into an ACL object
|
||||
func NewACL(management bool, policies []*Policy) (*ACL, error) {
|
||||
// Hot-path management tokens
|
||||
if management {
|
||||
return &ACL{management: true}, nil
|
||||
}
|
||||
|
||||
// Create the ACL object
|
||||
acl := &ACL{}
|
||||
nsTxn := iradix.New().Txn()
|
||||
|
||||
for _, policy := range policies {
|
||||
NAMESPACES:
|
||||
for _, ns := range policy.Namespaces {
|
||||
// Check for existing capabilities
|
||||
var capabilities capabilitySet
|
||||
raw, ok := nsTxn.Get([]byte(ns.Name))
|
||||
if ok {
|
||||
capabilities = raw.(capabilitySet)
|
||||
} else {
|
||||
capabilities = make(capabilitySet)
|
||||
nsTxn.Insert([]byte(ns.Name), capabilities)
|
||||
}
|
||||
|
||||
// Deny always takes precedence
|
||||
if capabilities.Check(NamespaceCapabilityDeny) {
|
||||
continue NAMESPACES
|
||||
}
|
||||
|
||||
// Add in all the capabilities
|
||||
for _, cap := range ns.Capabilities {
|
||||
if cap == NamespaceCapabilityDeny {
|
||||
// Overwrite any existing capabilities
|
||||
capabilities.Clear()
|
||||
capabilities.Set(NamespaceCapabilityDeny)
|
||||
continue NAMESPACES
|
||||
}
|
||||
capabilities.Set(cap)
|
||||
}
|
||||
}
|
||||
|
||||
// Take the maximum privilege for agent, node, and operator
|
||||
if policy.Agent != nil {
|
||||
acl.agent = maxPrivilege(acl.agent, policy.Agent.Policy)
|
||||
}
|
||||
if policy.Node != nil {
|
||||
acl.node = maxPrivilege(acl.node, policy.Node.Policy)
|
||||
}
|
||||
if policy.Operator != nil {
|
||||
acl.operator = maxPrivilege(acl.operator, policy.Operator.Policy)
|
||||
}
|
||||
if policy.Quota != nil {
|
||||
acl.quota = maxPrivilege(acl.quota, policy.Quota.Policy)
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize the namespaces
|
||||
acl.namespaces = nsTxn.Commit()
|
||||
return acl, nil
|
||||
}
|
||||
|
||||
// AllowNsOp is shorthand for AllowNamespaceOperation
|
||||
func (a *ACL) AllowNsOp(ns string, op string) bool {
|
||||
return a.AllowNamespaceOperation(ns, op)
|
||||
}
|
||||
|
||||
// AllowNamespaceOperation checks if a given operation is allowed for a namespace
|
||||
func (a *ACL) AllowNamespaceOperation(ns string, op string) bool {
|
||||
// Hot path management tokens
|
||||
if a.management {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check for a matching capability set
|
||||
raw, ok := a.namespaces.Get([]byte(ns))
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the capability has been granted
|
||||
capabilities := raw.(capabilitySet)
|
||||
return capabilities.Check(op)
|
||||
}
|
||||
|
||||
// AllowNamespace checks if any operations are allowed for a namespace
|
||||
func (a *ACL) AllowNamespace(ns string) bool {
|
||||
// Hot path management tokens
|
||||
if a.management {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check for a matching capability set
|
||||
raw, ok := a.namespaces.Get([]byte(ns))
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the capability has been granted
|
||||
capabilities := raw.(capabilitySet)
|
||||
if len(capabilities) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return !capabilities.Check(PolicyDeny)
|
||||
}
|
||||
|
||||
// AllowAgentRead checks if read operations are allowed for an agent
|
||||
func (a *ACL) AllowAgentRead() bool {
|
||||
switch {
|
||||
case a.management:
|
||||
return true
|
||||
case a.agent == PolicyWrite:
|
||||
return true
|
||||
case a.agent == PolicyRead:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// AllowAgentWrite checks if write operations are allowed for an agent
|
||||
func (a *ACL) AllowAgentWrite() bool {
|
||||
switch {
|
||||
case a.management:
|
||||
return true
|
||||
case a.agent == PolicyWrite:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// AllowNodeRead checks if read operations are allowed for a node
|
||||
func (a *ACL) AllowNodeRead() bool {
|
||||
switch {
|
||||
case a.management:
|
||||
return true
|
||||
case a.node == PolicyWrite:
|
||||
return true
|
||||
case a.node == PolicyRead:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// AllowNodeWrite checks if write operations are allowed for a node
|
||||
func (a *ACL) AllowNodeWrite() bool {
|
||||
switch {
|
||||
case a.management:
|
||||
return true
|
||||
case a.node == PolicyWrite:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// AllowOperatorRead checks if read operations are allowed for a operator
|
||||
func (a *ACL) AllowOperatorRead() bool {
|
||||
switch {
|
||||
case a.management:
|
||||
return true
|
||||
case a.operator == PolicyWrite:
|
||||
return true
|
||||
case a.operator == PolicyRead:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// AllowOperatorWrite checks if write operations are allowed for a operator
|
||||
func (a *ACL) AllowOperatorWrite() bool {
|
||||
switch {
|
||||
case a.management:
|
||||
return true
|
||||
case a.operator == PolicyWrite:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// AllowQuotaRead checks if read operations are allowed for all quotas
|
||||
func (a *ACL) AllowQuotaRead() bool {
|
||||
switch {
|
||||
case a.management:
|
||||
return true
|
||||
case a.quota == PolicyWrite:
|
||||
return true
|
||||
case a.quota == PolicyRead:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// AllowQuotaWrite checks if write operations are allowed for quotas
|
||||
func (a *ACL) AllowQuotaWrite() bool {
|
||||
switch {
|
||||
case a.management:
|
||||
return true
|
||||
case a.quota == PolicyWrite:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsManagement checks if this represents a management token
|
||||
func (a *ACL) IsManagement() bool {
|
||||
return a.management
|
||||
}
|
||||
191
vendor/github.com/hashicorp/nomad/acl/policy.go
generated
vendored
Normal file
191
vendor/github.com/hashicorp/nomad/acl/policy.go
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
package acl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/hashicorp/hcl"
|
||||
)
|
||||
|
||||
const (
|
||||
// The following levels are the only valid values for the `policy = "read"` stanza.
|
||||
// When policies are merged together, the most privilege is granted, except for deny
|
||||
// which always takes precedence and supercedes.
|
||||
PolicyDeny = "deny"
|
||||
PolicyRead = "read"
|
||||
PolicyWrite = "write"
|
||||
)
|
||||
|
||||
const (
|
||||
// The following are the fine-grained capabilities that can be granted within a namespace.
|
||||
// The Policy stanza is a short hand for granting several of these. When capabilities are
|
||||
// combined we take the union of all capabilities. If the deny capability is present, it
|
||||
// takes precedence and overwrites all other capabilities.
|
||||
NamespaceCapabilityDeny = "deny"
|
||||
NamespaceCapabilityListJobs = "list-jobs"
|
||||
NamespaceCapabilityReadJob = "read-job"
|
||||
NamespaceCapabilitySubmitJob = "submit-job"
|
||||
NamespaceCapabilityDispatchJob = "dispatch-job"
|
||||
NamespaceCapabilityReadLogs = "read-logs"
|
||||
NamespaceCapabilityReadFS = "read-fs"
|
||||
NamespaceCapabilitySentinelOverride = "sentinel-override"
|
||||
)
|
||||
|
||||
var (
|
||||
validNamespace = regexp.MustCompile("^[a-zA-Z0-9-]{1,128}$")
|
||||
)
|
||||
|
||||
// Policy represents a parsed HCL or JSON policy.
|
||||
type Policy struct {
|
||||
Namespaces []*NamespacePolicy `hcl:"namespace,expand"`
|
||||
Agent *AgentPolicy `hcl:"agent"`
|
||||
Node *NodePolicy `hcl:"node"`
|
||||
Operator *OperatorPolicy `hcl:"operator"`
|
||||
Quota *QuotaPolicy `hcl:"quota"`
|
||||
Raw string `hcl:"-"`
|
||||
}
|
||||
|
||||
// IsEmpty checks to make sure that at least one policy has been set and is not
|
||||
// comprised of only a raw policy.
|
||||
func (p *Policy) IsEmpty() bool {
|
||||
return len(p.Namespaces) == 0 &&
|
||||
p.Agent == nil &&
|
||||
p.Node == nil &&
|
||||
p.Operator == nil &&
|
||||
p.Quota == nil
|
||||
}
|
||||
|
||||
// NamespacePolicy is the policy for a specific namespace
|
||||
type NamespacePolicy struct {
|
||||
Name string `hcl:",key"`
|
||||
Policy string
|
||||
Capabilities []string
|
||||
}
|
||||
|
||||
type AgentPolicy struct {
|
||||
Policy string
|
||||
}
|
||||
|
||||
type NodePolicy struct {
|
||||
Policy string
|
||||
}
|
||||
|
||||
type OperatorPolicy struct {
|
||||
Policy string
|
||||
}
|
||||
|
||||
type QuotaPolicy struct {
|
||||
Policy string
|
||||
}
|
||||
|
||||
// isPolicyValid makes sure the given string matches one of the valid policies.
|
||||
func isPolicyValid(policy string) bool {
|
||||
switch policy {
|
||||
case PolicyDeny, PolicyRead, PolicyWrite:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// isNamespaceCapabilityValid ensures the given capability is valid for a namespace policy
|
||||
func isNamespaceCapabilityValid(cap string) bool {
|
||||
switch cap {
|
||||
case NamespaceCapabilityDeny, NamespaceCapabilityListJobs, NamespaceCapabilityReadJob,
|
||||
NamespaceCapabilitySubmitJob, NamespaceCapabilityDispatchJob, NamespaceCapabilityReadLogs,
|
||||
NamespaceCapabilityReadFS:
|
||||
return true
|
||||
// Separate the enterprise-only capabilities
|
||||
case NamespaceCapabilitySentinelOverride:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// expandNamespacePolicy provides the equivalent set of capabilities for
|
||||
// a namespace policy
|
||||
func expandNamespacePolicy(policy string) []string {
|
||||
switch policy {
|
||||
case PolicyDeny:
|
||||
return []string{NamespaceCapabilityDeny}
|
||||
case PolicyRead:
|
||||
return []string{
|
||||
NamespaceCapabilityListJobs,
|
||||
NamespaceCapabilityReadJob,
|
||||
}
|
||||
case PolicyWrite:
|
||||
return []string{
|
||||
NamespaceCapabilityListJobs,
|
||||
NamespaceCapabilityReadJob,
|
||||
NamespaceCapabilitySubmitJob,
|
||||
NamespaceCapabilityDispatchJob,
|
||||
NamespaceCapabilityReadLogs,
|
||||
NamespaceCapabilityReadFS,
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Parse is used to parse the specified ACL rules into an
|
||||
// intermediary set of policies, before being compiled into
|
||||
// the ACL
|
||||
func Parse(rules string) (*Policy, error) {
|
||||
// Decode the rules
|
||||
p := &Policy{Raw: rules}
|
||||
if rules == "" {
|
||||
// Hot path for empty rules
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Attempt to parse
|
||||
if err := hcl.Decode(p, rules); err != nil {
|
||||
return nil, fmt.Errorf("Failed to parse ACL Policy: %v", err)
|
||||
}
|
||||
|
||||
// At least one valid policy must be specified, we don't want to store only
|
||||
// raw data
|
||||
if p.IsEmpty() {
|
||||
return nil, fmt.Errorf("Invalid policy: %s", p.Raw)
|
||||
}
|
||||
|
||||
// Validate the policy
|
||||
for _, ns := range p.Namespaces {
|
||||
if !validNamespace.MatchString(ns.Name) {
|
||||
return nil, fmt.Errorf("Invalid namespace name: %#v", ns)
|
||||
}
|
||||
if ns.Policy != "" && !isPolicyValid(ns.Policy) {
|
||||
return nil, fmt.Errorf("Invalid namespace policy: %#v", ns)
|
||||
}
|
||||
for _, cap := range ns.Capabilities {
|
||||
if !isNamespaceCapabilityValid(cap) {
|
||||
return nil, fmt.Errorf("Invalid namespace capability '%s': %#v", cap, ns)
|
||||
}
|
||||
}
|
||||
|
||||
// Expand the short hand policy to the capabilities and
|
||||
// add to any existing capabilities
|
||||
if ns.Policy != "" {
|
||||
extraCap := expandNamespacePolicy(ns.Policy)
|
||||
ns.Capabilities = append(ns.Capabilities, extraCap...)
|
||||
}
|
||||
}
|
||||
|
||||
if p.Agent != nil && !isPolicyValid(p.Agent.Policy) {
|
||||
return nil, fmt.Errorf("Invalid agent policy: %#v", p.Agent)
|
||||
}
|
||||
|
||||
if p.Node != nil && !isPolicyValid(p.Node.Policy) {
|
||||
return nil, fmt.Errorf("Invalid node policy: %#v", p.Node)
|
||||
}
|
||||
|
||||
if p.Operator != nil && !isPolicyValid(p.Operator.Policy) {
|
||||
return nil, fmt.Errorf("Invalid operator policy: %#v", p.Operator)
|
||||
}
|
||||
|
||||
if p.Quota != nil && !isPolicyValid(p.Quota.Policy) {
|
||||
return nil, fmt.Errorf("Invalid quota policy: %#v", p.Quota)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
Reference in New Issue
Block a user