mirror of
https://github.com/kemko/nomad.git
synced 2026-01-04 01:15:43 +03:00
Merge pull request #9142 from hashicorp/f-hclv2-2.3
Support HCLv2 for Nomad jobs
This commit is contained in:
@@ -15,9 +15,9 @@ const (
|
||||
|
||||
// Constraint is used to serialize a job placement constraint.
|
||||
type Constraint struct {
|
||||
LTarget string
|
||||
RTarget string
|
||||
Operand string
|
||||
LTarget string `hcl:"attribute,optional"`
|
||||
RTarget string `hcl:"value,optional"`
|
||||
Operand string `hcl:"operator,optional"`
|
||||
}
|
||||
|
||||
// NewConstraint generates a new job placement constraint.
|
||||
|
||||
@@ -89,9 +89,9 @@ const (
|
||||
)
|
||||
|
||||
type CSIMountOptions struct {
|
||||
FSType string `hcl:"fs_type"`
|
||||
MountFlags []string `hcl:"mount_flags"`
|
||||
ExtraKeysHCL []string `hcl:",unusedKeys" json:"-"` // report unexpected keys
|
||||
FSType string `hcl:"fs_type,optional"`
|
||||
MountFlags []string `hcl:"mount_flags,optional"`
|
||||
ExtraKeysHCL []string `hcl1:",unusedKeys" json:"-"` // report unexpected keys
|
||||
}
|
||||
|
||||
type CSISecrets map[string]string
|
||||
@@ -133,7 +133,7 @@ type CSIVolume struct {
|
||||
ModifyIndex uint64
|
||||
|
||||
// ExtraKeysHCL is used by the hcl parser to report unexpected keys
|
||||
ExtraKeysHCL []string `hcl:",unusedKeys" json:"-"`
|
||||
ExtraKeysHCL []string `hcl1:",unusedKeys" json:"-"`
|
||||
}
|
||||
|
||||
// allocs is called after we query the volume (creating this CSIVolume struct) to collapse
|
||||
|
||||
98
api/jobs.go
98
api/jobs.go
@@ -50,6 +50,9 @@ type JobsParseRequest struct {
|
||||
// JobHCL is an hcl jobspec
|
||||
JobHCL string
|
||||
|
||||
// HCLv1 indicates whether the JobHCL should be parsed with the hcl v1 parser
|
||||
HCLv1 bool `json:"hclv1,omitempty"`
|
||||
|
||||
// Canonicalize is a flag as to if the server should return default values
|
||||
// for unset fields
|
||||
Canonicalize bool
|
||||
@@ -439,15 +442,15 @@ type periodicForceResponse struct {
|
||||
|
||||
// UpdateStrategy defines a task groups update strategy.
|
||||
type UpdateStrategy struct {
|
||||
Stagger *time.Duration `mapstructure:"stagger"`
|
||||
MaxParallel *int `mapstructure:"max_parallel"`
|
||||
HealthCheck *string `mapstructure:"health_check"`
|
||||
MinHealthyTime *time.Duration `mapstructure:"min_healthy_time"`
|
||||
HealthyDeadline *time.Duration `mapstructure:"healthy_deadline"`
|
||||
ProgressDeadline *time.Duration `mapstructure:"progress_deadline"`
|
||||
Canary *int `mapstructure:"canary"`
|
||||
AutoRevert *bool `mapstructure:"auto_revert"`
|
||||
AutoPromote *bool `mapstructure:"auto_promote"`
|
||||
Stagger *time.Duration `mapstructure:"stagger" hcl:"stagger,optional"`
|
||||
MaxParallel *int `mapstructure:"max_parallel" hcl:"max_parallel,optional"`
|
||||
HealthCheck *string `mapstructure:"health_check" hcl:"health_check,optional"`
|
||||
MinHealthyTime *time.Duration `mapstructure:"min_healthy_time" hcl:"min_healthy_time,optional"`
|
||||
HealthyDeadline *time.Duration `mapstructure:"healthy_deadline" hcl:"healthy_deadline,optional"`
|
||||
ProgressDeadline *time.Duration `mapstructure:"progress_deadline" hcl:"progress_deadline,optional"`
|
||||
Canary *int `mapstructure:"canary" hcl:"canary,optional"`
|
||||
AutoRevert *bool `mapstructure:"auto_revert" hcl:"auto_revert,optional"`
|
||||
AutoPromote *bool `mapstructure:"auto_promote" hcl:"auto_promote,optional"`
|
||||
}
|
||||
|
||||
// DefaultUpdateStrategy provides a baseline that can be used to upgrade
|
||||
@@ -640,8 +643,8 @@ func (u *UpdateStrategy) Empty() bool {
|
||||
}
|
||||
|
||||
type Multiregion struct {
|
||||
Strategy *MultiregionStrategy
|
||||
Regions []*MultiregionRegion
|
||||
Strategy *MultiregionStrategy `hcl:"strategy,block"`
|
||||
Regions []*MultiregionRegion `hcl:"region,block"`
|
||||
}
|
||||
|
||||
func (m *Multiregion) Canonicalize() {
|
||||
@@ -700,24 +703,24 @@ func (m *Multiregion) Copy() *Multiregion {
|
||||
}
|
||||
|
||||
type MultiregionStrategy struct {
|
||||
MaxParallel *int `mapstructure:"max_parallel"`
|
||||
OnFailure *string `mapstructure:"on_failure"`
|
||||
MaxParallel *int `mapstructure:"max_parallel" hcl:"max_parallel,optional"`
|
||||
OnFailure *string `mapstructure:"on_failure" hcl:"on_failure,optional"`
|
||||
}
|
||||
|
||||
type MultiregionRegion struct {
|
||||
Name string
|
||||
Count *int
|
||||
Datacenters []string
|
||||
Meta map[string]string
|
||||
Name string `hcl:",label"`
|
||||
Count *int `hcl:"count,optional"`
|
||||
Datacenters []string `hcl:"datacenters,optional"`
|
||||
Meta map[string]string `hcl:"meta,block"`
|
||||
}
|
||||
|
||||
// PeriodicConfig is for serializing periodic config for a job.
|
||||
type PeriodicConfig struct {
|
||||
Enabled *bool
|
||||
Spec *string
|
||||
Enabled *bool `hcl:"enabled,optional"`
|
||||
Spec *string `hcl:"cron,optional"`
|
||||
SpecType *string
|
||||
ProhibitOverlap *bool `mapstructure:"prohibit_overlap"`
|
||||
TimeZone *string `mapstructure:"time_zone"`
|
||||
ProhibitOverlap *bool `mapstructure:"prohibit_overlap" hcl:"prohibit_overlap,optional"`
|
||||
TimeZone *string `mapstructure:"time_zone" hcl:"time_zone,optional"`
|
||||
}
|
||||
|
||||
func (p *PeriodicConfig) Canonicalize() {
|
||||
@@ -779,38 +782,43 @@ func (p *PeriodicConfig) GetLocation() (*time.Location, error) {
|
||||
|
||||
// ParameterizedJobConfig is used to configure the parameterized job.
|
||||
type ParameterizedJobConfig struct {
|
||||
Payload string
|
||||
MetaRequired []string `mapstructure:"meta_required"`
|
||||
MetaOptional []string `mapstructure:"meta_optional"`
|
||||
Payload string `hcl:"payload,optional"`
|
||||
MetaRequired []string `mapstructure:"meta_required" hcl:"meta_required,optional"`
|
||||
MetaOptional []string `mapstructure:"meta_optional" hcl:"meta_optional,optional"`
|
||||
}
|
||||
|
||||
// Job is used to serialize a job.
|
||||
type Job struct {
|
||||
/* Fields parsed from HCL config */
|
||||
|
||||
Region *string `hcl:"region,optional"`
|
||||
Namespace *string `hcl:"namespace,optional"`
|
||||
ID *string `hcl:"id,optional"`
|
||||
Name *string `hcl:"name,optional"`
|
||||
Type *string `hcl:"type,optional"`
|
||||
Priority *int `hcl:"priority,optional"`
|
||||
AllAtOnce *bool `mapstructure:"all_at_once" hcl:"all_at_once,optional"`
|
||||
Datacenters []string `hcl:"datacenters,optional"`
|
||||
Constraints []*Constraint `hcl:"constraint,block"`
|
||||
Affinities []*Affinity `hcl:"affinity,block"`
|
||||
TaskGroups []*TaskGroup `hcl:"group,block"`
|
||||
Update *UpdateStrategy `hcl:"update,block"`
|
||||
Multiregion *Multiregion `hcl:"multiregion,block"`
|
||||
Spreads []*Spread `hcl:"spread,block"`
|
||||
Periodic *PeriodicConfig `hcl:"periodic,block"`
|
||||
ParameterizedJob *ParameterizedJobConfig `hcl:"parameterized,block"`
|
||||
Reschedule *ReschedulePolicy `hcl:"reschedule,block"`
|
||||
Migrate *MigrateStrategy `hcl:"migrate,block"`
|
||||
Meta map[string]string `hcl:"meta,block"`
|
||||
ConsulToken *string `mapstructure:"consul_token" hcl:"consul_token,optional"`
|
||||
VaultToken *string `mapstructure:"vault_token" hcl:"vault_token,optional"`
|
||||
|
||||
/* Fields set by server, not sourced from job config file */
|
||||
|
||||
Stop *bool
|
||||
Region *string
|
||||
Namespace *string
|
||||
ID *string
|
||||
ParentID *string
|
||||
Name *string
|
||||
Type *string
|
||||
Priority *int
|
||||
AllAtOnce *bool `mapstructure:"all_at_once"`
|
||||
Datacenters []string
|
||||
Constraints []*Constraint
|
||||
Affinities []*Affinity
|
||||
TaskGroups []*TaskGroup
|
||||
Update *UpdateStrategy
|
||||
Multiregion *Multiregion
|
||||
Spreads []*Spread
|
||||
Periodic *PeriodicConfig
|
||||
ParameterizedJob *ParameterizedJobConfig
|
||||
Dispatched bool
|
||||
Payload []byte
|
||||
Reschedule *ReschedulePolicy
|
||||
Migrate *MigrateStrategy
|
||||
Meta map[string]string
|
||||
ConsulToken *string `mapstructure:"consul_token"`
|
||||
VaultToken *string `mapstructure:"vault_token"`
|
||||
VaultNamespace *string `mapstructure:"vault_namespace"`
|
||||
NomadTokenID *string `mapstructure:"nomad_token_id"`
|
||||
Status *string
|
||||
|
||||
@@ -7,17 +7,17 @@ import (
|
||||
// Resources encapsulates the required resources of
|
||||
// a given task or task group.
|
||||
type Resources struct {
|
||||
CPU *int
|
||||
MemoryMB *int `mapstructure:"memory"`
|
||||
DiskMB *int `mapstructure:"disk"`
|
||||
Networks []*NetworkResource
|
||||
Devices []*RequestedDevice
|
||||
CPU *int `hcl:"cpu,optional"`
|
||||
MemoryMB *int `mapstructure:"memory" hcl:"memory,optional"`
|
||||
DiskMB *int `mapstructure:"disk" hcl:"disk,optional"`
|
||||
Networks []*NetworkResource `hcl:"network,block"`
|
||||
Devices []*RequestedDevice `hcl:"device,block"`
|
||||
|
||||
// COMPAT(0.10)
|
||||
// XXX Deprecated. Please do not use. The field will be removed in Nomad
|
||||
// 0.10 and is only being kept to allow any references to be removed before
|
||||
// then.
|
||||
IOPS *int
|
||||
IOPS *int `hcl:"iops,optional"`
|
||||
}
|
||||
|
||||
// Canonicalize will supply missing values in the cases
|
||||
@@ -84,29 +84,29 @@ func (r *Resources) Merge(other *Resources) {
|
||||
}
|
||||
|
||||
type Port struct {
|
||||
Label string
|
||||
Value int `mapstructure:"static"`
|
||||
To int `mapstructure:"to"`
|
||||
HostNetwork string `mapstructure:"host_network"`
|
||||
Label string `hcl:",label"`
|
||||
Value int `mapstructure:"static" hcl:"static,optional"`
|
||||
To int `mapstructure:"to" hcl:"to,optional"`
|
||||
HostNetwork string `mapstructure:"host_network" hcl:"host_network,optional"`
|
||||
}
|
||||
|
||||
type DNSConfig struct {
|
||||
Servers []string `mapstructure:"servers"`
|
||||
Searches []string `mapstructure:"searches"`
|
||||
Options []string `mapstructure:"options"`
|
||||
Servers []string `mapstructure:"servers" hcl:"servers,optional"`
|
||||
Searches []string `mapstructure:"searches" hcl:"searches,optional"`
|
||||
Options []string `mapstructure:"options" hcl:"options,optional"`
|
||||
}
|
||||
|
||||
// NetworkResource is used to describe required network
|
||||
// resources of a given task.
|
||||
type NetworkResource struct {
|
||||
Mode string
|
||||
Device string
|
||||
CIDR string
|
||||
IP string
|
||||
MBits *int
|
||||
DNS *DNSConfig
|
||||
ReservedPorts []Port
|
||||
DynamicPorts []Port
|
||||
Mode string `hcl:"mode,optional"`
|
||||
Device string `hcl:"device,optional"`
|
||||
CIDR string `hcl:"cidr,optional"`
|
||||
IP string `hcl:"ip,optional"`
|
||||
MBits *int `hcl:"mbits,optional"`
|
||||
DNS *DNSConfig `hcl:"dns,block"`
|
||||
ReservedPorts []Port `hcl:"reserved_ports,block"`
|
||||
DynamicPorts []Port `hcl:"port,block"`
|
||||
}
|
||||
|
||||
func (n *NetworkResource) Canonicalize() {
|
||||
@@ -226,18 +226,18 @@ type RequestedDevice struct {
|
||||
// * "gpu"
|
||||
// * "nvidia/gpu"
|
||||
// * "nvidia/gpu/GTX2080Ti"
|
||||
Name string
|
||||
Name string `hcl:",label"`
|
||||
|
||||
// Count is the number of requested devices
|
||||
Count *uint64
|
||||
Count *uint64 `hcl:"count,optional"`
|
||||
|
||||
// Constraints are a set of constraints to apply when selecting the device
|
||||
// to use.
|
||||
Constraints []*Constraint
|
||||
Constraints []*Constraint `hcl:"constraint,block"`
|
||||
|
||||
// Affinities are a set of affinites to apply when selecting the device
|
||||
// to use.
|
||||
Affinities []*Affinity
|
||||
Affinities []*Affinity `hcl:"affinity,block"`
|
||||
}
|
||||
|
||||
func (d *RequestedDevice) Canonicalize() {
|
||||
|
||||
@@ -60,14 +60,19 @@ type ScalingRequest struct {
|
||||
|
||||
// ScalingPolicy is the user-specified API object for an autoscaling policy
|
||||
type ScalingPolicy struct {
|
||||
/* fields set by user in HCL config */
|
||||
|
||||
Min *int64 `hcl:"min,optional"`
|
||||
Max *int64 `hcl:"max,optional"`
|
||||
Policy map[string]interface{} `hcl:"policy,block"`
|
||||
Enabled *bool `hcl:"enabled,optional"`
|
||||
|
||||
/* fields set by server */
|
||||
|
||||
ID string
|
||||
Namespace string
|
||||
Type string
|
||||
Target map[string]string
|
||||
Min *int64
|
||||
Max *int64
|
||||
Policy map[string]interface{}
|
||||
Enabled *bool
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
171
api/services.go
171
api/services.go
@@ -8,9 +8,9 @@ import (
|
||||
// CheckRestart describes if and when a task should be restarted based on
|
||||
// failing health checks.
|
||||
type CheckRestart struct {
|
||||
Limit int `mapstructure:"limit"`
|
||||
Grace *time.Duration `mapstructure:"grace"`
|
||||
IgnoreWarnings bool `mapstructure:"ignore_warnings"`
|
||||
Limit int `mapstructure:"limit" hcl:"limit,optional"`
|
||||
Grace *time.Duration `mapstructure:"grace" hcl:"grace,optional"`
|
||||
IgnoreWarnings bool `mapstructure:"ignore_warnings" hcl:"ignore_warnings,optional"`
|
||||
}
|
||||
|
||||
// Canonicalize CheckRestart fields if not nil.
|
||||
@@ -73,46 +73,46 @@ func (c *CheckRestart) Merge(o *CheckRestart) *CheckRestart {
|
||||
// ServiceCheck represents the consul health check that Nomad registers.
|
||||
type ServiceCheck struct {
|
||||
//FIXME Id is unused. Remove?
|
||||
Id string
|
||||
Name string
|
||||
Type string
|
||||
Command string
|
||||
Args []string
|
||||
Path string
|
||||
Protocol string
|
||||
PortLabel string `mapstructure:"port"`
|
||||
Expose bool
|
||||
AddressMode string `mapstructure:"address_mode"`
|
||||
Interval time.Duration
|
||||
Timeout time.Duration
|
||||
InitialStatus string `mapstructure:"initial_status"`
|
||||
TLSSkipVerify bool `mapstructure:"tls_skip_verify"`
|
||||
Header map[string][]string
|
||||
Method string
|
||||
CheckRestart *CheckRestart `mapstructure:"check_restart"`
|
||||
GRPCService string `mapstructure:"grpc_service"`
|
||||
GRPCUseTLS bool `mapstructure:"grpc_use_tls"`
|
||||
TaskName string `mapstructure:"task"`
|
||||
SuccessBeforePassing int `mapstructure:"success_before_passing"`
|
||||
FailuresBeforeCritical int `mapstructure:"failures_before_critical"`
|
||||
Id string `hcl:"id,optional"`
|
||||
Name string `hcl:"name,optional"`
|
||||
Type string `hcl:"type,optional"`
|
||||
Command string `hcl:"command,optional"`
|
||||
Args []string `hcl:"args,optional"`
|
||||
Path string `hcl:"path,optional"`
|
||||
Protocol string `hcl:"protocol,optional"`
|
||||
PortLabel string `mapstructure:"port" hcl:"port,optional"`
|
||||
Expose bool `hcl:"expose,optional"`
|
||||
AddressMode string `mapstructure:"address_mode" hcl:"address_mode,optional"`
|
||||
Interval time.Duration `hcl:"interval,optional"`
|
||||
Timeout time.Duration `hcl:"timeout,optional"`
|
||||
InitialStatus string `mapstructure:"initial_status" hcl:"initial_status,optional"`
|
||||
TLSSkipVerify bool `mapstructure:"tls_skip_verify" hcl:"tls_skip_verify,optional"`
|
||||
Header map[string][]string `hcl:"header,block"`
|
||||
Method string `hcl:"method,optional"`
|
||||
CheckRestart *CheckRestart `mapstructure:"check_restart" hcl:"check_restart,block"`
|
||||
GRPCService string `mapstructure:"grpc_service" hcl:"grpc_service,optional"`
|
||||
GRPCUseTLS bool `mapstructure:"grpc_use_tls" hcl:"grpc_use_tls,optional"`
|
||||
TaskName string `mapstructure:"task" hcl:"task,optional"`
|
||||
SuccessBeforePassing int `mapstructure:"success_before_passing" hcl:"success_before_passing,optional"`
|
||||
FailuresBeforeCritical int `mapstructure:"failures_before_critical" hcl:"failures_before_critical,optional"`
|
||||
}
|
||||
|
||||
// Service represents a Consul service definition.
|
||||
type Service struct {
|
||||
//FIXME Id is unused. Remove?
|
||||
Id string
|
||||
Name string
|
||||
Tags []string
|
||||
CanaryTags []string `mapstructure:"canary_tags"`
|
||||
EnableTagOverride bool `mapstructure:"enable_tag_override"`
|
||||
PortLabel string `mapstructure:"port"`
|
||||
AddressMode string `mapstructure:"address_mode"`
|
||||
Checks []ServiceCheck
|
||||
CheckRestart *CheckRestart `mapstructure:"check_restart"`
|
||||
Connect *ConsulConnect
|
||||
Meta map[string]string
|
||||
CanaryMeta map[string]string
|
||||
TaskName string `mapstructure:"task"`
|
||||
Id string `hcl:"id,optional"`
|
||||
Name string `hcl:"name,optional"`
|
||||
Tags []string `hcl:"tags,optional"`
|
||||
CanaryTags []string `mapstructure:"canary_tags" hcl:"canary_tags,optional"`
|
||||
EnableTagOverride bool `mapstructure:"enable_tag_override" hcl:"enable_tag_override,optional"`
|
||||
PortLabel string `mapstructure:"port" hcl:"port,optional"`
|
||||
AddressMode string `mapstructure:"address_mode" hcl:"address_mode,optional"`
|
||||
Checks []ServiceCheck `hcl:"check,block"`
|
||||
CheckRestart *CheckRestart `mapstructure:"check_restart" hcl:"check_restart,block"`
|
||||
Connect *ConsulConnect `hcl:"connect,block"`
|
||||
Meta map[string]string `hcl:"meta,block"`
|
||||
CanaryMeta map[string]string `hcl:"canary_meta,block"`
|
||||
TaskName string `mapstructure:"task" hcl:"task,optional"`
|
||||
}
|
||||
|
||||
// Canonicalize the Service by ensuring its name and address mode are set. Task
|
||||
@@ -151,10 +151,10 @@ func (s *Service) Canonicalize(t *Task, tg *TaskGroup, job *Job) {
|
||||
|
||||
// ConsulConnect represents a Consul Connect jobspec stanza.
|
||||
type ConsulConnect struct {
|
||||
Native bool
|
||||
Gateway *ConsulGateway
|
||||
SidecarService *ConsulSidecarService `mapstructure:"sidecar_service"`
|
||||
SidecarTask *SidecarTask `mapstructure:"sidecar_task"`
|
||||
Native bool `hcl:"native,optional"`
|
||||
Gateway *ConsulGateway `hcl:"gateway,block"`
|
||||
SidecarService *ConsulSidecarService `mapstructure:"sidecar_service" hcl:"sidecar_service,block"`
|
||||
SidecarTask *SidecarTask `mapstructure:"sidecar_task" hcl:"sidecar_task,block"`
|
||||
}
|
||||
|
||||
func (cc *ConsulConnect) Canonicalize() {
|
||||
@@ -170,9 +170,9 @@ func (cc *ConsulConnect) Canonicalize() {
|
||||
// ConsulSidecarService represents a Consul Connect SidecarService jobspec
|
||||
// stanza.
|
||||
type ConsulSidecarService struct {
|
||||
Tags []string
|
||||
Port string
|
||||
Proxy *ConsulProxy
|
||||
Tags []string `hcl:"tags,optional"`
|
||||
Port string `hcl:"port,optional"`
|
||||
Proxy *ConsulProxy `hcl:"proxy,block"`
|
||||
}
|
||||
|
||||
func (css *ConsulSidecarService) Canonicalize() {
|
||||
@@ -190,17 +190,17 @@ func (css *ConsulSidecarService) Canonicalize() {
|
||||
// SidecarTask represents a subset of Task fields that can be set to override
|
||||
// the fields of the Task generated for the sidecar
|
||||
type SidecarTask struct {
|
||||
Name string
|
||||
Driver string
|
||||
User string
|
||||
Config map[string]interface{}
|
||||
Env map[string]string
|
||||
Resources *Resources
|
||||
Meta map[string]string
|
||||
KillTimeout *time.Duration `mapstructure:"kill_timeout"`
|
||||
LogConfig *LogConfig `mapstructure:"logs"`
|
||||
ShutdownDelay *time.Duration `mapstructure:"shutdown_delay"`
|
||||
KillSignal string `mapstructure:"kill_signal"`
|
||||
Name string `hcl:"name,optional"`
|
||||
Driver string `hcl:"driver,optional"`
|
||||
User string `hcl:"user,optional"`
|
||||
Config map[string]interface{} `hcl:"config,block"`
|
||||
Env map[string]string `hcl:"env,block"`
|
||||
Resources *Resources `hcl:"resources,block"`
|
||||
Meta map[string]string `hcl:"meta,block"`
|
||||
KillTimeout *time.Duration `mapstructure:"kill_timeout" hcl:"kill_timeout,optional"`
|
||||
LogConfig *LogConfig `mapstructure:"logs" hcl:"logs,block"`
|
||||
ShutdownDelay *time.Duration `mapstructure:"shutdown_delay" hcl:"shutdown_delay,optional"`
|
||||
KillSignal string `mapstructure:"kill_signal" hcl:"kill_signal,optional"`
|
||||
}
|
||||
|
||||
func (st *SidecarTask) Canonicalize() {
|
||||
@@ -243,11 +243,11 @@ func (st *SidecarTask) Canonicalize() {
|
||||
|
||||
// ConsulProxy represents a Consul Connect sidecar proxy jobspec stanza.
|
||||
type ConsulProxy struct {
|
||||
LocalServiceAddress string `mapstructure:"local_service_address"`
|
||||
LocalServicePort int `mapstructure:"local_service_port"`
|
||||
ExposeConfig *ConsulExposeConfig `mapstructure:"expose"`
|
||||
Upstreams []*ConsulUpstream
|
||||
Config map[string]interface{}
|
||||
LocalServiceAddress string `mapstructure:"local_service_address" hcl:"local_service_address,optional"`
|
||||
LocalServicePort int `mapstructure:"local_service_port" hcl:"local_service_port,optional"`
|
||||
ExposeConfig *ConsulExposeConfig `mapstructure:"expose" hcl:"expose,block"`
|
||||
Upstreams []*ConsulUpstream `hcl:"upstreams,block"`
|
||||
Config map[string]interface{} `hcl:"config,block"`
|
||||
}
|
||||
|
||||
func (cp *ConsulProxy) Canonicalize() {
|
||||
@@ -268,12 +268,12 @@ func (cp *ConsulProxy) Canonicalize() {
|
||||
|
||||
// ConsulUpstream represents a Consul Connect upstream jobspec stanza.
|
||||
type ConsulUpstream struct {
|
||||
DestinationName string `mapstructure:"destination_name"`
|
||||
LocalBindPort int `mapstructure:"local_bind_port"`
|
||||
DestinationName string `mapstructure:"destination_name" hcl:"destination_name,optional"`
|
||||
LocalBindPort int `mapstructure:"local_bind_port" hcl:"local_bind_port,optional"`
|
||||
}
|
||||
|
||||
type ConsulExposeConfig struct {
|
||||
Path []*ConsulExposePath `mapstructure:"path"`
|
||||
Path []*ConsulExposePath `mapstructure:"path" hcl:"path,block"`
|
||||
}
|
||||
|
||||
func (cec *ConsulExposeConfig) Canonicalize() {
|
||||
@@ -287,19 +287,19 @@ func (cec *ConsulExposeConfig) Canonicalize() {
|
||||
}
|
||||
|
||||
type ConsulExposePath struct {
|
||||
Path string
|
||||
Protocol string
|
||||
LocalPathPort int `mapstructure:"local_path_port"`
|
||||
ListenerPort string `mapstructure:"listener_port"`
|
||||
Path string `hcl:"path,optional"`
|
||||
Protocol string `hcl:"protocol,optional"`
|
||||
LocalPathPort int `mapstructure:"local_path_port" hcl:"local_path_port,optional"`
|
||||
ListenerPort string `mapstructure:"listener_port" hcl:"listener_port,optional"`
|
||||
}
|
||||
|
||||
// ConsulGateway is used to configure one of the Consul Connect Gateway types.
|
||||
type ConsulGateway struct {
|
||||
// Proxy is used to configure the Envoy instance acting as the gateway.
|
||||
Proxy *ConsulGatewayProxy
|
||||
Proxy *ConsulGatewayProxy `hcl:"proxy,block"`
|
||||
|
||||
// Ingress represents the Consul Configuration Entry for an Ingress Gateway.
|
||||
Ingress *ConsulIngressConfigEntry
|
||||
Ingress *ConsulIngressConfigEntry `hcl:"ingress,block"`
|
||||
|
||||
// Terminating is not yet supported.
|
||||
// Terminating *ConsulTerminatingConfigEntry
|
||||
@@ -328,8 +328,9 @@ func (g *ConsulGateway) Copy() *ConsulGateway {
|
||||
}
|
||||
|
||||
type ConsulGatewayBindAddress struct {
|
||||
Address string `mapstructure:"address"`
|
||||
Port int `mapstructure:"port"`
|
||||
Name string `hcl:",label"`
|
||||
Address string `mapstructure:"address" hcl:"address,optional"`
|
||||
Port int `mapstructure:"port" hcl:"port,optional"`
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -341,11 +342,11 @@ var (
|
||||
//
|
||||
// https://www.consul.io/docs/connect/proxies/envoy#gateway-options
|
||||
type ConsulGatewayProxy struct {
|
||||
ConnectTimeout *time.Duration `mapstructure:"connect_timeout"`
|
||||
EnvoyGatewayBindTaggedAddresses bool `mapstructure:"envoy_gateway_bind_tagged_addresses"`
|
||||
EnvoyGatewayBindAddresses map[string]*ConsulGatewayBindAddress `mapstructure:"envoy_gateway_bind_addresses"`
|
||||
EnvoyGatewayNoDefaultBind bool `mapstructure:"envoy_gateway_no_default_bind"`
|
||||
Config map[string]interface{} // escape hatch envoy config
|
||||
ConnectTimeout *time.Duration `mapstructure:"connect_timeout" hcl:"connect_timeout,optional"`
|
||||
EnvoyGatewayBindTaggedAddresses bool `mapstructure:"envoy_gateway_bind_tagged_addresses" hcl:"envoy_gateway_bind_tagged_addresses,optional"`
|
||||
EnvoyGatewayBindAddresses map[string]*ConsulGatewayBindAddress `mapstructure:"envoy_gateway_bind_addresses" hcl:"envoy_gateway_bind_addresses,block"`
|
||||
EnvoyGatewayNoDefaultBind bool `mapstructure:"envoy_gateway_no_default_bind" hcl:"envoy_gateway_no_default_bind,optional"`
|
||||
Config map[string]interface{} `hcl:"config,block"` // escape hatch envoy config
|
||||
}
|
||||
|
||||
func (p *ConsulGatewayProxy) Canonicalize() {
|
||||
@@ -399,7 +400,7 @@ func (p *ConsulGatewayProxy) Copy() *ConsulGatewayProxy {
|
||||
|
||||
// ConsulGatewayTLSConfig is used to configure TLS for a gateway.
|
||||
type ConsulGatewayTLSConfig struct {
|
||||
Enabled bool
|
||||
Enabled bool `hcl:"enabled,optional"`
|
||||
}
|
||||
|
||||
func (tc *ConsulGatewayTLSConfig) Canonicalize() {
|
||||
@@ -419,9 +420,9 @@ func (tc *ConsulGatewayTLSConfig) Copy() *ConsulGatewayTLSConfig {
|
||||
type ConsulIngressService struct {
|
||||
// Namespace is not yet supported.
|
||||
// Namespace string
|
||||
Name string
|
||||
Name string `hcl:"name,optional"`
|
||||
|
||||
Hosts []string
|
||||
Hosts []string `hcl:"hosts,optional"`
|
||||
}
|
||||
|
||||
func (s *ConsulIngressService) Canonicalize() {
|
||||
@@ -458,9 +459,9 @@ const (
|
||||
// ConsulIngressListener is used to configure a listener on a Consul Ingress
|
||||
// Gateway.
|
||||
type ConsulIngressListener struct {
|
||||
Port int
|
||||
Protocol string
|
||||
Services []*ConsulIngressService
|
||||
Port int `hcl:"port,optional"`
|
||||
Protocol string `hcl:"protocol,optional"`
|
||||
Services []*ConsulIngressService `hcl:"service,block"`
|
||||
}
|
||||
|
||||
func (l *ConsulIngressListener) Canonicalize() {
|
||||
@@ -506,8 +507,8 @@ type ConsulIngressConfigEntry struct {
|
||||
// Namespace is not yet supported.
|
||||
// Namespace string
|
||||
|
||||
TLS *ConsulGatewayTLSConfig
|
||||
Listeners []*ConsulIngressListener
|
||||
TLS *ConsulGatewayTLSConfig `hcl:"tls,block"`
|
||||
Listeners []*ConsulIngressListener `hcl:"listener,block"`
|
||||
}
|
||||
|
||||
func (e *ConsulIngressConfigEntry) Canonicalize() {
|
||||
|
||||
214
api/tasks.go
214
api/tasks.go
@@ -67,10 +67,10 @@ type AllocResourceUsage struct {
|
||||
// RestartPolicy defines how the Nomad client restarts
|
||||
// tasks in a taskgroup when they fail
|
||||
type RestartPolicy struct {
|
||||
Interval *time.Duration
|
||||
Attempts *int
|
||||
Delay *time.Duration
|
||||
Mode *string
|
||||
Interval *time.Duration `hcl:"interval,optional"`
|
||||
Attempts *int `hcl:"attempts,optional"`
|
||||
Delay *time.Duration `hcl:"delay,optional"`
|
||||
Mode *string `hcl:"mode,optional"`
|
||||
}
|
||||
|
||||
func (r *RestartPolicy) Merge(rp *RestartPolicy) {
|
||||
@@ -91,24 +91,24 @@ func (r *RestartPolicy) Merge(rp *RestartPolicy) {
|
||||
// Reschedule configures how Tasks are rescheduled when they crash or fail.
|
||||
type ReschedulePolicy struct {
|
||||
// Attempts limits the number of rescheduling attempts that can occur in an interval.
|
||||
Attempts *int `mapstructure:"attempts"`
|
||||
Attempts *int `mapstructure:"attempts" hcl:"attempts,optional"`
|
||||
|
||||
// Interval is a duration in which we can limit the number of reschedule attempts.
|
||||
Interval *time.Duration `mapstructure:"interval"`
|
||||
Interval *time.Duration `mapstructure:"interval" hcl:"interval,optional"`
|
||||
|
||||
// Delay is a minimum duration to wait between reschedule attempts.
|
||||
// The delay function determines how much subsequent reschedule attempts are delayed by.
|
||||
Delay *time.Duration `mapstructure:"delay"`
|
||||
Delay *time.Duration `mapstructure:"delay" hcl:"delay,optional"`
|
||||
|
||||
// DelayFunction determines how the delay progressively changes on subsequent reschedule
|
||||
// attempts. Valid values are "exponential", "constant", and "fibonacci".
|
||||
DelayFunction *string `mapstructure:"delay_function"`
|
||||
DelayFunction *string `mapstructure:"delay_function" hcl:"delay_function,optional"`
|
||||
|
||||
// MaxDelay is an upper bound on the delay.
|
||||
MaxDelay *time.Duration `mapstructure:"max_delay"`
|
||||
MaxDelay *time.Duration `mapstructure:"max_delay" hcl:"max_delay,optional"`
|
||||
|
||||
// Unlimited allows rescheduling attempts until they succeed
|
||||
Unlimited *bool `mapstructure:"unlimited"`
|
||||
Unlimited *bool `mapstructure:"unlimited" hcl:"unlimited,optional"`
|
||||
}
|
||||
|
||||
func (r *ReschedulePolicy) Merge(rp *ReschedulePolicy) {
|
||||
@@ -159,10 +159,10 @@ func (r *ReschedulePolicy) Canonicalize(jobType string) {
|
||||
|
||||
// Affinity is used to serialize task group affinities
|
||||
type Affinity struct {
|
||||
LTarget string // Left-hand target
|
||||
RTarget string // Right-hand target
|
||||
Operand string // Constraint operand (<=, <, =, !=, >, >=), set_contains_all, set_contains_any
|
||||
Weight *int8 // Weight applied to nodes that match the affinity. Can be negative
|
||||
LTarget string `hcl:"attribute,optional"` // Left-hand target
|
||||
RTarget string `hcl:"value,optional"` // Right-hand target
|
||||
Operand string `hcl:"operator,optional"` // Constraint operand (<=, <, =, !=, >, >=), set_contains_all, set_contains_any
|
||||
Weight *int8 `hcl:"weight,optional"` // Weight applied to nodes that match the affinity. Can be negative
|
||||
}
|
||||
|
||||
func NewAffinity(LTarget string, Operand string, RTarget string, Weight int8) *Affinity {
|
||||
@@ -255,15 +255,15 @@ func (p *ReschedulePolicy) String() string {
|
||||
|
||||
// Spread is used to serialize task group allocation spread preferences
|
||||
type Spread struct {
|
||||
Attribute string
|
||||
Weight *int8
|
||||
SpreadTarget []*SpreadTarget
|
||||
Attribute string `hcl:"attribute,optional"`
|
||||
Weight *int8 `hcl:"weight,optional"`
|
||||
SpreadTarget []*SpreadTarget `hcl:"target,block"`
|
||||
}
|
||||
|
||||
// SpreadTarget is used to serialize target allocation spread percentages
|
||||
type SpreadTarget struct {
|
||||
Value string
|
||||
Percent uint8
|
||||
Value string `hcl:",label"`
|
||||
Percent uint8 `hcl:"percent,optional"`
|
||||
}
|
||||
|
||||
func NewSpreadTarget(value string, percent uint8) *SpreadTarget {
|
||||
@@ -289,9 +289,9 @@ func (s *Spread) Canonicalize() {
|
||||
|
||||
// EphemeralDisk is an ephemeral disk object
|
||||
type EphemeralDisk struct {
|
||||
Sticky *bool
|
||||
Migrate *bool
|
||||
SizeMB *int `mapstructure:"size"`
|
||||
Sticky *bool `hcl:"sticky,optional"`
|
||||
Migrate *bool `hcl:"migrate,optional"`
|
||||
SizeMB *int `mapstructure:"size" hcl:"size,optional"`
|
||||
}
|
||||
|
||||
func DefaultEphemeralDisk() *EphemeralDisk {
|
||||
@@ -317,10 +317,10 @@ func (e *EphemeralDisk) Canonicalize() {
|
||||
// MigrateStrategy describes how allocations for a task group should be
|
||||
// migrated between nodes (eg when draining).
|
||||
type MigrateStrategy struct {
|
||||
MaxParallel *int `mapstructure:"max_parallel"`
|
||||
HealthCheck *string `mapstructure:"health_check"`
|
||||
MinHealthyTime *time.Duration `mapstructure:"min_healthy_time"`
|
||||
HealthyDeadline *time.Duration `mapstructure:"healthy_deadline"`
|
||||
MaxParallel *int `mapstructure:"max_parallel" hcl:"max_parallel,optional"`
|
||||
HealthCheck *string `mapstructure:"health_check" hcl:"health_check,optional"`
|
||||
MinHealthyTime *time.Duration `mapstructure:"min_healthy_time" hcl:"min_healthy_time,optional"`
|
||||
HealthyDeadline *time.Duration `mapstructure:"healthy_deadline" hcl:"healthy_deadline,optional"`
|
||||
}
|
||||
|
||||
func DefaultMigrateStrategy() *MigrateStrategy {
|
||||
@@ -377,12 +377,12 @@ func (m *MigrateStrategy) Copy() *MigrateStrategy {
|
||||
|
||||
// VolumeRequest is a representation of a storage volume that a TaskGroup wishes to use.
|
||||
type VolumeRequest struct {
|
||||
Name string
|
||||
Type string
|
||||
Source string
|
||||
ReadOnly bool `hcl:"read_only"`
|
||||
MountOptions *CSIMountOptions `hcl:"mount_options"`
|
||||
ExtraKeysHCL []string `hcl:",unusedKeys" json:"-"`
|
||||
Name string `hcl:"name,label"`
|
||||
Type string `hcl:"type,optional"`
|
||||
Source string `hcl:"source,optional"`
|
||||
ReadOnly bool `hcl:"read_only,optional"`
|
||||
MountOptions *CSIMountOptions `hcl:"mount_options,block"`
|
||||
ExtraKeysHCL []string `hcl1:",unusedKeys,optional" json:"-"`
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -394,10 +394,10 @@ const (
|
||||
// VolumeMount represents the relationship between a destination path in a task
|
||||
// and the task group volume that should be mounted there.
|
||||
type VolumeMount struct {
|
||||
Volume *string
|
||||
Destination *string
|
||||
ReadOnly *bool `mapstructure:"read_only"`
|
||||
PropagationMode *string `mapstructure:"propagation_mode"`
|
||||
Volume *string `hcl:"volume,optional"`
|
||||
Destination *string `hcl:"destination,optional"`
|
||||
ReadOnly *bool `mapstructure:"read_only" hcl:"read_only,optional"`
|
||||
PropagationMode *string `mapstructure:"propagation_mode" hcl:"propagation_mode,optional"`
|
||||
}
|
||||
|
||||
func (vm *VolumeMount) Canonicalize() {
|
||||
@@ -411,24 +411,24 @@ func (vm *VolumeMount) Canonicalize() {
|
||||
|
||||
// TaskGroup is the unit of scheduling.
|
||||
type TaskGroup struct {
|
||||
Name *string
|
||||
Count *int
|
||||
Constraints []*Constraint
|
||||
Affinities []*Affinity
|
||||
Tasks []*Task
|
||||
Spreads []*Spread
|
||||
Volumes map[string]*VolumeRequest
|
||||
RestartPolicy *RestartPolicy
|
||||
ReschedulePolicy *ReschedulePolicy
|
||||
EphemeralDisk *EphemeralDisk
|
||||
Update *UpdateStrategy
|
||||
Migrate *MigrateStrategy
|
||||
Networks []*NetworkResource
|
||||
Meta map[string]string
|
||||
Services []*Service
|
||||
ShutdownDelay *time.Duration `mapstructure:"shutdown_delay"`
|
||||
StopAfterClientDisconnect *time.Duration `mapstructure:"stop_after_client_disconnect"`
|
||||
Scaling *ScalingPolicy
|
||||
Name *string `hcl:"name,label"`
|
||||
Count *int `hcl:"count,optional"`
|
||||
Constraints []*Constraint `hcl:"constraint,block"`
|
||||
Affinities []*Affinity `hcl:"affinity,block"`
|
||||
Tasks []*Task `hcl:"task,block"`
|
||||
Spreads []*Spread `hcl:"spread,block"`
|
||||
Volumes map[string]*VolumeRequest `hcl:"volume,block"`
|
||||
RestartPolicy *RestartPolicy `hcl:"restart,block"`
|
||||
ReschedulePolicy *ReschedulePolicy `hcl:"reschedule,block"`
|
||||
EphemeralDisk *EphemeralDisk `hcl:"ephemeral_disk,block"`
|
||||
Update *UpdateStrategy `hcl:"update,block"`
|
||||
Migrate *MigrateStrategy `hcl:"migrate,block"`
|
||||
Networks []*NetworkResource `hcl:"network,block"`
|
||||
Meta map[string]string `hcl:"meta,block"`
|
||||
Services []*Service `hcl:"service,block"`
|
||||
ShutdownDelay *time.Duration `mapstructure:"shutdown_delay" hcl:"shutdown_delay,optional"`
|
||||
StopAfterClientDisconnect *time.Duration `mapstructure:"stop_after_client_disconnect" hcl:"stop_after_client_disconnect,optional"`
|
||||
Scaling *ScalingPolicy `hcl:"scaling,block"`
|
||||
}
|
||||
|
||||
// NewTaskGroup creates a new TaskGroup.
|
||||
@@ -605,8 +605,8 @@ func (g *TaskGroup) AddSpread(s *Spread) *TaskGroup {
|
||||
|
||||
// LogConfig provides configuration for log rotation
|
||||
type LogConfig struct {
|
||||
MaxFiles *int `mapstructure:"max_files"`
|
||||
MaxFileSizeMB *int `mapstructure:"max_file_size"`
|
||||
MaxFiles *int `mapstructure:"max_files" hcl:"max_files,optional"`
|
||||
MaxFileSizeMB *int `mapstructure:"max_file_size" hcl:"max_file_size,optional"`
|
||||
}
|
||||
|
||||
func DefaultLogConfig() *LogConfig {
|
||||
@@ -627,17 +627,17 @@ func (l *LogConfig) Canonicalize() {
|
||||
|
||||
// DispatchPayloadConfig configures how a task gets its input from a job dispatch
|
||||
type DispatchPayloadConfig struct {
|
||||
File string
|
||||
File string `hcl:"file,optional"`
|
||||
}
|
||||
|
||||
const (
|
||||
TaskLifecycleHookPrestart = "prestart"
|
||||
TaskLifecycleHookPrestart = "prestart"
|
||||
TaskLifecycleHookPoststart = "poststart"
|
||||
)
|
||||
|
||||
type TaskLifecycle struct {
|
||||
Hook string `mapstructure:"hook"`
|
||||
Sidecar bool `mapstructure:"sidecar"`
|
||||
Hook string `mapstructure:"hook" hcl:"hook,optional"`
|
||||
Sidecar bool `mapstructure:"sidecar" hcl:"sidecar,optional"`
|
||||
}
|
||||
|
||||
// Determine if lifecycle has user-input values
|
||||
@@ -647,30 +647,30 @@ func (l *TaskLifecycle) Empty() bool {
|
||||
|
||||
// Task is a single process in a task group.
|
||||
type Task struct {
|
||||
Name string
|
||||
Driver string
|
||||
User string
|
||||
Lifecycle *TaskLifecycle
|
||||
Config map[string]interface{}
|
||||
Constraints []*Constraint
|
||||
Affinities []*Affinity
|
||||
Env map[string]string
|
||||
Services []*Service
|
||||
Resources *Resources
|
||||
RestartPolicy *RestartPolicy
|
||||
Meta map[string]string
|
||||
KillTimeout *time.Duration `mapstructure:"kill_timeout"`
|
||||
LogConfig *LogConfig `mapstructure:"logs"`
|
||||
Artifacts []*TaskArtifact
|
||||
Vault *Vault
|
||||
Templates []*Template
|
||||
DispatchPayload *DispatchPayloadConfig
|
||||
VolumeMounts []*VolumeMount
|
||||
CSIPluginConfig *TaskCSIPluginConfig `mapstructure:"csi_plugin" json:",omitempty"`
|
||||
Leader bool
|
||||
ShutdownDelay time.Duration `mapstructure:"shutdown_delay"`
|
||||
KillSignal string `mapstructure:"kill_signal"`
|
||||
Kind string
|
||||
Name string `hcl:"name,label"`
|
||||
Driver string `hcl:"driver,optional"`
|
||||
User string `hcl:"user,optional"`
|
||||
Lifecycle *TaskLifecycle `hcl:"lifecycle,block"`
|
||||
Config map[string]interface{} `hcl:"config,block"`
|
||||
Constraints []*Constraint `hcl:"constraint,block"`
|
||||
Affinities []*Affinity `hcl:"affinity,block"`
|
||||
Env map[string]string `hcl:"env,block"`
|
||||
Services []*Service `hcl:"service,block"`
|
||||
Resources *Resources `hcl:"resources,block"`
|
||||
RestartPolicy *RestartPolicy `hcl:"restart,block"`
|
||||
Meta map[string]string `hcl:"meta,block"`
|
||||
KillTimeout *time.Duration `mapstructure:"kill_timeout" hcl:"kill_timeout,optional"`
|
||||
LogConfig *LogConfig `mapstructure:"logs" hcl:"logs,block"`
|
||||
Artifacts []*TaskArtifact `hcl:"artifact,block"`
|
||||
Vault *Vault `hcl:"vault,block"`
|
||||
Templates []*Template `hcl:"template,block"`
|
||||
DispatchPayload *DispatchPayloadConfig `hcl:"dispatch_payload,block"`
|
||||
VolumeMounts []*VolumeMount `hcl:"volume_mount,block"`
|
||||
CSIPluginConfig *TaskCSIPluginConfig `mapstructure:"csi_plugin" json:",omitempty" hcl:"csi_plugin,block"`
|
||||
Leader bool `hcl:"leader,optional"`
|
||||
ShutdownDelay time.Duration `mapstructure:"shutdown_delay" hcl:"shutdown_delay,optional"`
|
||||
KillSignal string `mapstructure:"kill_signal" hcl:"kill_signal,optional"`
|
||||
Kind string `hcl:"kind,optional"`
|
||||
}
|
||||
|
||||
func (t *Task) Canonicalize(tg *TaskGroup, job *Job) {
|
||||
@@ -723,10 +723,10 @@ func (t *Task) Canonicalize(tg *TaskGroup, job *Job) {
|
||||
|
||||
// TaskArtifact is used to download artifacts before running a task.
|
||||
type TaskArtifact struct {
|
||||
GetterSource *string `mapstructure:"source"`
|
||||
GetterOptions map[string]string `mapstructure:"options"`
|
||||
GetterMode *string `mapstructure:"mode"`
|
||||
RelativeDest *string `mapstructure:"destination"`
|
||||
GetterSource *string `mapstructure:"source" hcl:"source,optional"`
|
||||
GetterOptions map[string]string `mapstructure:"options" hcl:"options,block"`
|
||||
GetterMode *string `mapstructure:"mode" hcl:"mode,optional"`
|
||||
RelativeDest *string `mapstructure:"destination" hcl:"destination,optional"`
|
||||
}
|
||||
|
||||
func (a *TaskArtifact) Canonicalize() {
|
||||
@@ -753,17 +753,17 @@ func (a *TaskArtifact) Canonicalize() {
|
||||
}
|
||||
|
||||
type Template struct {
|
||||
SourcePath *string `mapstructure:"source"`
|
||||
DestPath *string `mapstructure:"destination"`
|
||||
EmbeddedTmpl *string `mapstructure:"data"`
|
||||
ChangeMode *string `mapstructure:"change_mode"`
|
||||
ChangeSignal *string `mapstructure:"change_signal"`
|
||||
Splay *time.Duration `mapstructure:"splay"`
|
||||
Perms *string `mapstructure:"perms"`
|
||||
LeftDelim *string `mapstructure:"left_delimiter"`
|
||||
RightDelim *string `mapstructure:"right_delimiter"`
|
||||
Envvars *bool `mapstructure:"env"`
|
||||
VaultGrace *time.Duration `mapstructure:"vault_grace"`
|
||||
SourcePath *string `mapstructure:"source" hcl:"source,optional"`
|
||||
DestPath *string `mapstructure:"destination" hcl:"destination,optional"`
|
||||
EmbeddedTmpl *string `mapstructure:"data" hcl:"data,optional"`
|
||||
ChangeMode *string `mapstructure:"change_mode" hcl:"change_mode,optional"`
|
||||
ChangeSignal *string `mapstructure:"change_signal" hcl:"change_signal,optional"`
|
||||
Splay *time.Duration `mapstructure:"splay" hcl:"splay,optional"`
|
||||
Perms *string `mapstructure:"perms" hcl:"perms,optional"`
|
||||
LeftDelim *string `mapstructure:"left_delimiter" hcl:"left_delimiter,optional"`
|
||||
RightDelim *string `mapstructure:"right_delimiter" hcl:"right_delimiter,optional"`
|
||||
Envvars *bool `mapstructure:"env" hcl:"env,optional"`
|
||||
VaultGrace *time.Duration `mapstructure:"vault_grace" hcl:"vault_grace,optional"`
|
||||
}
|
||||
|
||||
func (tmpl *Template) Canonicalize() {
|
||||
@@ -812,11 +812,11 @@ func (tmpl *Template) Canonicalize() {
|
||||
}
|
||||
|
||||
type Vault struct {
|
||||
Policies []string
|
||||
Namespace *string `mapstructure:"namespace"`
|
||||
Env *bool
|
||||
ChangeMode *string `mapstructure:"change_mode"`
|
||||
ChangeSignal *string `mapstructure:"change_signal"`
|
||||
Policies []string `hcl:"policies,optional"`
|
||||
Namespace *string `mapstructure:"namespace" hcl:"namespace,optional"`
|
||||
Env *bool `hcl:"env,optional"`
|
||||
ChangeMode *string `mapstructure:"change_mode" hcl:"change_mode,optional"`
|
||||
ChangeSignal *string `mapstructure:"change_signal" hcl:"change_signal,optional"`
|
||||
}
|
||||
|
||||
func (v *Vault) Canonicalize() {
|
||||
@@ -975,10 +975,10 @@ const (
|
||||
type TaskCSIPluginConfig struct {
|
||||
// ID is the identifier of the plugin.
|
||||
// Ideally this should be the FQDN of the plugin.
|
||||
ID string `mapstructure:"id"`
|
||||
ID string `mapstructure:"id" hcl:"id,optional"`
|
||||
|
||||
// CSIPluginType instructs Nomad on how to handle processing a plugin
|
||||
Type CSIPluginType `mapstructure:"type"`
|
||||
Type CSIPluginType `mapstructure:"type" hcl:"type,optional"`
|
||||
|
||||
// MountDir is the destination that nomad should mount in its CSI
|
||||
// directory for the plugin. It will then expect a file called CSISocketName
|
||||
@@ -986,7 +986,7 @@ type TaskCSIPluginConfig struct {
|
||||
// "MountDir/CSIIntermediaryDirname/VolumeName/AllocID for mounts.
|
||||
//
|
||||
// Default is /csi.
|
||||
MountDir string `mapstructure:"mount_dir"`
|
||||
MountDir string `mapstructure:"mount_dir" hcl:"mount_dir,optional"`
|
||||
}
|
||||
|
||||
func (t *TaskCSIPluginConfig) Canonicalize() {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/hashicorp/nomad/api"
|
||||
"github.com/hashicorp/nomad/helper"
|
||||
"github.com/hashicorp/nomad/jobspec"
|
||||
"github.com/hashicorp/nomad/jobspec2"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
)
|
||||
|
||||
@@ -675,7 +676,14 @@ func (s *HTTPServer) JobsParseRequest(resp http.ResponseWriter, req *http.Reques
|
||||
}
|
||||
|
||||
jobfile := strings.NewReader(args.JobHCL)
|
||||
jobStruct, err := jobspec.Parse(jobfile)
|
||||
|
||||
var jobStruct *api.Job
|
||||
var err error
|
||||
if args.HCLv1 {
|
||||
jobStruct, err = jobspec.Parse(jobfile)
|
||||
} else {
|
||||
jobStruct, err = jobspec2.ParseWithArgs("input.hcl", jobfile, nil, false)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, CodedError(400, err.Error())
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
gg "github.com/hashicorp/go-getter"
|
||||
"github.com/hashicorp/nomad/api"
|
||||
"github.com/hashicorp/nomad/jobspec"
|
||||
"github.com/hashicorp/nomad/jobspec2"
|
||||
"github.com/kr/text"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/posener/complete"
|
||||
@@ -378,13 +380,20 @@ READ:
|
||||
}
|
||||
|
||||
type JobGetter struct {
|
||||
hcl1 bool
|
||||
|
||||
// The fields below can be overwritten for tests
|
||||
testStdin io.Reader
|
||||
}
|
||||
|
||||
// StructJob returns the Job struct from jobfile.
|
||||
func (j *JobGetter) ApiJob(jpath string) (*api.Job, error) {
|
||||
return j.ApiJobWithArgs(jpath, nil)
|
||||
}
|
||||
|
||||
func (j *JobGetter) ApiJobWithArgs(jpath string, vars map[string]string) (*api.Job, error) {
|
||||
var jobfile io.Reader
|
||||
pathName := filepath.Base(jpath)
|
||||
switch jpath {
|
||||
case "-":
|
||||
if j.testStdin != nil {
|
||||
@@ -392,6 +401,7 @@ func (j *JobGetter) ApiJob(jpath string) (*api.Job, error) {
|
||||
} else {
|
||||
jobfile = os.Stdin
|
||||
}
|
||||
pathName = "stdin.hcl"
|
||||
default:
|
||||
if len(jpath) == 0 {
|
||||
return nil, fmt.Errorf("Error jobfile path has to be specified.")
|
||||
@@ -432,9 +442,15 @@ func (j *JobGetter) ApiJob(jpath string) (*api.Job, error) {
|
||||
}
|
||||
|
||||
// Parse the JobFile
|
||||
jobStruct, err := jobspec.Parse(jobfile)
|
||||
var jobStruct *api.Job
|
||||
var err error
|
||||
if j.hcl1 {
|
||||
jobStruct, err = jobspec.Parse(jobfile)
|
||||
} else {
|
||||
jobStruct, err = jobspec2.ParseWithArgs(pathName, jobfile, vars, true)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error parsing job file from %s: %v", jpath, err)
|
||||
return nil, fmt.Errorf("Error parsing job file from %s:\n%v", jpath, err)
|
||||
}
|
||||
|
||||
return jobStruct, nil
|
||||
@@ -507,3 +523,25 @@ func (w *uiErrorWriter) Close() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseVars decodes a slice of `<key>=<val>` or `<key>` strings into a golang map.
|
||||
//
|
||||
// `<key>` without corresponding value, is mapped to the `<key>` environment variable.
|
||||
func parseVars(vars []string) map[string]string {
|
||||
if len(vars) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := make(map[string]string, len(vars))
|
||||
for _, v := range vars {
|
||||
parts := strings.SplitN(v, "=", 2)
|
||||
k := parts[0]
|
||||
if len(parts) == 2 {
|
||||
result[k] = parts[1]
|
||||
} else {
|
||||
result[k] = os.Getenv(k)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -209,20 +209,20 @@ func TestHelpers_LineLimitReader_TimeLimit(t *testing.T) {
|
||||
|
||||
const (
|
||||
job = `job "job1" {
|
||||
type = "service"
|
||||
datacenters = [ "dc1" ]
|
||||
group "group1" {
|
||||
count = 1
|
||||
task "task1" {
|
||||
driver = "exec"
|
||||
resources = {}
|
||||
}
|
||||
restart{
|
||||
attempts = 10
|
||||
mode = "delay"
|
||||
interval = "15s"
|
||||
}
|
||||
}
|
||||
type = "service"
|
||||
datacenters = ["dc1"]
|
||||
group "group1" {
|
||||
count = 1
|
||||
task "task1" {
|
||||
driver = "exec"
|
||||
resources {}
|
||||
}
|
||||
restart {
|
||||
attempts = 10
|
||||
mode = "delay"
|
||||
interval = "15s"
|
||||
}
|
||||
}
|
||||
}`
|
||||
)
|
||||
|
||||
@@ -392,3 +392,14 @@ func TestUiErrorWriter(t *testing.T) {
|
||||
expectedErr += "and thensome more\n"
|
||||
require.Equal(t, expectedErr, errBuf.String())
|
||||
}
|
||||
|
||||
func TestParseVars(t *testing.T) {
|
||||
input := []string{"key1=val1", "HOME", "key2=321"}
|
||||
expected := map[string]string{
|
||||
"key1": "val1",
|
||||
"HOME": os.Getenv("HOME"),
|
||||
"key2": "321",
|
||||
}
|
||||
|
||||
require.Equal(t, expected, parseVars(input))
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
cflags "github.com/hashicorp/consul/command/flags"
|
||||
"github.com/hashicorp/nomad/api"
|
||||
"github.com/hashicorp/nomad/scheduler"
|
||||
"github.com/posener/complete"
|
||||
@@ -72,6 +73,9 @@ Plan Options:
|
||||
Determines whether the diff between the remote job and planned job is shown.
|
||||
Defaults to true.
|
||||
|
||||
-hcl1
|
||||
Parses the job file as HCLv1.
|
||||
|
||||
-policy-override
|
||||
Sets the flag to force override any soft mandatory Sentinel policies.
|
||||
|
||||
@@ -91,6 +95,8 @@ func (c *JobPlanCommand) AutocompleteFlags() complete.Flags {
|
||||
"-diff": complete.PredictNothing,
|
||||
"-policy-override": complete.PredictNothing,
|
||||
"-verbose": complete.PredictNothing,
|
||||
"-hcl1": complete.PredictNothing,
|
||||
"-var": complete.PredictAnything,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -101,12 +107,15 @@ func (c *JobPlanCommand) AutocompleteArgs() complete.Predictor {
|
||||
func (c *JobPlanCommand) Name() string { return "job plan" }
|
||||
func (c *JobPlanCommand) Run(args []string) int {
|
||||
var diff, policyOverride, verbose bool
|
||||
var varArgs cflags.AppendSliceValue
|
||||
|
||||
flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
|
||||
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||
flags.BoolVar(&diff, "diff", true, "")
|
||||
flags.BoolVar(&policyOverride, "policy-override", false, "")
|
||||
flags.BoolVar(&verbose, "verbose", false, "")
|
||||
flags.BoolVar(&c.JobGetter.hcl1, "hcl1", false, "")
|
||||
flags.Var(&varArgs, "var", "")
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 255
|
||||
@@ -122,7 +131,7 @@ func (c *JobPlanCommand) Run(args []string) int {
|
||||
|
||||
path := args[0]
|
||||
// Get Job struct from Jobfile
|
||||
job, err := c.JobGetter.ApiJob(args[0])
|
||||
job, err := c.JobGetter.ApiJobWithArgs(args[0], parseVars(varArgs))
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error getting job struct: %s", err))
|
||||
return 255
|
||||
|
||||
@@ -94,7 +94,7 @@ job "job1" {
|
||||
count = 1
|
||||
task "task1" {
|
||||
driver = "exec"
|
||||
resources = {
|
||||
resources {
|
||||
cpu = 1000
|
||||
memory = 512
|
||||
}
|
||||
@@ -134,7 +134,7 @@ job "job1" {
|
||||
count = 1
|
||||
task "task1" {
|
||||
driver = "exec"
|
||||
resources = {
|
||||
resources {
|
||||
cpu = 1000
|
||||
memory = 512
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
cflags "github.com/hashicorp/consul/command/flags"
|
||||
"github.com/hashicorp/nomad/api"
|
||||
"github.com/hashicorp/nomad/helper"
|
||||
"github.com/posener/complete"
|
||||
@@ -79,6 +80,9 @@ Run Options:
|
||||
the evaluation ID will be printed to the screen, which can be used to
|
||||
examine the evaluation using the eval-status command.
|
||||
|
||||
-hcl1
|
||||
Parses the job file as HCLv1.
|
||||
|
||||
-output
|
||||
Output the JSON that would be submitted to the HTTP API without submitting
|
||||
the job.
|
||||
@@ -88,7 +92,7 @@ Run Options:
|
||||
|
||||
-preserve-counts
|
||||
If set, the existing task group counts will be preserved when updating a job.
|
||||
|
||||
|
||||
-consul-token
|
||||
If set, the passed Consul token is stored in the job before sending to the
|
||||
Nomad servers. This allows passing the Consul token without storing it in
|
||||
@@ -127,6 +131,8 @@ func (c *JobRunCommand) AutocompleteFlags() complete.Flags {
|
||||
"-output": complete.PredictNothing,
|
||||
"-policy-override": complete.PredictNothing,
|
||||
"-preserve-counts": complete.PredictNothing,
|
||||
"-hcl1": complete.PredictNothing,
|
||||
"-var": complete.PredictAnything,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -139,6 +145,7 @@ func (c *JobRunCommand) Name() string { return "job run" }
|
||||
func (c *JobRunCommand) Run(args []string) int {
|
||||
var detach, verbose, output, override, preserveCounts bool
|
||||
var checkIndexStr, consulToken, vaultToken, vaultNamespace string
|
||||
var varArgs cflags.AppendSliceValue
|
||||
|
||||
flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
|
||||
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||
@@ -147,10 +154,12 @@ func (c *JobRunCommand) Run(args []string) int {
|
||||
flags.BoolVar(&output, "output", false, "")
|
||||
flags.BoolVar(&override, "policy-override", false, "")
|
||||
flags.BoolVar(&preserveCounts, "preserve-counts", false, "")
|
||||
flags.BoolVar(&c.JobGetter.hcl1, "hcl1", false, "")
|
||||
flags.StringVar(&checkIndexStr, "check-index", "", "")
|
||||
flags.StringVar(&consulToken, "consul-token", "", "")
|
||||
flags.StringVar(&vaultToken, "vault-token", "", "")
|
||||
flags.StringVar(&vaultNamespace, "vault-namespace", "", "")
|
||||
flags.Var(&varArgs, "var", "")
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
@@ -171,7 +180,7 @@ func (c *JobRunCommand) Run(args []string) int {
|
||||
}
|
||||
|
||||
// Get Job struct from Jobfile
|
||||
job, err := c.JobGetter.ApiJob(args[0])
|
||||
job, err := c.JobGetter.ApiJobWithArgs(args[0], parseVars(varArgs))
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error getting job struct: %s", err))
|
||||
return 1
|
||||
|
||||
@@ -33,7 +33,7 @@ job "job1" {
|
||||
count = 1
|
||||
task "task1" {
|
||||
driver = "exec"
|
||||
resources = {
|
||||
resources {
|
||||
cpu = 1000
|
||||
memory = 512
|
||||
}
|
||||
@@ -127,7 +127,7 @@ job "job1" {
|
||||
count = 1
|
||||
task "task1" {
|
||||
driver = "exec"
|
||||
resources = {
|
||||
resources {
|
||||
cpu = 1000
|
||||
memory = 512
|
||||
}
|
||||
@@ -177,7 +177,7 @@ job "job1" {
|
||||
count = 1
|
||||
task "task1" {
|
||||
driver = "exec"
|
||||
resources = {
|
||||
resources {
|
||||
cpu = 1000
|
||||
memory = 512
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
cflags "github.com/hashicorp/consul/command/flags"
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/nomad/api"
|
||||
"github.com/hashicorp/nomad/command/agent"
|
||||
@@ -27,6 +28,11 @@ Alias: nomad validate
|
||||
If the supplied path is "-", the jobfile is read from stdin. Otherwise
|
||||
it is read from the file at the supplied path or downloaded and
|
||||
read from URL specified.
|
||||
|
||||
Validate Options:
|
||||
|
||||
-hcl1
|
||||
Parses the job file as HCLv1.
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
@@ -36,7 +42,10 @@ func (c *JobValidateCommand) Synopsis() string {
|
||||
}
|
||||
|
||||
func (c *JobValidateCommand) AutocompleteFlags() complete.Flags {
|
||||
return nil
|
||||
return complete.Flags{
|
||||
"-hcl1": complete.PredictNothing,
|
||||
"-var": complete.PredictAnything,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *JobValidateCommand) AutocompleteArgs() complete.Predictor {
|
||||
@@ -46,8 +55,13 @@ func (c *JobValidateCommand) AutocompleteArgs() complete.Predictor {
|
||||
func (c *JobValidateCommand) Name() string { return "job validate" }
|
||||
|
||||
func (c *JobValidateCommand) Run(args []string) int {
|
||||
var varArgs cflags.AppendSliceValue
|
||||
|
||||
flags := c.Meta.FlagSet(c.Name(), FlagSetNone)
|
||||
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||
flags.BoolVar(&c.JobGetter.hcl1, "hcl1", false, "")
|
||||
flags.Var(&varArgs, "var", "")
|
||||
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
@@ -61,7 +75,7 @@ func (c *JobValidateCommand) Run(args []string) int {
|
||||
}
|
||||
|
||||
// Get Job struct from Jobfile
|
||||
job, err := c.JobGetter.ApiJob(args[0])
|
||||
job, err := c.JobGetter.ApiJobWithArgs(args[0], parseVars(varArgs))
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error getting job struct: %s", err))
|
||||
return 1
|
||||
|
||||
@@ -40,7 +40,7 @@ job "job1" {
|
||||
config {
|
||||
command = "/bin/sleep"
|
||||
}
|
||||
resources = {
|
||||
resources {
|
||||
cpu = 1000
|
||||
memory = 512
|
||||
}
|
||||
@@ -142,7 +142,7 @@ job "job1" {
|
||||
config {
|
||||
command = "/bin/echo"
|
||||
}
|
||||
resources = {
|
||||
resources {
|
||||
cpu = 1000
|
||||
memory = 512
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ job "snapshot-test-job" {
|
||||
count = 1
|
||||
task "task1" {
|
||||
driver = "exec"
|
||||
resources = {
|
||||
resources {
|
||||
cpu = 1000
|
||||
memory = 512
|
||||
}
|
||||
|
||||
8
go.mod
8
go.mod
@@ -9,6 +9,7 @@ replace (
|
||||
github.com/godbus/dbus => github.com/godbus/dbus v5.0.1+incompatible
|
||||
github.com/golang/protobuf => github.com/golang/protobuf v1.3.4
|
||||
github.com/hashicorp/go-discover => github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f
|
||||
github.com/hashicorp/hcl => github.com/hashicorp/hcl v1.0.1-0.20201016140508-a07e7d50bbee
|
||||
github.com/hashicorp/nomad/api => ./api
|
||||
github.com/kr/pty => github.com/kr/pty v1.1.5
|
||||
)
|
||||
@@ -57,6 +58,7 @@ require (
|
||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1
|
||||
github.com/hashicorp/go-connlimit v0.2.0
|
||||
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840
|
||||
github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f
|
||||
github.com/hashicorp/go-envparse v0.0.0-20180119215841-310ca1881b22
|
||||
github.com/hashicorp/go-getter v1.5.0
|
||||
@@ -71,8 +73,8 @@ require (
|
||||
github.com/hashicorp/go-uuid v1.0.1
|
||||
github.com/hashicorp/go-version v1.2.1-0.20191009193637-2046c9d0f0b0
|
||||
github.com/hashicorp/golang-lru v0.5.4
|
||||
github.com/hashicorp/hcl v1.0.1-0.20191016231534-914dc3f8dd7c
|
||||
github.com/hashicorp/hcl/v2 v2.5.1
|
||||
github.com/hashicorp/hcl v1.0.1-0.20201016140508-a07e7d50bbee
|
||||
github.com/hashicorp/hcl/v2 v2.7.1-0.20201020204811-68a97f93bb48
|
||||
github.com/hashicorp/logutils v1.0.0
|
||||
github.com/hashicorp/memberlist v0.2.2
|
||||
github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69
|
||||
@@ -97,6 +99,7 @@ require (
|
||||
github.com/mitchellh/go-testing-interface v1.0.3
|
||||
github.com/mitchellh/hashstructure v1.0.0
|
||||
github.com/mitchellh/mapstructure v1.3.1
|
||||
github.com/mitchellh/reflectwalk v1.0.1
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/oklog/run v1.0.1-0.20180308005104-6934b124db28 // indirect
|
||||
github.com/onsi/gomega v1.9.0 // indirect
|
||||
@@ -116,6 +119,7 @@ require (
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2
|
||||
github.com/zclconf/go-cty v1.4.1
|
||||
github.com/zclconf/go-cty-yaml v1.0.2
|
||||
go.opencensus.io v0.22.1-0.20190713072201-b4a14686f0a9 // indirect
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 // indirect
|
||||
|
||||
21
go.sum
21
go.sum
@@ -85,6 +85,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfTJ/SpF+U=
|
||||
github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
|
||||
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA=
|
||||
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
|
||||
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
|
||||
@@ -117,6 +119,8 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/bmatcuk/doublestar v1.1.5 h1:2bNwBOmhyFEFcoB3tGvTD5xanq+4kyOZlB8wFYbMjkk=
|
||||
github.com/bmatcuk/doublestar v1.1.5/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
@@ -307,6 +311,7 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
@@ -355,6 +360,8 @@ github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVo
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-connlimit v0.2.0 h1:OZjcfNxH/hPh/bT2Iw5yOJcLzz+zuIWpsp3I1S4Pjw4=
|
||||
github.com/hashicorp/go-connlimit v0.2.0/go.mod h1:OUj9FGL1tPIhl/2RCfzYHrIiWj+VVPGNyVPnUX8AqS0=
|
||||
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840 h1:kgvybwEeu0SXktbB2y3uLHX9lklLo+nzUwh59A3jzQc=
|
||||
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840/go.mod h1:Abjk0jbRkDaNCzsRhOv2iDCofYpX1eVsjozoiK63qLA=
|
||||
github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f h1:7WFMVeuJQp6BkzuTv9O52pzwtEFVUJubKYN+zez8eTI=
|
||||
github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f/go.mod h1:D4eo8/CN92vm9/9UDG+ldX1/fMFa4kpl8qzyTolus8o=
|
||||
github.com/hashicorp/go-envparse v0.0.0-20180119215841-310ca1881b22 h1:HTmDIaSN95gbdMyrsbNiXSdW4fbGctGQwEqv0H7OhDQ=
|
||||
@@ -415,11 +422,10 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/hcl v1.0.1-0.20191016231534-914dc3f8dd7c h1:PdZEHcpa3117kJ1Wa5EYupzCzn9QlBby8Fx2YpZPYvo=
|
||||
github.com/hashicorp/hcl v1.0.1-0.20191016231534-914dc3f8dd7c/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/hcl/v2 v2.5.1 h1:5ytFZykUu2/4U59ogd2f+XZdi9+6oC/Tv5WzsH6fIDA=
|
||||
github.com/hashicorp/hcl/v2 v2.5.1/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
|
||||
github.com/hashicorp/hcl v1.0.1-0.20201016140508-a07e7d50bbee h1:8B4HqvMUtYSjsGkYjiQGStc9pXffY2J+Z2SPQAj+wMY=
|
||||
github.com/hashicorp/hcl v1.0.1-0.20201016140508-a07e7d50bbee/go.mod h1:gwlu9+/P9MmKtYrMsHeFRZPXj2CTPm11TDnMeaRHS7g=
|
||||
github.com/hashicorp/hcl/v2 v2.7.1-0.20201020204811-68a97f93bb48 h1:iaau0VStfX9CgOlpbceawI94uVEM3sliqnjpHSVQqUo=
|
||||
github.com/hashicorp/hcl/v2 v2.7.1-0.20201020204811-68a97f93bb48/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
|
||||
github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5 h1:uk280DXEbQiCOZgCOI3elFSeNxf8YIZiNsbr2pQLYD0=
|
||||
github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5/go.mod h1:KHvg/R2/dPtaePb16oW4qIyzkMxXOL38xjRN64adsts=
|
||||
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
|
||||
@@ -741,9 +747,13 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
|
||||
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
|
||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
||||
github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
|
||||
github.com/zclconf/go-cty v1.4.0/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ=
|
||||
github.com/zclconf/go-cty v1.4.1 h1:Xzr4m4utRDhHDifag1onwwUSq32HLoLBsp+w6tD0880=
|
||||
github.com/zclconf/go-cty v1.4.1/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ=
|
||||
github.com/zclconf/go-cty-yaml v1.0.2 h1:dNyg4QLTrv2IfJpm7Wtxi55ed5gLGOlPrZ6kMd51hY0=
|
||||
github.com/zclconf/go-cty-yaml v1.0.2/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
@@ -765,6 +775,7 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191106202628-ed6320f186d4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
||||
@@ -348,6 +348,7 @@ func parseGatewayProxy(o *ast.ObjectItem) (*api.ConsulGatewayProxy, error) {
|
||||
if err := hcl.DecodeObject(&bind, listenerListVal); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
bind.Name = listenerName
|
||||
proxy.EnvoyGatewayBindAddresses[listenerName] = &bind
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,9 +135,10 @@ func TestParse(t *testing.T) {
|
||||
ExtraKeysHCL: nil,
|
||||
},
|
||||
"bar": {
|
||||
Name: "bar",
|
||||
Type: "csi",
|
||||
Source: "bar-vol",
|
||||
Name: "bar",
|
||||
Type: "csi",
|
||||
Source: "bar-vol",
|
||||
ReadOnly: true,
|
||||
MountOptions: &api.CSIMountOptions{
|
||||
FSType: "ext4",
|
||||
},
|
||||
@@ -1422,8 +1423,8 @@ func TestParse(t *testing.T) {
|
||||
ConnectTimeout: timeToPtr(3 * time.Second),
|
||||
EnvoyGatewayBindTaggedAddresses: true,
|
||||
EnvoyGatewayBindAddresses: map[string]*api.ConsulGatewayBindAddress{
|
||||
"listener1": {Address: "10.0.0.1", Port: 8888},
|
||||
"listener2": {Address: "10.0.0.2", Port: 8889},
|
||||
"listener1": {Name: "listener1", Address: "10.0.0.1", Port: 8888},
|
||||
"listener2": {Name: "listener2", Address: "10.0.0.2", Port: 8889},
|
||||
},
|
||||
EnvoyGatewayNoDefaultBind: true,
|
||||
Config: map[string]interface{}{"foo": "bar"},
|
||||
|
||||
@@ -76,8 +76,9 @@ job "binstore-storagelocker" {
|
||||
}
|
||||
|
||||
volume "bar" {
|
||||
type = "csi"
|
||||
source = "bar-vol"
|
||||
type = "csi"
|
||||
source = "bar-vol"
|
||||
read_only = true
|
||||
|
||||
mount_options {
|
||||
fs_type = "ext4"
|
||||
|
||||
@@ -9,7 +9,7 @@ job "elastic" {
|
||||
foo = "bar"
|
||||
b = true
|
||||
val = 5
|
||||
f =.1
|
||||
f = 0.1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ job "sidecar_task_name" {
|
||||
name = "example"
|
||||
|
||||
connect {
|
||||
sidecar_service = {}
|
||||
sidecar_service {}
|
||||
|
||||
sidecar_task {
|
||||
name = "my-sidecar"
|
||||
|
||||
@@ -7,14 +7,14 @@ job "group_service_proxy_expose" {
|
||||
sidecar_service {
|
||||
proxy {
|
||||
expose {
|
||||
path = {
|
||||
path {
|
||||
path = "/health"
|
||||
protocol = "http"
|
||||
local_path_port = 2222
|
||||
listener_port = "healthcheck"
|
||||
}
|
||||
|
||||
path = {
|
||||
path {
|
||||
path = "/metrics"
|
||||
protocol = "grpc"
|
||||
local_path_port = 3000
|
||||
|
||||
133
jobspec2/functions.go
Normal file
133
jobspec2/functions.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package jobspec2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-cty-funcs/cidr"
|
||||
"github.com/hashicorp/go-cty-funcs/crypto"
|
||||
"github.com/hashicorp/go-cty-funcs/encoding"
|
||||
"github.com/hashicorp/go-cty-funcs/filesystem"
|
||||
"github.com/hashicorp/go-cty-funcs/uuid"
|
||||
"github.com/hashicorp/hcl/v2/ext/tryfunc"
|
||||
"github.com/hashicorp/hcl/v2/ext/typeexpr"
|
||||
ctyyaml "github.com/zclconf/go-cty-yaml"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/function/stdlib"
|
||||
)
|
||||
|
||||
// Functions returns the set of functions that should be used to when
|
||||
// evaluating expressions in the receiving scope.
|
||||
//
|
||||
// basedir is used with file functions and allows a user to reference a file
|
||||
// using local path. Usually basedir is the directory in which the config file
|
||||
// is located
|
||||
//
|
||||
func Functions(basedir string, allowFS bool) map[string]function.Function {
|
||||
funcs := map[string]function.Function{
|
||||
"abs": stdlib.AbsoluteFunc,
|
||||
"base64decode": encoding.Base64DecodeFunc,
|
||||
"base64encode": encoding.Base64EncodeFunc,
|
||||
"bcrypt": crypto.BcryptFunc,
|
||||
"can": tryfunc.CanFunc,
|
||||
"ceil": stdlib.CeilFunc,
|
||||
"chomp": stdlib.ChompFunc,
|
||||
"chunklist": stdlib.ChunklistFunc,
|
||||
"cidrhost": cidr.HostFunc,
|
||||
"cidrnetmask": cidr.NetmaskFunc,
|
||||
"cidrsubnet": cidr.SubnetFunc,
|
||||
"cidrsubnets": cidr.SubnetsFunc,
|
||||
"coalesce": stdlib.CoalesceFunc,
|
||||
"coalescelist": stdlib.CoalesceListFunc,
|
||||
"compact": stdlib.CompactFunc,
|
||||
"concat": stdlib.ConcatFunc,
|
||||
"contains": stdlib.ContainsFunc,
|
||||
"convert": typeexpr.ConvertFunc,
|
||||
"csvdecode": stdlib.CSVDecodeFunc,
|
||||
"distinct": stdlib.DistinctFunc,
|
||||
"element": stdlib.ElementFunc,
|
||||
"flatten": stdlib.FlattenFunc,
|
||||
"floor": stdlib.FloorFunc,
|
||||
"format": stdlib.FormatFunc,
|
||||
"formatdate": stdlib.FormatDateFunc,
|
||||
"formatlist": stdlib.FormatListFunc,
|
||||
"indent": stdlib.IndentFunc,
|
||||
"index": stdlib.IndexFunc,
|
||||
"join": stdlib.JoinFunc,
|
||||
"jsondecode": stdlib.JSONDecodeFunc,
|
||||
"jsonencode": stdlib.JSONEncodeFunc,
|
||||
"keys": stdlib.KeysFunc,
|
||||
"length": stdlib.LengthFunc,
|
||||
"log": stdlib.LogFunc,
|
||||
"lookup": stdlib.LookupFunc,
|
||||
"lower": stdlib.LowerFunc,
|
||||
"max": stdlib.MaxFunc,
|
||||
"md5": crypto.Md5Func,
|
||||
"merge": stdlib.MergeFunc,
|
||||
"min": stdlib.MinFunc,
|
||||
"parseint": stdlib.ParseIntFunc,
|
||||
"pow": stdlib.PowFunc,
|
||||
"range": stdlib.RangeFunc,
|
||||
"reverse": stdlib.ReverseFunc,
|
||||
"replace": stdlib.ReplaceFunc,
|
||||
"regex_replace": stdlib.RegexReplaceFunc,
|
||||
"rsadecrypt": crypto.RsaDecryptFunc,
|
||||
"setintersection": stdlib.SetIntersectionFunc,
|
||||
"setproduct": stdlib.SetProductFunc,
|
||||
"setunion": stdlib.SetUnionFunc,
|
||||
"sha1": crypto.Sha1Func,
|
||||
"sha256": crypto.Sha256Func,
|
||||
"sha512": crypto.Sha512Func,
|
||||
"signum": stdlib.SignumFunc,
|
||||
"slice": stdlib.SliceFunc,
|
||||
"sort": stdlib.SortFunc,
|
||||
"split": stdlib.SplitFunc,
|
||||
"strrev": stdlib.ReverseFunc,
|
||||
"substr": stdlib.SubstrFunc,
|
||||
"timeadd": stdlib.TimeAddFunc,
|
||||
"title": stdlib.TitleFunc,
|
||||
"trim": stdlib.TrimFunc,
|
||||
"trimprefix": stdlib.TrimPrefixFunc,
|
||||
"trimspace": stdlib.TrimSpaceFunc,
|
||||
"trimsuffix": stdlib.TrimSuffixFunc,
|
||||
"try": tryfunc.TryFunc,
|
||||
"upper": stdlib.UpperFunc,
|
||||
"urlencode": encoding.URLEncodeFunc,
|
||||
"uuidv4": uuid.V4Func,
|
||||
"uuidv5": uuid.V5Func,
|
||||
"values": stdlib.ValuesFunc,
|
||||
"yamldecode": ctyyaml.YAMLDecodeFunc,
|
||||
"yamlencode": ctyyaml.YAMLEncodeFunc,
|
||||
"zipmap": stdlib.ZipmapFunc,
|
||||
|
||||
// filesystem calls
|
||||
"abspath": guardFS(allowFS, filesystem.AbsPathFunc),
|
||||
"basename": guardFS(allowFS, filesystem.BasenameFunc),
|
||||
"dirname": guardFS(allowFS, filesystem.DirnameFunc),
|
||||
"file": guardFS(allowFS, filesystem.MakeFileFunc(basedir, false)),
|
||||
"fileexists": guardFS(allowFS, filesystem.MakeFileExistsFunc(basedir)),
|
||||
"fileset": guardFS(allowFS, filesystem.MakeFileSetFunc(basedir)),
|
||||
"pathexpand": guardFS(allowFS, filesystem.PathExpandFunc),
|
||||
}
|
||||
|
||||
return funcs
|
||||
}
|
||||
|
||||
func guardFS(allowFS bool, fn function.Function) function.Function {
|
||||
if allowFS {
|
||||
return fn
|
||||
}
|
||||
|
||||
spec := &function.Spec{
|
||||
Params: fn.Params(),
|
||||
VarParam: fn.VarParam(),
|
||||
Type: func([]cty.Value) (cty.Type, error) {
|
||||
return cty.DynamicPseudoType, fmt.Errorf("filesystem function disabled")
|
||||
},
|
||||
Impl: func([]cty.Value, cty.Type) (cty.Value, error) {
|
||||
return cty.DynamicVal, fmt.Errorf("filesystem functions disabled")
|
||||
},
|
||||
}
|
||||
|
||||
return function.New(spec)
|
||||
}
|
||||
281
jobspec2/hcl_conversions.go
Normal file
281
jobspec2/hcl_conversions.go
Normal file
@@ -0,0 +1,281 @@
|
||||
package jobspec2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/nomad/api"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
)
|
||||
|
||||
var hclDecoder *gohcl.Decoder
|
||||
|
||||
func init() {
|
||||
hclDecoder = newHCLDecoder()
|
||||
hclDecoder.RegisterBlockDecoder(reflect.TypeOf(api.TaskGroup{}), decodeTaskGroup)
|
||||
}
|
||||
|
||||
func newHCLDecoder() *gohcl.Decoder {
|
||||
decoder := &gohcl.Decoder{}
|
||||
|
||||
// time conversion
|
||||
d := time.Duration(0)
|
||||
decoder.RegisterExpressionDecoder(reflect.TypeOf(d), decodeDuration)
|
||||
decoder.RegisterExpressionDecoder(reflect.TypeOf(&d), decodeDuration)
|
||||
|
||||
// custom nomad types
|
||||
decoder.RegisterBlockDecoder(reflect.TypeOf(api.Affinity{}), decodeAffinity)
|
||||
decoder.RegisterBlockDecoder(reflect.TypeOf(api.Constraint{}), decodeConstraint)
|
||||
decoder.RegisterBlockDecoder(reflect.TypeOf(jobWrapper{}), decodeJob)
|
||||
|
||||
return decoder
|
||||
}
|
||||
|
||||
func decodeDuration(expr hcl.Expression, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics {
|
||||
srcVal, diags := expr.Value(ctx)
|
||||
|
||||
if srcVal.Type() == cty.String {
|
||||
dur, err := time.ParseDuration(srcVal.AsString())
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Unsuitable value type",
|
||||
Detail: fmt.Sprintf("Unsuitable duration value: %s", err.Error()),
|
||||
Subject: expr.StartRange().Ptr(),
|
||||
Context: expr.Range().Ptr(),
|
||||
})
|
||||
return diags
|
||||
}
|
||||
|
||||
srcVal = cty.NumberIntVal(int64(dur))
|
||||
}
|
||||
|
||||
if srcVal.Type() != cty.Number {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Unsuitable value type",
|
||||
Detail: fmt.Sprintf("Unsuitable value: expected a string but found %s", srcVal.Type()),
|
||||
Subject: expr.StartRange().Ptr(),
|
||||
Context: expr.Range().Ptr(),
|
||||
})
|
||||
return diags
|
||||
|
||||
}
|
||||
|
||||
err := gocty.FromCtyValue(srcVal, val)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Unsuitable value type",
|
||||
Detail: fmt.Sprintf("Unsuitable value: %s", err.Error()),
|
||||
Subject: expr.StartRange().Ptr(),
|
||||
Context: expr.Range().Ptr(),
|
||||
})
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
var affinitySpec = hcldec.ObjectSpec{
|
||||
"attribute": &hcldec.AttrSpec{Name: "attribute", Type: cty.String, Required: false},
|
||||
"value": &hcldec.AttrSpec{Name: "value", Type: cty.String, Required: false},
|
||||
"operator": &hcldec.AttrSpec{Name: "operator", Type: cty.String, Required: false},
|
||||
"weight": &hcldec.AttrSpec{Name: "weight", Type: cty.Number, Required: false},
|
||||
|
||||
api.ConstraintVersion: &hcldec.AttrSpec{Name: api.ConstraintVersion, Type: cty.String, Required: false},
|
||||
api.ConstraintSemver: &hcldec.AttrSpec{Name: api.ConstraintSemver, Type: cty.String, Required: false},
|
||||
api.ConstraintRegex: &hcldec.AttrSpec{Name: api.ConstraintRegex, Type: cty.String, Required: false},
|
||||
api.ConstraintSetContains: &hcldec.AttrSpec{Name: api.ConstraintSetContains, Type: cty.String, Required: false},
|
||||
api.ConstraintSetContainsAll: &hcldec.AttrSpec{Name: api.ConstraintSetContainsAll, Type: cty.String, Required: false},
|
||||
api.ConstraintSetContainsAny: &hcldec.AttrSpec{Name: api.ConstraintSetContainsAny, Type: cty.String, Required: false},
|
||||
}
|
||||
|
||||
func decodeAffinity(body hcl.Body, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics {
|
||||
a := val.(*api.Affinity)
|
||||
v, diags := hcldec.Decode(body, affinitySpec, ctx)
|
||||
if len(diags) != 0 {
|
||||
return diags
|
||||
}
|
||||
|
||||
attr := func(attr string) string {
|
||||
a := v.GetAttr(attr)
|
||||
if a.IsNull() {
|
||||
return ""
|
||||
}
|
||||
return a.AsString()
|
||||
}
|
||||
a.LTarget = attr("attribute")
|
||||
a.RTarget = attr("value")
|
||||
a.Operand = attr("operator")
|
||||
weight := v.GetAttr("weight")
|
||||
if !weight.IsNull() {
|
||||
w, _ := weight.AsBigFloat().Int64()
|
||||
a.Weight = int8ToPtr(int8(w))
|
||||
}
|
||||
|
||||
// If "version" is provided, set the operand
|
||||
// to "version" and the value to the "RTarget"
|
||||
if affinity := attr(api.ConstraintVersion); affinity != "" {
|
||||
a.Operand = api.ConstraintVersion
|
||||
a.RTarget = affinity
|
||||
}
|
||||
|
||||
// If "semver" is provided, set the operand
|
||||
// to "semver" and the value to the "RTarget"
|
||||
if affinity := attr(api.ConstraintSemver); affinity != "" {
|
||||
a.Operand = api.ConstraintSemver
|
||||
a.RTarget = affinity
|
||||
}
|
||||
|
||||
// If "regexp" is provided, set the operand
|
||||
// to "regexp" and the value to the "RTarget"
|
||||
if affinity := attr(api.ConstraintRegex); affinity != "" {
|
||||
a.Operand = api.ConstraintRegex
|
||||
a.RTarget = affinity
|
||||
}
|
||||
|
||||
// If "set_contains_any" is provided, set the operand
|
||||
// to "set_contains_any" and the value to the "RTarget"
|
||||
if affinity := attr(api.ConstraintSetContainsAny); affinity != "" {
|
||||
a.Operand = api.ConstraintSetContainsAny
|
||||
a.RTarget = affinity
|
||||
}
|
||||
|
||||
// If "set_contains_all" is provided, set the operand
|
||||
// to "set_contains_all" and the value to the "RTarget"
|
||||
if affinity := attr(api.ConstraintSetContainsAll); affinity != "" {
|
||||
a.Operand = api.ConstraintSetContainsAll
|
||||
a.RTarget = affinity
|
||||
}
|
||||
|
||||
// set_contains is a synonym of set_contains_all
|
||||
if affinity := attr(api.ConstraintSetContains); affinity != "" {
|
||||
a.Operand = api.ConstraintSetContains
|
||||
a.RTarget = affinity
|
||||
}
|
||||
|
||||
if a.Operand == "" {
|
||||
a.Operand = "="
|
||||
}
|
||||
return diags
|
||||
}
|
||||
|
||||
var constraintSpec = hcldec.ObjectSpec{
|
||||
"attribute": &hcldec.AttrSpec{Name: "attribute", Type: cty.String, Required: false},
|
||||
"value": &hcldec.AttrSpec{Name: "value", Type: cty.String, Required: false},
|
||||
"operator": &hcldec.AttrSpec{Name: "operator", Type: cty.String, Required: false},
|
||||
|
||||
api.ConstraintDistinctProperty: &hcldec.AttrSpec{Name: api.ConstraintDistinctProperty, Type: cty.String, Required: false},
|
||||
api.ConstraintDistinctHosts: &hcldec.AttrSpec{Name: api.ConstraintDistinctHosts, Type: cty.Bool, Required: false},
|
||||
api.ConstraintRegex: &hcldec.AttrSpec{Name: api.ConstraintRegex, Type: cty.String, Required: false},
|
||||
api.ConstraintVersion: &hcldec.AttrSpec{Name: api.ConstraintVersion, Type: cty.String, Required: false},
|
||||
api.ConstraintSemver: &hcldec.AttrSpec{Name: api.ConstraintSemver, Type: cty.String, Required: false},
|
||||
api.ConstraintSetContains: &hcldec.AttrSpec{Name: api.ConstraintSetContains, Type: cty.String, Required: false},
|
||||
api.ConstraintSetContainsAll: &hcldec.AttrSpec{Name: api.ConstraintSetContainsAll, Type: cty.String, Required: false},
|
||||
api.ConstraintSetContainsAny: &hcldec.AttrSpec{Name: api.ConstraintSetContainsAny, Type: cty.String, Required: false},
|
||||
api.ConstraintAttributeIsSet: &hcldec.AttrSpec{Name: api.ConstraintAttributeIsSet, Type: cty.String, Required: false},
|
||||
api.ConstraintAttributeIsNotSet: &hcldec.AttrSpec{Name: api.ConstraintAttributeIsNotSet, Type: cty.String, Required: false},
|
||||
}
|
||||
|
||||
func decodeConstraint(body hcl.Body, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics {
|
||||
c := val.(*api.Constraint)
|
||||
|
||||
v, diags := hcldec.Decode(body, constraintSpec, ctx)
|
||||
if len(diags) != 0 {
|
||||
return diags
|
||||
}
|
||||
|
||||
attr := func(attr string) string {
|
||||
a := v.GetAttr(attr)
|
||||
if a.IsNull() {
|
||||
return ""
|
||||
}
|
||||
return a.AsString()
|
||||
}
|
||||
|
||||
c.LTarget = attr("attribute")
|
||||
c.RTarget = attr("value")
|
||||
c.Operand = attr("operator")
|
||||
|
||||
// If "version" is provided, set the operand
|
||||
// to "version" and the value to the "RTarget"
|
||||
if constraint := attr(api.ConstraintVersion); constraint != "" {
|
||||
c.Operand = api.ConstraintVersion
|
||||
c.RTarget = constraint
|
||||
}
|
||||
|
||||
// If "semver" is provided, set the operand
|
||||
// to "semver" and the value to the "RTarget"
|
||||
if constraint := attr(api.ConstraintSemver); constraint != "" {
|
||||
c.Operand = api.ConstraintSemver
|
||||
c.RTarget = constraint
|
||||
}
|
||||
|
||||
// If "regexp" is provided, set the operand
|
||||
// to "regexp" and the value to the "RTarget"
|
||||
if constraint := attr(api.ConstraintRegex); constraint != "" {
|
||||
c.Operand = api.ConstraintRegex
|
||||
c.RTarget = constraint
|
||||
}
|
||||
|
||||
// If "set_contains" is provided, set the operand
|
||||
// to "set_contains" and the value to the "RTarget"
|
||||
if constraint := attr(api.ConstraintSetContains); constraint != "" {
|
||||
c.Operand = api.ConstraintSetContains
|
||||
c.RTarget = constraint
|
||||
}
|
||||
|
||||
if d := v.GetAttr(api.ConstraintDistinctHosts); !d.IsNull() && d.True() {
|
||||
c.Operand = api.ConstraintDistinctHosts
|
||||
}
|
||||
|
||||
if property := attr(api.ConstraintDistinctProperty); property != "" {
|
||||
c.Operand = api.ConstraintDistinctProperty
|
||||
c.LTarget = property
|
||||
}
|
||||
|
||||
if c.Operand == "" {
|
||||
c.Operand = "="
|
||||
}
|
||||
return diags
|
||||
}
|
||||
|
||||
func decodeTaskGroup(body hcl.Body, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics {
|
||||
tg := val.(*api.TaskGroup)
|
||||
tgExtra := struct {
|
||||
Vault *api.Vault `hcl:"vault,block"`
|
||||
}{}
|
||||
|
||||
extra, _ := gohcl.ImpliedBodySchema(tgExtra)
|
||||
content, tgBody, diags := body.PartialContent(extra)
|
||||
if len(diags) != 0 {
|
||||
return diags
|
||||
}
|
||||
|
||||
for _, b := range content.Blocks {
|
||||
if b.Type == "vault" {
|
||||
v := &api.Vault{}
|
||||
diags = append(diags, hclDecoder.DecodeBody(b.Body, ctx, v)...)
|
||||
tgExtra.Vault = v
|
||||
}
|
||||
}
|
||||
|
||||
d := newHCLDecoder()
|
||||
diags = d.DecodeBody(tgBody, ctx, tg)
|
||||
|
||||
if tgExtra.Vault != nil {
|
||||
for _, t := range tg.Tasks {
|
||||
if t.Vault == nil {
|
||||
t.Vault = tgExtra.Vault
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diags
|
||||
|
||||
}
|
||||
176
jobspec2/hclutil/blockattrs.go
Normal file
176
jobspec2/hclutil/blockattrs.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package hclutil
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
hcls "github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
)
|
||||
|
||||
// BlocksAsAttrs rewrites the hcl.Body so that hcl blocks are treated as
|
||||
// attributes when schema is unknown.
|
||||
//
|
||||
// This conversion is necessary for parsing task driver configs, as they can be
|
||||
// arbitrary nested without pre-defined schema.
|
||||
//
|
||||
// More concretely, it changes the following:
|
||||
//
|
||||
// ```
|
||||
// config {
|
||||
// meta { ... }
|
||||
// }
|
||||
// ```
|
||||
|
||||
// to
|
||||
//
|
||||
// ```
|
||||
// config {
|
||||
// meta = { ... } # <- attribute now
|
||||
// }
|
||||
// ```
|
||||
func BlocksAsAttrs(body hcl.Body) hcl.Body {
|
||||
if hclb, ok := body.(*hcls.Body); ok {
|
||||
return &blockAttrs{body: hclb}
|
||||
}
|
||||
return body
|
||||
}
|
||||
|
||||
type blockAttrs struct {
|
||||
body hcl.Body
|
||||
|
||||
hiddenAttrs map[string]struct{}
|
||||
hiddenBlocks map[string]struct{}
|
||||
}
|
||||
|
||||
func (b *blockAttrs) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
|
||||
bc, diags := b.body.Content(schema)
|
||||
bc.Blocks = expandBlocks(bc.Blocks)
|
||||
return bc, diags
|
||||
}
|
||||
func (b *blockAttrs) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
|
||||
bc, remainBody, diags := b.body.PartialContent(schema)
|
||||
bc.Blocks = expandBlocks(bc.Blocks)
|
||||
|
||||
remain := &blockAttrs{
|
||||
body: remainBody,
|
||||
hiddenAttrs: map[string]struct{}{},
|
||||
hiddenBlocks: map[string]struct{}{},
|
||||
}
|
||||
for name := range b.hiddenAttrs {
|
||||
remain.hiddenAttrs[name] = struct{}{}
|
||||
}
|
||||
for typeName := range b.hiddenBlocks {
|
||||
remain.hiddenBlocks[typeName] = struct{}{}
|
||||
}
|
||||
for _, attrS := range schema.Attributes {
|
||||
remain.hiddenAttrs[attrS.Name] = struct{}{}
|
||||
}
|
||||
for _, blockS := range schema.Blocks {
|
||||
remain.hiddenBlocks[blockS.Type] = struct{}{}
|
||||
}
|
||||
|
||||
return bc, remain, diags
|
||||
}
|
||||
|
||||
func (b *blockAttrs) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
|
||||
body, ok := b.body.(*hcls.Body)
|
||||
if !ok {
|
||||
return b.body.JustAttributes()
|
||||
}
|
||||
|
||||
attrs := make(hcl.Attributes)
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
if body.Attributes == nil && len(body.Blocks) == 0 {
|
||||
return attrs, diags
|
||||
}
|
||||
|
||||
for name, attr := range body.Attributes {
|
||||
if _, hidden := b.hiddenAttrs[name]; hidden {
|
||||
continue
|
||||
}
|
||||
attrs[name] = attr.AsHCLAttribute()
|
||||
}
|
||||
|
||||
for _, blockS := range body.Blocks {
|
||||
if _, hidden := b.hiddenBlocks[blockS.Type]; hidden {
|
||||
continue
|
||||
}
|
||||
|
||||
attrs[blockS.Type] = convertToAttribute(blockS).AsHCLAttribute()
|
||||
}
|
||||
|
||||
return attrs, diags
|
||||
}
|
||||
|
||||
func (b *blockAttrs) MissingItemRange() hcl.Range {
|
||||
return b.body.MissingItemRange()
|
||||
}
|
||||
|
||||
func expandBlocks(blocks hcl.Blocks) hcl.Blocks {
|
||||
if len(blocks) == 0 {
|
||||
return blocks
|
||||
}
|
||||
|
||||
r := make([]*hcl.Block, len(blocks))
|
||||
for i, b := range blocks {
|
||||
nb := *b
|
||||
nb.Body = BlocksAsAttrs(b.Body)
|
||||
r[i] = &nb
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func convertToAttribute(b *hcls.Block) *hcls.Attribute {
|
||||
items := []hcls.ObjectConsItem{}
|
||||
|
||||
for _, attr := range b.Body.Attributes {
|
||||
keyExpr := &hcls.ScopeTraversalExpr{
|
||||
Traversal: hcl.Traversal{
|
||||
hcl.TraverseRoot{
|
||||
Name: attr.Name,
|
||||
SrcRange: attr.NameRange,
|
||||
},
|
||||
},
|
||||
SrcRange: attr.NameRange,
|
||||
}
|
||||
key := &hcls.ObjectConsKeyExpr{
|
||||
Wrapped: keyExpr,
|
||||
}
|
||||
|
||||
items = append(items, hcls.ObjectConsItem{
|
||||
KeyExpr: key,
|
||||
ValueExpr: attr.Expr,
|
||||
})
|
||||
}
|
||||
|
||||
for _, block := range b.Body.Blocks {
|
||||
keyExpr := &hcls.ScopeTraversalExpr{
|
||||
Traversal: hcl.Traversal{
|
||||
hcl.TraverseRoot{
|
||||
Name: block.Type,
|
||||
SrcRange: block.TypeRange,
|
||||
},
|
||||
},
|
||||
SrcRange: block.TypeRange,
|
||||
}
|
||||
key := &hcls.ObjectConsKeyExpr{
|
||||
Wrapped: keyExpr,
|
||||
}
|
||||
valExpr := convertToAttribute(block).Expr
|
||||
items = append(items, hcls.ObjectConsItem{
|
||||
KeyExpr: key,
|
||||
ValueExpr: valExpr,
|
||||
})
|
||||
}
|
||||
|
||||
attr := &hcls.Attribute{
|
||||
Name: b.Type,
|
||||
NameRange: b.TypeRange,
|
||||
EqualsRange: b.OpenBraceRange,
|
||||
SrcRange: b.Body.SrcRange,
|
||||
Expr: &hcls.ObjectConsExpr{
|
||||
Items: items,
|
||||
},
|
||||
}
|
||||
|
||||
return attr
|
||||
}
|
||||
109
jobspec2/parse.go
Normal file
109
jobspec2/parse.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package jobspec2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/ext/dynblock"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
hcljson "github.com/hashicorp/hcl/v2/json"
|
||||
"github.com/hashicorp/nomad/api"
|
||||
"github.com/hashicorp/nomad/jobspec2/hclutil"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func Parse(path string, r io.Reader) (*api.Job, error) {
|
||||
return ParseWithArgs(path, r, nil, false)
|
||||
}
|
||||
|
||||
func toVars(vars map[string]string) cty.Value {
|
||||
attrs := make(map[string]cty.Value, len(vars))
|
||||
for k, v := range vars {
|
||||
attrs[k] = cty.StringVal(v)
|
||||
}
|
||||
|
||||
return cty.ObjectVal(attrs)
|
||||
}
|
||||
|
||||
func ParseWithArgs(path string, r io.Reader, vars map[string]string, allowFS bool) (*api.Job, error) {
|
||||
if path == "" {
|
||||
if f, ok := r.(*os.File); ok {
|
||||
path = f.Name()
|
||||
}
|
||||
}
|
||||
basedir := filepath.Dir(path)
|
||||
|
||||
// Copy the reader into an in-memory buffer first since HCL requires it.
|
||||
var buf bytes.Buffer
|
||||
if _, err := io.Copy(&buf, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
evalContext := &hcl.EvalContext{
|
||||
Functions: Functions(basedir, allowFS),
|
||||
Variables: map[string]cty.Value{
|
||||
"vars": toVars(vars),
|
||||
},
|
||||
UnknownVariable: func(expr string) (cty.Value, error) {
|
||||
v := "${" + expr + "}"
|
||||
return cty.StringVal(v), nil
|
||||
},
|
||||
}
|
||||
var result struct {
|
||||
Job jobWrapper `hcl:"job,block"`
|
||||
}
|
||||
err := decode(path, buf.Bytes(), evalContext, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
normalizeJob(&result.Job)
|
||||
return result.Job.Job, nil
|
||||
}
|
||||
|
||||
func decode(filename string, src []byte, ctx *hcl.EvalContext, target interface{}) error {
|
||||
var file *hcl.File
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
if !isJSON(src) {
|
||||
file, diags = hclsyntax.ParseConfig(src, filename, hcl.Pos{Line: 1, Column: 1})
|
||||
} else {
|
||||
file, diags = hcljson.Parse(src, filename)
|
||||
|
||||
}
|
||||
if diags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
||||
body := hclutil.BlocksAsAttrs(file.Body)
|
||||
body = dynblock.Expand(body, ctx)
|
||||
diags = hclDecoder.DecodeBody(body, ctx, target)
|
||||
if diags.HasErrors() {
|
||||
var str strings.Builder
|
||||
for i, diag := range diags {
|
||||
if i != 0 {
|
||||
str.WriteByte('\n')
|
||||
}
|
||||
str.WriteString(diag.Error())
|
||||
}
|
||||
return errors.New(str.String())
|
||||
}
|
||||
diags = append(diags, decodeMapInterfaceType(target, ctx)...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func isJSON(src []byte) bool {
|
||||
for _, c := range src {
|
||||
if c == ' ' {
|
||||
continue
|
||||
}
|
||||
|
||||
return c == '{'
|
||||
}
|
||||
return false
|
||||
}
|
||||
169
jobspec2/parse_job.go
Normal file
169
jobspec2/parse_job.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package jobspec2
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/hashicorp/nomad/api"
|
||||
)
|
||||
|
||||
type jobWrapper struct {
|
||||
JobID string `hcl:",label"`
|
||||
Job *api.Job
|
||||
|
||||
Extra struct {
|
||||
Vault *api.Vault `hcl:"vault,block"`
|
||||
Tasks []*api.Task `hcl:"task,block"`
|
||||
}
|
||||
}
|
||||
|
||||
func decodeJob(body hcl.Body, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics {
|
||||
m := val.(*jobWrapper)
|
||||
extra, _ := gohcl.ImpliedBodySchema(m.Extra)
|
||||
content, job, diags := body.PartialContent(extra)
|
||||
if len(diags) != 0 {
|
||||
return diags
|
||||
}
|
||||
|
||||
for _, b := range content.Blocks {
|
||||
if b.Type == "vault" {
|
||||
v := &api.Vault{}
|
||||
diags = append(diags, hclDecoder.DecodeBody(b.Body, ctx, v)...)
|
||||
m.Extra.Vault = v
|
||||
} else if b.Type == "task" {
|
||||
t := &api.Task{}
|
||||
diags = append(diags, hclDecoder.DecodeBody(b.Body, ctx, t)...)
|
||||
if len(b.Labels) == 1 {
|
||||
t.Name = b.Labels[0]
|
||||
m.Extra.Tasks = append(m.Extra.Tasks, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m.Job = &api.Job{}
|
||||
return hclDecoder.DecodeBody(job, ctx, m.Job)
|
||||
}
|
||||
|
||||
func normalizeJob(jw *jobWrapper) {
|
||||
j := jw.Job
|
||||
if j.Name == nil {
|
||||
j.Name = &jw.JobID
|
||||
}
|
||||
if j.ID == nil {
|
||||
j.ID = &jw.JobID
|
||||
}
|
||||
|
||||
if j.Periodic != nil && j.Periodic.Spec != nil {
|
||||
v := "cron"
|
||||
j.Periodic.SpecType = &v
|
||||
}
|
||||
|
||||
normalizeVault(jw.Extra.Vault)
|
||||
|
||||
if len(jw.Extra.Tasks) != 0 {
|
||||
alone := make([]*api.TaskGroup, 0, len(jw.Extra.Tasks))
|
||||
for _, t := range jw.Extra.Tasks {
|
||||
alone = append(alone, &api.TaskGroup{
|
||||
Name: &t.Name,
|
||||
Tasks: []*api.Task{t},
|
||||
})
|
||||
}
|
||||
alone = append(alone, j.TaskGroups...)
|
||||
j.TaskGroups = alone
|
||||
}
|
||||
|
||||
for _, tg := range j.TaskGroups {
|
||||
normalizeNetworkPorts(tg.Networks)
|
||||
for _, t := range tg.Tasks {
|
||||
if t.Resources != nil {
|
||||
normalizeNetworkPorts(t.Resources.Networks)
|
||||
}
|
||||
|
||||
normalizeTemplates(t.Templates)
|
||||
|
||||
// normalize Vault
|
||||
normalizeVault(t.Vault)
|
||||
|
||||
if t.Vault == nil {
|
||||
t.Vault = jw.Extra.Vault
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeVault(v *api.Vault) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if v.Env == nil {
|
||||
v.Env = boolToPtr(true)
|
||||
}
|
||||
if v.ChangeMode == nil {
|
||||
v.ChangeMode = stringToPtr("restart")
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeNetworkPorts(networks []*api.NetworkResource) {
|
||||
if networks == nil {
|
||||
return
|
||||
}
|
||||
for _, n := range networks {
|
||||
if len(n.DynamicPorts) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
dynamic := make([]api.Port, 0, len(n.DynamicPorts))
|
||||
var reserved []api.Port
|
||||
|
||||
for _, p := range n.DynamicPorts {
|
||||
if p.Value > 0 {
|
||||
reserved = append(reserved, p)
|
||||
} else {
|
||||
dynamic = append(dynamic, p)
|
||||
}
|
||||
}
|
||||
if len(dynamic) == 0 {
|
||||
dynamic = nil
|
||||
}
|
||||
|
||||
n.DynamicPorts = dynamic
|
||||
n.ReservedPorts = reserved
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func normalizeTemplates(templates []*api.Template) {
|
||||
if len(templates) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, t := range templates {
|
||||
if t.ChangeMode == nil {
|
||||
t.ChangeMode = stringToPtr("restart")
|
||||
}
|
||||
if t.Perms == nil {
|
||||
t.Perms = stringToPtr("0644")
|
||||
}
|
||||
if t.Splay == nil {
|
||||
t.Splay = durationToPtr(5 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func int8ToPtr(v int8) *int8 {
|
||||
return &v
|
||||
}
|
||||
|
||||
func boolToPtr(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
func stringToPtr(v string) *string {
|
||||
return &v
|
||||
}
|
||||
|
||||
func durationToPtr(v time.Duration) *time.Duration {
|
||||
return &v
|
||||
}
|
||||
172
jobspec2/parse_map.go
Normal file
172
jobspec2/parse_map.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package jobspec2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/mitchellh/reflectwalk"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// decodeMapInterfaceType decodes hcl instances of `map[string]interface{}` fields
|
||||
// of v.
|
||||
//
|
||||
// The HCL parser stores the hcl AST as the map values, and decodeMapInterfaceType
|
||||
// evaluates the AST and converts them to the native golang types.
|
||||
func decodeMapInterfaceType(v interface{}, ctx *hcl.EvalContext) hcl.Diagnostics {
|
||||
w := &walker{ctx: ctx}
|
||||
err := reflectwalk.Walk(v, w)
|
||||
if err != nil {
|
||||
w.diags = append(w.diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "unexpected internal error",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
}
|
||||
return w.diags
|
||||
}
|
||||
|
||||
type walker struct {
|
||||
ctx *hcl.EvalContext
|
||||
diags hcl.Diagnostics
|
||||
}
|
||||
|
||||
var mapStringInterfaceType = reflect.TypeOf(map[string]interface{}{})
|
||||
|
||||
func (w *walker) Map(m reflect.Value) error {
|
||||
if !m.Type().AssignableTo(mapStringInterfaceType) {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, k := range m.MapKeys() {
|
||||
v := m.MapIndex(k)
|
||||
if attr, ok := v.Interface().(*hcl.Attribute); ok {
|
||||
c, diags := decodeInterface(attr.Expr, w.ctx)
|
||||
w.diags = append(w.diags, diags...)
|
||||
|
||||
m.SetMapIndex(k, reflect.ValueOf(c))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *walker) MapElem(m, k, v reflect.Value) error {
|
||||
return nil
|
||||
}
|
||||
func decodeInterface(expr hcl.Expression, ctx *hcl.EvalContext) (interface{}, hcl.Diagnostics) {
|
||||
srvVal, diags := expr.Value(ctx)
|
||||
|
||||
dst, err := interfaceFromCtyValue(srvVal)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "unsuitable value type",
|
||||
Detail: fmt.Sprintf("Unsuitable value: %s", err.Error()),
|
||||
Subject: expr.StartRange().Ptr(),
|
||||
Context: expr.Range().Ptr(),
|
||||
})
|
||||
}
|
||||
return dst, diags
|
||||
}
|
||||
|
||||
func interfaceFromCtyValue(val cty.Value) (interface{}, error) {
|
||||
t := val.Type()
|
||||
|
||||
if val.IsNull() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if !val.IsKnown() {
|
||||
return nil, fmt.Errorf("value is not known")
|
||||
}
|
||||
|
||||
// The caller should've guaranteed that the given val is conformant with
|
||||
// the given type t, so we'll proceed under that assumption here.
|
||||
|
||||
switch {
|
||||
case t.IsPrimitiveType():
|
||||
switch t {
|
||||
case cty.String:
|
||||
return val.AsString(), nil
|
||||
case cty.Number:
|
||||
if val.RawEquals(cty.PositiveInfinity) {
|
||||
return math.Inf(1), nil
|
||||
} else if val.RawEquals(cty.NegativeInfinity) {
|
||||
return math.Inf(-1), nil
|
||||
} else {
|
||||
return smallestNumber(val.AsBigFloat()), nil
|
||||
}
|
||||
case cty.Bool:
|
||||
return val.True(), nil
|
||||
default:
|
||||
panic("unsupported primitive type")
|
||||
}
|
||||
case t.IsListType(), t.IsSetType(), t.IsTupleType():
|
||||
result := []interface{}{}
|
||||
|
||||
it := val.ElementIterator()
|
||||
for it.Next() {
|
||||
_, ev := it.Element()
|
||||
evi, err := interfaceFromCtyValue(ev)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, evi)
|
||||
}
|
||||
return result, nil
|
||||
case t.IsMapType():
|
||||
result := map[string]interface{}{}
|
||||
it := val.ElementIterator()
|
||||
for it.Next() {
|
||||
ek, ev := it.Element()
|
||||
|
||||
ekv := ek.AsString()
|
||||
evv, err := interfaceFromCtyValue(ev)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result[ekv] = evv
|
||||
}
|
||||
return []map[string]interface{}{result}, nil
|
||||
case t.IsObjectType():
|
||||
result := map[string]interface{}{}
|
||||
|
||||
for k := range t.AttributeTypes() {
|
||||
av := val.GetAttr(k)
|
||||
avv, err := interfaceFromCtyValue(av)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result[k] = avv
|
||||
}
|
||||
return []map[string]interface{}{result}, nil
|
||||
case t.IsCapsuleType():
|
||||
rawVal := val.EncapsulatedValue()
|
||||
return rawVal, nil
|
||||
default:
|
||||
// should never happen
|
||||
return nil, fmt.Errorf("cannot serialize %s", t.FriendlyName())
|
||||
}
|
||||
}
|
||||
|
||||
func smallestNumber(b *big.Float) interface{} {
|
||||
|
||||
if v, acc := b.Int64(); acc == big.Exact {
|
||||
// check if it fits in int
|
||||
if int64(int(v)) == v {
|
||||
return int(v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
if v, acc := b.Float64(); acc == big.Exact || acc == big.Above {
|
||||
return v
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
130
jobspec2/parse_test.go
Normal file
130
jobspec2/parse_test.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package jobspec2
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/nomad/jobspec"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEquivalentToHCL1(t *testing.T) {
|
||||
hclSpecDir := "../jobspec/test-fixtures/"
|
||||
fis, err := ioutil.ReadDir(hclSpecDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, fi := range fis {
|
||||
name := fi.Name()
|
||||
|
||||
t.Run(name, func(t *testing.T) {
|
||||
f, err := os.Open(hclSpecDir + name)
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
|
||||
job1, err := jobspec.Parse(f)
|
||||
if err != nil {
|
||||
t.Skip("file is not parsable in v1")
|
||||
}
|
||||
|
||||
f.Seek(0, 0)
|
||||
|
||||
job2, err := Parse(name, f)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, job1, job2)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse_VarsAndFunctions(t *testing.T) {
|
||||
hcl := `
|
||||
job "example" {
|
||||
datacenters = [for s in ["dc1", "dc2"] : upper(s)]
|
||||
region = vars.region_var
|
||||
}
|
||||
`
|
||||
|
||||
out, err := ParseWithArgs("input.hcl", strings.NewReader(hcl), map[string]string{"region_var": "aug"}, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, []string{"DC1", "DC2"}, out.Datacenters)
|
||||
require.NotNil(t, out.Region)
|
||||
require.Equal(t, "aug", *out.Region)
|
||||
}
|
||||
|
||||
// TestParse_UnknownVariables asserts that unknown variables are left intact for further processing
|
||||
func TestParse_UnknownVariables(t *testing.T) {
|
||||
hcl := `
|
||||
job "example" {
|
||||
datacenters = [for s in ["dc1", "dc2"] : upper(s)]
|
||||
region = vars.region_var
|
||||
meta {
|
||||
known_var = "${vars.region_var}"
|
||||
unknown_var = "${UNKNOWN}"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
out, err := ParseWithArgs("input.hcl", strings.NewReader(hcl), map[string]string{"region_var": "aug"}, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
meta := map[string]string{
|
||||
"known_var": "aug",
|
||||
"unknown_var": "${UNKNOWN}",
|
||||
}
|
||||
|
||||
require.Equal(t, meta, out.Meta)
|
||||
}
|
||||
|
||||
func TestParse_FileOperators(t *testing.T) {
|
||||
hcl := `
|
||||
job "example" {
|
||||
region = file("parse_test.go")
|
||||
}
|
||||
`
|
||||
|
||||
t.Run("enabled", func(t *testing.T) {
|
||||
out, err := ParseWithArgs("input.hcl", strings.NewReader(hcl), nil, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
expected, err := ioutil.ReadFile("parse_test.go")
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotNil(t, out.Region)
|
||||
require.Equal(t, string(expected), *out.Region)
|
||||
})
|
||||
|
||||
t.Run("disabled", func(t *testing.T) {
|
||||
_, err := ParseWithArgs("input.hcl", strings.NewReader(hcl), nil, false)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "filesystem function disabled")
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseDynamic(t *testing.T) {
|
||||
hcl := `
|
||||
job "example" {
|
||||
|
||||
dynamic "group" {
|
||||
for_each = ["groupA", "groupB", "groupC"]
|
||||
labels = [group.value]
|
||||
|
||||
content {
|
||||
task "simple" {
|
||||
driver = "raw_exec"
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
out, err := ParseWithArgs("input.hcl", strings.NewReader(hcl), nil, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, out.TaskGroups, 3)
|
||||
require.Equal(t, "groupA", *out.TaskGroups[0].Name)
|
||||
require.Equal(t, "groupB", *out.TaskGroups[1].Name)
|
||||
require.Equal(t, "groupC", *out.TaskGroups[2].Name)
|
||||
}
|
||||
19
vendor/github.com/apparentlymart/go-cidr/LICENSE
generated
vendored
Normal file
19
vendor/github.com/apparentlymart/go-cidr/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2015 Martin Atkins
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
218
vendor/github.com/apparentlymart/go-cidr/cidr/cidr.go
generated
vendored
Normal file
218
vendor/github.com/apparentlymart/go-cidr/cidr/cidr.go
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
// Package cidr is a collection of assorted utilities for computing
|
||||
// network and host addresses within network ranges.
|
||||
//
|
||||
// It expects a CIDR-type address structure where addresses are divided into
|
||||
// some number of prefix bits representing the network and then the remaining
|
||||
// suffix bits represent the host.
|
||||
//
|
||||
// For example, it can help to calculate addresses for sub-networks of a
|
||||
// parent network, or to calculate host addresses within a particular prefix.
|
||||
//
|
||||
// At present this package is prioritizing simplicity of implementation and
|
||||
// de-prioritizing speed and memory usage. Thus caution is advised before
|
||||
// using this package in performance-critical applications or hot codepaths.
|
||||
// Patches to improve the speed and memory usage may be accepted as long as
|
||||
// they do not result in a significant increase in code complexity.
|
||||
package cidr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Subnet takes a parent CIDR range and creates a subnet within it
|
||||
// with the given number of additional prefix bits and the given
|
||||
// network number.
|
||||
//
|
||||
// For example, 10.3.0.0/16, extended by 8 bits, with a network number
|
||||
// of 5, becomes 10.3.5.0/24 .
|
||||
func Subnet(base *net.IPNet, newBits int, num int) (*net.IPNet, error) {
|
||||
ip := base.IP
|
||||
mask := base.Mask
|
||||
|
||||
parentLen, addrLen := mask.Size()
|
||||
newPrefixLen := parentLen + newBits
|
||||
|
||||
if newPrefixLen > addrLen {
|
||||
return nil, fmt.Errorf("insufficient address space to extend prefix of %d by %d", parentLen, newBits)
|
||||
}
|
||||
|
||||
maxNetNum := uint64(1<<uint64(newBits)) - 1
|
||||
if uint64(num) > maxNetNum {
|
||||
return nil, fmt.Errorf("prefix extension of %d does not accommodate a subnet numbered %d", newBits, num)
|
||||
}
|
||||
|
||||
return &net.IPNet{
|
||||
IP: insertNumIntoIP(ip, big.NewInt(int64(num)), newPrefixLen),
|
||||
Mask: net.CIDRMask(newPrefixLen, addrLen),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Host takes a parent CIDR range and turns it into a host IP address with
|
||||
// the given host number.
|
||||
//
|
||||
// For example, 10.3.0.0/16 with a host number of 2 gives 10.3.0.2.
|
||||
func Host(base *net.IPNet, num int) (net.IP, error) {
|
||||
ip := base.IP
|
||||
mask := base.Mask
|
||||
bigNum := big.NewInt(int64(num))
|
||||
|
||||
parentLen, addrLen := mask.Size()
|
||||
hostLen := addrLen - parentLen
|
||||
|
||||
maxHostNum := big.NewInt(int64(1))
|
||||
maxHostNum.Lsh(maxHostNum, uint(hostLen))
|
||||
maxHostNum.Sub(maxHostNum, big.NewInt(1))
|
||||
|
||||
numUint64 := big.NewInt(int64(bigNum.Uint64()))
|
||||
if bigNum.Cmp(big.NewInt(0)) == -1 {
|
||||
numUint64.Neg(bigNum)
|
||||
numUint64.Sub(numUint64, big.NewInt(int64(1)))
|
||||
bigNum.Sub(maxHostNum, numUint64)
|
||||
}
|
||||
|
||||
if numUint64.Cmp(maxHostNum) == 1 {
|
||||
return nil, fmt.Errorf("prefix of %d does not accommodate a host numbered %d", parentLen, num)
|
||||
}
|
||||
var bitlength int
|
||||
if ip.To4() != nil {
|
||||
bitlength = 32
|
||||
} else {
|
||||
bitlength = 128
|
||||
}
|
||||
return insertNumIntoIP(ip, bigNum, bitlength), nil
|
||||
}
|
||||
|
||||
// AddressRange returns the first and last addresses in the given CIDR range.
|
||||
func AddressRange(network *net.IPNet) (net.IP, net.IP) {
|
||||
// the first IP is easy
|
||||
firstIP := network.IP
|
||||
|
||||
// the last IP is the network address OR NOT the mask address
|
||||
prefixLen, bits := network.Mask.Size()
|
||||
if prefixLen == bits {
|
||||
// Easy!
|
||||
// But make sure that our two slices are distinct, since they
|
||||
// would be in all other cases.
|
||||
lastIP := make([]byte, len(firstIP))
|
||||
copy(lastIP, firstIP)
|
||||
return firstIP, lastIP
|
||||
}
|
||||
|
||||
firstIPInt, bits := ipToInt(firstIP)
|
||||
hostLen := uint(bits) - uint(prefixLen)
|
||||
lastIPInt := big.NewInt(1)
|
||||
lastIPInt.Lsh(lastIPInt, hostLen)
|
||||
lastIPInt.Sub(lastIPInt, big.NewInt(1))
|
||||
lastIPInt.Or(lastIPInt, firstIPInt)
|
||||
|
||||
return firstIP, intToIP(lastIPInt, bits)
|
||||
}
|
||||
|
||||
// AddressCount returns the number of distinct host addresses within the given
|
||||
// CIDR range.
|
||||
//
|
||||
// Since the result is a uint64, this function returns meaningful information
|
||||
// only for IPv4 ranges and IPv6 ranges with a prefix size of at least 65.
|
||||
func AddressCount(network *net.IPNet) uint64 {
|
||||
prefixLen, bits := network.Mask.Size()
|
||||
return 1 << (uint64(bits) - uint64(prefixLen))
|
||||
}
|
||||
|
||||
//VerifyNoOverlap takes a list subnets and supernet (CIDRBlock) and verifies
|
||||
//none of the subnets overlap and all subnets are in the supernet
|
||||
//it returns an error if any of those conditions are not satisfied
|
||||
func VerifyNoOverlap(subnets []*net.IPNet, CIDRBlock *net.IPNet) error {
|
||||
firstLastIP := make([][]net.IP, len(subnets))
|
||||
for i, s := range subnets {
|
||||
first, last := AddressRange(s)
|
||||
firstLastIP[i] = []net.IP{first, last}
|
||||
}
|
||||
for i, s := range subnets {
|
||||
if !CIDRBlock.Contains(firstLastIP[i][0]) || !CIDRBlock.Contains(firstLastIP[i][1]) {
|
||||
return fmt.Errorf("%s does not fully contain %s", CIDRBlock.String(), s.String())
|
||||
}
|
||||
for j := 0; j < len(subnets); j++ {
|
||||
if i == j {
|
||||
continue
|
||||
}
|
||||
|
||||
first := firstLastIP[j][0]
|
||||
last := firstLastIP[j][1]
|
||||
if s.Contains(first) || s.Contains(last) {
|
||||
return fmt.Errorf("%s overlaps with %s", subnets[j].String(), s.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PreviousSubnet returns the subnet of the desired mask in the IP space
|
||||
// just lower than the start of IPNet provided. If the IP space rolls over
|
||||
// then the second return value is true
|
||||
func PreviousSubnet(network *net.IPNet, prefixLen int) (*net.IPNet, bool) {
|
||||
startIP := checkIPv4(network.IP)
|
||||
previousIP := make(net.IP, len(startIP))
|
||||
copy(previousIP, startIP)
|
||||
cMask := net.CIDRMask(prefixLen, 8*len(previousIP))
|
||||
previousIP = Dec(previousIP)
|
||||
previous := &net.IPNet{IP: previousIP.Mask(cMask), Mask: cMask}
|
||||
if startIP.Equal(net.IPv4zero) || startIP.Equal(net.IPv6zero) {
|
||||
return previous, true
|
||||
}
|
||||
return previous, false
|
||||
}
|
||||
|
||||
// NextSubnet returns the next available subnet of the desired mask size
|
||||
// starting for the maximum IP of the offset subnet
|
||||
// If the IP exceeds the maxium IP then the second return value is true
|
||||
func NextSubnet(network *net.IPNet, prefixLen int) (*net.IPNet, bool) {
|
||||
_, currentLast := AddressRange(network)
|
||||
mask := net.CIDRMask(prefixLen, 8*len(currentLast))
|
||||
currentSubnet := &net.IPNet{IP: currentLast.Mask(mask), Mask: mask}
|
||||
_, last := AddressRange(currentSubnet)
|
||||
last = Inc(last)
|
||||
next := &net.IPNet{IP: last.Mask(mask), Mask: mask}
|
||||
if last.Equal(net.IPv4zero) || last.Equal(net.IPv6zero) {
|
||||
return next, true
|
||||
}
|
||||
return next, false
|
||||
}
|
||||
|
||||
//Inc increases the IP by one this returns a new []byte for the IP
|
||||
func Inc(IP net.IP) net.IP {
|
||||
IP = checkIPv4(IP)
|
||||
incIP := make([]byte, len(IP))
|
||||
copy(incIP, IP)
|
||||
for j := len(incIP) - 1; j >= 0; j-- {
|
||||
incIP[j]++
|
||||
if incIP[j] > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return incIP
|
||||
}
|
||||
|
||||
//Dec decreases the IP by one this returns a new []byte for the IP
|
||||
func Dec(IP net.IP) net.IP {
|
||||
IP = checkIPv4(IP)
|
||||
decIP := make([]byte, len(IP))
|
||||
copy(decIP, IP)
|
||||
decIP = checkIPv4(decIP)
|
||||
for j := len(decIP) - 1; j >= 0; j-- {
|
||||
decIP[j]--
|
||||
if decIP[j] < 255 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return decIP
|
||||
}
|
||||
|
||||
func checkIPv4(ip net.IP) net.IP {
|
||||
// Go for some reason allocs IPv6len for IPv4 so we have to correct it
|
||||
if v4 := ip.To4(); v4 != nil {
|
||||
return v4
|
||||
}
|
||||
return ip
|
||||
}
|
||||
37
vendor/github.com/apparentlymart/go-cidr/cidr/wrangling.go
generated
vendored
Normal file
37
vendor/github.com/apparentlymart/go-cidr/cidr/wrangling.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package cidr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
)
|
||||
|
||||
func ipToInt(ip net.IP) (*big.Int, int) {
|
||||
val := &big.Int{}
|
||||
val.SetBytes([]byte(ip))
|
||||
if len(ip) == net.IPv4len {
|
||||
return val, 32
|
||||
} else if len(ip) == net.IPv6len {
|
||||
return val, 128
|
||||
} else {
|
||||
panic(fmt.Errorf("Unsupported address length %d", len(ip)))
|
||||
}
|
||||
}
|
||||
|
||||
func intToIP(ipInt *big.Int, bits int) net.IP {
|
||||
ipBytes := ipInt.Bytes()
|
||||
ret := make([]byte, bits/8)
|
||||
// Pack our IP bytes into the end of the return array,
|
||||
// since big.Int.Bytes() removes front zero padding.
|
||||
for i := 1; i <= len(ipBytes); i++ {
|
||||
ret[len(ret)-i] = ipBytes[len(ipBytes)-i]
|
||||
}
|
||||
return net.IP(ret)
|
||||
}
|
||||
|
||||
func insertNumIntoIP(ip net.IP, bigNum *big.Int, prefixLen int) net.IP {
|
||||
ipInt, totalBits := ipToInt(ip)
|
||||
bigNum.Lsh(bigNum, uint(totalBits-prefixLen))
|
||||
ipInt.Or(ipInt, bigNum)
|
||||
return intToIP(ipInt, totalBits)
|
||||
}
|
||||
32
vendor/github.com/bmatcuk/doublestar/.gitignore
generated
vendored
Normal file
32
vendor/github.com/bmatcuk/doublestar/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# vi
|
||||
*~
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
# test directory
|
||||
test/
|
||||
15
vendor/github.com/bmatcuk/doublestar/.travis.yml
generated
vendored
Normal file
15
vendor/github.com/bmatcuk/doublestar/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.11
|
||||
- 1.12
|
||||
|
||||
before_install:
|
||||
- go get -t -v ./...
|
||||
|
||||
script:
|
||||
- go test -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
22
vendor/github.com/bmatcuk/doublestar/LICENSE
generated
vendored
Normal file
22
vendor/github.com/bmatcuk/doublestar/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Bob Matcuk
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
109
vendor/github.com/bmatcuk/doublestar/README.md
generated
vendored
Normal file
109
vendor/github.com/bmatcuk/doublestar/README.md
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||

|
||||
[](https://travis-ci.org/bmatcuk/doublestar)
|
||||
[](https://codecov.io/github/bmatcuk/doublestar?branch=master)
|
||||
|
||||
# doublestar
|
||||
|
||||
**doublestar** is a [golang](http://golang.org/) implementation of path pattern
|
||||
matching and globbing with support for "doublestar" (aka globstar: `**`)
|
||||
patterns.
|
||||
|
||||
doublestar patterns match files and directories recursively. For example, if
|
||||
you had the following directory structure:
|
||||
|
||||
```
|
||||
grandparent
|
||||
`-- parent
|
||||
|-- child1
|
||||
`-- child2
|
||||
```
|
||||
|
||||
You could find the children with patterns such as: `**/child*`,
|
||||
`grandparent/**/child?`, `**/parent/*`, or even just `**` by itself (which will
|
||||
return all files and directories recursively).
|
||||
|
||||
Bash's globstar is doublestar's inspiration and, as such, works similarly.
|
||||
Note that the doublestar must appear as a path component by itself. A pattern
|
||||
such as `/path**` is invalid and will be treated the same as `/path*`, but
|
||||
`/path*/**` should achieve the desired result. Additionally, `/path/**` will
|
||||
match all directories and files under the path directory, but `/path/**/` will
|
||||
only match directories.
|
||||
|
||||
## Installation
|
||||
|
||||
**doublestar** can be installed via `go get`:
|
||||
|
||||
```bash
|
||||
go get github.com/bmatcuk/doublestar
|
||||
```
|
||||
|
||||
To use it in your code, you must import it:
|
||||
|
||||
```go
|
||||
import "github.com/bmatcuk/doublestar"
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
### Match
|
||||
```go
|
||||
func Match(pattern, name string) (bool, error)
|
||||
```
|
||||
|
||||
Match returns true if `name` matches the file name `pattern`
|
||||
([see below](#patterns)). `name` and `pattern` are split on forward slash (`/`)
|
||||
characters and may be relative or absolute.
|
||||
|
||||
Note: `Match()` is meant to be a drop-in replacement for `path.Match()`. As
|
||||
such, it always uses `/` as the path separator. If you are writing code that
|
||||
will run on systems where `/` is not the path separator (such as Windows), you
|
||||
want to use `PathMatch()` (below) instead.
|
||||
|
||||
|
||||
### PathMatch
|
||||
```go
|
||||
func PathMatch(pattern, name string) (bool, error)
|
||||
```
|
||||
|
||||
PathMatch returns true if `name` matches the file name `pattern`
|
||||
([see below](#patterns)). The difference between Match and PathMatch is that
|
||||
PathMatch will automatically use your system's path separator to split `name`
|
||||
and `pattern`.
|
||||
|
||||
`PathMatch()` is meant to be a drop-in replacement for `filepath.Match()`.
|
||||
|
||||
### Glob
|
||||
```go
|
||||
func Glob(pattern string) ([]string, error)
|
||||
```
|
||||
|
||||
Glob finds all files and directories in the filesystem that match `pattern`
|
||||
([see below](#patterns)). `pattern` may be relative (to the current working
|
||||
directory), or absolute.
|
||||
|
||||
`Glob()` is meant to be a drop-in replacement for `filepath.Glob()`.
|
||||
|
||||
## Patterns
|
||||
|
||||
**doublestar** supports the following special terms in the patterns:
|
||||
|
||||
Special Terms | Meaning
|
||||
------------- | -------
|
||||
`*` | matches any sequence of non-path-separators
|
||||
`**` | matches any sequence of characters, including path separators
|
||||
`?` | matches any single non-path-separator character
|
||||
`[class]` | matches any single non-path-separator character against a class of characters ([see below](#character-classes))
|
||||
`{alt1,...}` | matches a sequence of characters if one of the comma-separated alternatives matches
|
||||
|
||||
Any character with a special meaning can be escaped with a backslash (`\`).
|
||||
|
||||
### Character Classes
|
||||
|
||||
Character classes support the following:
|
||||
|
||||
Class | Meaning
|
||||
---------- | -------
|
||||
`[abc]` | matches any single character within the set
|
||||
`[a-z]` | matches any single character in the range
|
||||
`[^class]` | matches any single character which does *not* match the class
|
||||
|
||||
476
vendor/github.com/bmatcuk/doublestar/doublestar.go
generated
vendored
Normal file
476
vendor/github.com/bmatcuk/doublestar/doublestar.go
generated
vendored
Normal file
@@ -0,0 +1,476 @@
|
||||
package doublestar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// ErrBadPattern indicates a pattern was malformed.
|
||||
var ErrBadPattern = path.ErrBadPattern
|
||||
|
||||
// Split a path on the given separator, respecting escaping.
|
||||
func splitPathOnSeparator(path string, separator rune) (ret []string) {
|
||||
idx := 0
|
||||
if separator == '\\' {
|
||||
// if the separator is '\\', then we can just split...
|
||||
ret = strings.Split(path, string(separator))
|
||||
idx = len(ret)
|
||||
} else {
|
||||
// otherwise, we need to be careful of situations where the separator was escaped
|
||||
cnt := strings.Count(path, string(separator))
|
||||
if cnt == 0 {
|
||||
return []string{path}
|
||||
}
|
||||
|
||||
ret = make([]string, cnt+1)
|
||||
pathlen := len(path)
|
||||
separatorLen := utf8.RuneLen(separator)
|
||||
emptyEnd := false
|
||||
for start := 0; start < pathlen; {
|
||||
end := indexRuneWithEscaping(path[start:], separator)
|
||||
if end == -1 {
|
||||
emptyEnd = false
|
||||
end = pathlen
|
||||
} else {
|
||||
emptyEnd = true
|
||||
end += start
|
||||
}
|
||||
ret[idx] = path[start:end]
|
||||
start = end + separatorLen
|
||||
idx++
|
||||
}
|
||||
|
||||
// If the last rune is a path separator, we need to append an empty string to
|
||||
// represent the last, empty path component. By default, the strings from
|
||||
// make([]string, ...) will be empty, so we just need to icrement the count
|
||||
if emptyEnd {
|
||||
idx++
|
||||
}
|
||||
}
|
||||
|
||||
return ret[:idx]
|
||||
}
|
||||
|
||||
// Find the first index of a rune in a string,
|
||||
// ignoring any times the rune is escaped using "\".
|
||||
func indexRuneWithEscaping(s string, r rune) int {
|
||||
end := strings.IndexRune(s, r)
|
||||
if end == -1 {
|
||||
return -1
|
||||
}
|
||||
if end > 0 && s[end-1] == '\\' {
|
||||
start := end + utf8.RuneLen(r)
|
||||
end = indexRuneWithEscaping(s[start:], r)
|
||||
if end != -1 {
|
||||
end += start
|
||||
}
|
||||
}
|
||||
return end
|
||||
}
|
||||
|
||||
// Match returns true if name matches the shell file name pattern.
|
||||
// The pattern syntax is:
|
||||
//
|
||||
// pattern:
|
||||
// { term }
|
||||
// term:
|
||||
// '*' matches any sequence of non-path-separators
|
||||
// '**' matches any sequence of characters, including
|
||||
// path separators.
|
||||
// '?' matches any single non-path-separator character
|
||||
// '[' [ '^' ] { character-range } ']'
|
||||
// character class (must be non-empty)
|
||||
// '{' { term } [ ',' { term } ... ] '}'
|
||||
// c matches character c (c != '*', '?', '\\', '[')
|
||||
// '\\' c matches character c
|
||||
//
|
||||
// character-range:
|
||||
// c matches character c (c != '\\', '-', ']')
|
||||
// '\\' c matches character c
|
||||
// lo '-' hi matches character c for lo <= c <= hi
|
||||
//
|
||||
// Match requires pattern to match all of name, not just a substring.
|
||||
// The path-separator defaults to the '/' character. The only possible
|
||||
// returned error is ErrBadPattern, when pattern is malformed.
|
||||
//
|
||||
// Note: this is meant as a drop-in replacement for path.Match() which
|
||||
// always uses '/' as the path separator. If you want to support systems
|
||||
// which use a different path separator (such as Windows), what you want
|
||||
// is the PathMatch() function below.
|
||||
//
|
||||
func Match(pattern, name string) (bool, error) {
|
||||
return matchWithSeparator(pattern, name, '/')
|
||||
}
|
||||
|
||||
// PathMatch is like Match except that it uses your system's path separator.
|
||||
// For most systems, this will be '/'. However, for Windows, it would be '\\'.
|
||||
// Note that for systems where the path separator is '\\', escaping is
|
||||
// disabled.
|
||||
//
|
||||
// Note: this is meant as a drop-in replacement for filepath.Match().
|
||||
//
|
||||
func PathMatch(pattern, name string) (bool, error) {
|
||||
return matchWithSeparator(pattern, name, os.PathSeparator)
|
||||
}
|
||||
|
||||
// Match returns true if name matches the shell file name pattern.
|
||||
// The pattern syntax is:
|
||||
//
|
||||
// pattern:
|
||||
// { term }
|
||||
// term:
|
||||
// '*' matches any sequence of non-path-separators
|
||||
// '**' matches any sequence of characters, including
|
||||
// path separators.
|
||||
// '?' matches any single non-path-separator character
|
||||
// '[' [ '^' ] { character-range } ']'
|
||||
// character class (must be non-empty)
|
||||
// '{' { term } [ ',' { term } ... ] '}'
|
||||
// c matches character c (c != '*', '?', '\\', '[')
|
||||
// '\\' c matches character c
|
||||
//
|
||||
// character-range:
|
||||
// c matches character c (c != '\\', '-', ']')
|
||||
// '\\' c matches character c, unless separator is '\\'
|
||||
// lo '-' hi matches character c for lo <= c <= hi
|
||||
//
|
||||
// Match requires pattern to match all of name, not just a substring.
|
||||
// The only possible returned error is ErrBadPattern, when pattern
|
||||
// is malformed.
|
||||
//
|
||||
func matchWithSeparator(pattern, name string, separator rune) (bool, error) {
|
||||
patternComponents := splitPathOnSeparator(pattern, separator)
|
||||
nameComponents := splitPathOnSeparator(name, separator)
|
||||
return doMatching(patternComponents, nameComponents)
|
||||
}
|
||||
|
||||
func doMatching(patternComponents, nameComponents []string) (matched bool, err error) {
|
||||
// check for some base-cases
|
||||
patternLen, nameLen := len(patternComponents), len(nameComponents)
|
||||
if patternLen == 0 && nameLen == 0 {
|
||||
return true, nil
|
||||
}
|
||||
if patternLen == 0 || nameLen == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
patIdx, nameIdx := 0, 0
|
||||
for patIdx < patternLen && nameIdx < nameLen {
|
||||
if patternComponents[patIdx] == "**" {
|
||||
// if our last pattern component is a doublestar, we're done -
|
||||
// doublestar will match any remaining name components, if any.
|
||||
if patIdx++; patIdx >= patternLen {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// otherwise, try matching remaining components
|
||||
for ; nameIdx < nameLen; nameIdx++ {
|
||||
if m, _ := doMatching(patternComponents[patIdx:], nameComponents[nameIdx:]); m {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// try matching components
|
||||
matched, err = matchComponent(patternComponents[patIdx], nameComponents[nameIdx])
|
||||
if !matched || err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
patIdx++
|
||||
nameIdx++
|
||||
}
|
||||
return patIdx >= patternLen && nameIdx >= nameLen, nil
|
||||
}
|
||||
|
||||
// Glob returns the names of all files matching pattern or nil
|
||||
// if there is no matching file. The syntax of pattern is the same
|
||||
// as in Match. The pattern may describe hierarchical names such as
|
||||
// /usr/*/bin/ed (assuming the Separator is '/').
|
||||
//
|
||||
// Glob ignores file system errors such as I/O errors reading directories.
|
||||
// The only possible returned error is ErrBadPattern, when pattern
|
||||
// is malformed.
|
||||
//
|
||||
// Your system path separator is automatically used. This means on
|
||||
// systems where the separator is '\\' (Windows), escaping will be
|
||||
// disabled.
|
||||
//
|
||||
// Note: this is meant as a drop-in replacement for filepath.Glob().
|
||||
//
|
||||
func Glob(pattern string) (matches []string, err error) {
|
||||
patternComponents := splitPathOnSeparator(filepath.ToSlash(pattern), '/')
|
||||
if len(patternComponents) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// On Windows systems, this will return the drive name ('C:') for filesystem
|
||||
// paths, or \\<server>\<share> for UNC paths. On other systems, it will
|
||||
// return an empty string. Since absolute paths on non-Windows systems start
|
||||
// with a slash, patternComponent[0] == volumeName will return true for both
|
||||
// absolute Windows paths and absolute non-Windows paths, but we need a
|
||||
// separate check for UNC paths.
|
||||
volumeName := filepath.VolumeName(pattern)
|
||||
isWindowsUNC := strings.HasPrefix(pattern, `\\`)
|
||||
if isWindowsUNC || patternComponents[0] == volumeName {
|
||||
startComponentIndex := 1
|
||||
if isWindowsUNC {
|
||||
startComponentIndex = 4
|
||||
}
|
||||
return doGlob(fmt.Sprintf("%s%s", volumeName, string(os.PathSeparator)), patternComponents[startComponentIndex:], matches)
|
||||
}
|
||||
|
||||
// otherwise, it's a relative pattern
|
||||
return doGlob(".", patternComponents, matches)
|
||||
}
|
||||
|
||||
// Perform a glob
|
||||
func doGlob(basedir string, components, matches []string) (m []string, e error) {
|
||||
m = matches
|
||||
e = nil
|
||||
|
||||
// figure out how many components we don't need to glob because they're
|
||||
// just names without patterns - we'll use os.Lstat below to check if that
|
||||
// path actually exists
|
||||
patLen := len(components)
|
||||
patIdx := 0
|
||||
for ; patIdx < patLen; patIdx++ {
|
||||
if strings.IndexAny(components[patIdx], "*?[{\\") >= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if patIdx > 0 {
|
||||
basedir = filepath.Join(basedir, filepath.Join(components[0:patIdx]...))
|
||||
}
|
||||
|
||||
// Lstat will return an error if the file/directory doesn't exist
|
||||
fi, err := os.Lstat(basedir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// if there are no more components, we've found a match
|
||||
if patIdx >= patLen {
|
||||
m = append(m, basedir)
|
||||
return
|
||||
}
|
||||
|
||||
// otherwise, we need to check each item in the directory...
|
||||
// first, if basedir is a symlink, follow it...
|
||||
if (fi.Mode() & os.ModeSymlink) != 0 {
|
||||
fi, err = os.Stat(basedir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// confirm it's a directory...
|
||||
if !fi.IsDir() {
|
||||
return
|
||||
}
|
||||
|
||||
// read directory
|
||||
dir, err := os.Open(basedir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
files, _ := dir.Readdir(-1)
|
||||
lastComponent := (patIdx + 1) >= patLen
|
||||
if components[patIdx] == "**" {
|
||||
// if the current component is a doublestar, we'll try depth-first
|
||||
for _, file := range files {
|
||||
// if symlink, we may want to follow
|
||||
if (file.Mode() & os.ModeSymlink) != 0 {
|
||||
file, err = os.Stat(filepath.Join(basedir, file.Name()))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if file.IsDir() {
|
||||
// recurse into directories
|
||||
if lastComponent {
|
||||
m = append(m, filepath.Join(basedir, file.Name()))
|
||||
}
|
||||
m, e = doGlob(filepath.Join(basedir, file.Name()), components[patIdx:], m)
|
||||
} else if lastComponent {
|
||||
// if the pattern's last component is a doublestar, we match filenames, too
|
||||
m = append(m, filepath.Join(basedir, file.Name()))
|
||||
}
|
||||
}
|
||||
if lastComponent {
|
||||
return // we're done
|
||||
}
|
||||
patIdx++
|
||||
lastComponent = (patIdx + 1) >= patLen
|
||||
}
|
||||
|
||||
// check items in current directory and recurse
|
||||
var match bool
|
||||
for _, file := range files {
|
||||
match, e = matchComponent(components[patIdx], file.Name())
|
||||
if e != nil {
|
||||
return
|
||||
}
|
||||
if match {
|
||||
if lastComponent {
|
||||
m = append(m, filepath.Join(basedir, file.Name()))
|
||||
} else {
|
||||
m, e = doGlob(filepath.Join(basedir, file.Name()), components[patIdx+1:], m)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Attempt to match a single pattern component with a path component
|
||||
func matchComponent(pattern, name string) (bool, error) {
|
||||
// check some base cases
|
||||
patternLen, nameLen := len(pattern), len(name)
|
||||
if patternLen == 0 && nameLen == 0 {
|
||||
return true, nil
|
||||
}
|
||||
if patternLen == 0 {
|
||||
return false, nil
|
||||
}
|
||||
if nameLen == 0 && pattern != "*" {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// check for matches one rune at a time
|
||||
patIdx, nameIdx := 0, 0
|
||||
for patIdx < patternLen && nameIdx < nameLen {
|
||||
patRune, patAdj := utf8.DecodeRuneInString(pattern[patIdx:])
|
||||
nameRune, nameAdj := utf8.DecodeRuneInString(name[nameIdx:])
|
||||
if patRune == '\\' {
|
||||
// handle escaped runes
|
||||
patIdx += patAdj
|
||||
patRune, patAdj = utf8.DecodeRuneInString(pattern[patIdx:])
|
||||
if patRune == utf8.RuneError {
|
||||
return false, ErrBadPattern
|
||||
} else if patRune == nameRune {
|
||||
patIdx += patAdj
|
||||
nameIdx += nameAdj
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
} else if patRune == '*' {
|
||||
// handle stars
|
||||
if patIdx += patAdj; patIdx >= patternLen {
|
||||
// a star at the end of a pattern will always
|
||||
// match the rest of the path
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// check if we can make any matches
|
||||
for ; nameIdx < nameLen; nameIdx += nameAdj {
|
||||
if m, _ := matchComponent(pattern[patIdx:], name[nameIdx:]); m {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
} else if patRune == '[' {
|
||||
// handle character sets
|
||||
patIdx += patAdj
|
||||
endClass := indexRuneWithEscaping(pattern[patIdx:], ']')
|
||||
if endClass == -1 {
|
||||
return false, ErrBadPattern
|
||||
}
|
||||
endClass += patIdx
|
||||
classRunes := []rune(pattern[patIdx:endClass])
|
||||
classRunesLen := len(classRunes)
|
||||
if classRunesLen > 0 {
|
||||
classIdx := 0
|
||||
matchClass := false
|
||||
if classRunes[0] == '^' {
|
||||
classIdx++
|
||||
}
|
||||
for classIdx < classRunesLen {
|
||||
low := classRunes[classIdx]
|
||||
if low == '-' {
|
||||
return false, ErrBadPattern
|
||||
}
|
||||
classIdx++
|
||||
if low == '\\' {
|
||||
if classIdx < classRunesLen {
|
||||
low = classRunes[classIdx]
|
||||
classIdx++
|
||||
} else {
|
||||
return false, ErrBadPattern
|
||||
}
|
||||
}
|
||||
high := low
|
||||
if classIdx < classRunesLen && classRunes[classIdx] == '-' {
|
||||
// we have a range of runes
|
||||
if classIdx++; classIdx >= classRunesLen {
|
||||
return false, ErrBadPattern
|
||||
}
|
||||
high = classRunes[classIdx]
|
||||
if high == '-' {
|
||||
return false, ErrBadPattern
|
||||
}
|
||||
classIdx++
|
||||
if high == '\\' {
|
||||
if classIdx < classRunesLen {
|
||||
high = classRunes[classIdx]
|
||||
classIdx++
|
||||
} else {
|
||||
return false, ErrBadPattern
|
||||
}
|
||||
}
|
||||
}
|
||||
if low <= nameRune && nameRune <= high {
|
||||
matchClass = true
|
||||
}
|
||||
}
|
||||
if matchClass == (classRunes[0] == '^') {
|
||||
return false, nil
|
||||
}
|
||||
} else {
|
||||
return false, ErrBadPattern
|
||||
}
|
||||
patIdx = endClass + 1
|
||||
nameIdx += nameAdj
|
||||
} else if patRune == '{' {
|
||||
// handle alternatives such as {alt1,alt2,...}
|
||||
patIdx += patAdj
|
||||
endOptions := indexRuneWithEscaping(pattern[patIdx:], '}')
|
||||
if endOptions == -1 {
|
||||
return false, ErrBadPattern
|
||||
}
|
||||
endOptions += patIdx
|
||||
options := splitPathOnSeparator(pattern[patIdx:endOptions], ',')
|
||||
patIdx = endOptions + 1
|
||||
for _, o := range options {
|
||||
m, e := matchComponent(o+pattern[patIdx:], name[nameIdx:])
|
||||
if e != nil {
|
||||
return false, e
|
||||
}
|
||||
if m {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
} else if patRune == '?' || patRune == nameRune {
|
||||
// handle single-rune wildcard
|
||||
patIdx += patAdj
|
||||
nameIdx += nameAdj
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
if patIdx >= patternLen && nameIdx >= nameLen {
|
||||
return true, nil
|
||||
}
|
||||
if nameIdx >= nameLen && pattern[patIdx:] == "*" || pattern[patIdx:] == "**" {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
3
vendor/github.com/bmatcuk/doublestar/go.mod
generated
vendored
Normal file
3
vendor/github.com/bmatcuk/doublestar/go.mod
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module github.com/bmatcuk/doublestar
|
||||
|
||||
go 1.12
|
||||
9
vendor/github.com/google/uuid/.travis.yml
generated
vendored
Normal file
9
vendor/github.com/google/uuid/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4.3
|
||||
- 1.5.3
|
||||
- tip
|
||||
|
||||
script:
|
||||
- go test -v ./...
|
||||
10
vendor/github.com/google/uuid/CONTRIBUTING.md
generated
vendored
Normal file
10
vendor/github.com/google/uuid/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# How to contribute
|
||||
|
||||
We definitely welcome patches and contribution to this project!
|
||||
|
||||
### Legal requirements
|
||||
|
||||
In order to protect both you and ourselves, you will need to sign the
|
||||
[Contributor License Agreement](https://cla.developers.google.com/clas).
|
||||
|
||||
You may have already signed it for other Google projects.
|
||||
9
vendor/github.com/google/uuid/CONTRIBUTORS
generated
vendored
Normal file
9
vendor/github.com/google/uuid/CONTRIBUTORS
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
Paul Borman <borman@google.com>
|
||||
bmatsuo
|
||||
shawnps
|
||||
theory
|
||||
jboverfelt
|
||||
dsymonds
|
||||
cd1
|
||||
wallclockbuilder
|
||||
dansouza
|
||||
27
vendor/github.com/google/uuid/LICENSE
generated
vendored
Normal file
27
vendor/github.com/google/uuid/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009,2014 Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
19
vendor/github.com/google/uuid/README.md
generated
vendored
Normal file
19
vendor/github.com/google/uuid/README.md
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# uuid 
|
||||
The uuid package generates and inspects UUIDs based on
|
||||
[RFC 4122](http://tools.ietf.org/html/rfc4122)
|
||||
and DCE 1.1: Authentication and Security Services.
|
||||
|
||||
This package is based on the github.com/pborman/uuid package (previously named
|
||||
code.google.com/p/go-uuid). It differs from these earlier packages in that
|
||||
a UUID is a 16 byte array rather than a byte slice. One loss due to this
|
||||
change is the ability to represent an invalid UUID (vs a NIL UUID).
|
||||
|
||||
###### Install
|
||||
`go get github.com/google/uuid`
|
||||
|
||||
###### Documentation
|
||||
[](http://godoc.org/github.com/google/uuid)
|
||||
|
||||
Full `go doc` style documentation for the package can be viewed online without
|
||||
installing this package by using the GoDoc site here:
|
||||
http://godoc.org/github.com/google/uuid
|
||||
80
vendor/github.com/google/uuid/dce.go
generated
vendored
Normal file
80
vendor/github.com/google/uuid/dce.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// A Domain represents a Version 2 domain
|
||||
type Domain byte
|
||||
|
||||
// Domain constants for DCE Security (Version 2) UUIDs.
|
||||
const (
|
||||
Person = Domain(0)
|
||||
Group = Domain(1)
|
||||
Org = Domain(2)
|
||||
)
|
||||
|
||||
// NewDCESecurity returns a DCE Security (Version 2) UUID.
|
||||
//
|
||||
// The domain should be one of Person, Group or Org.
|
||||
// On a POSIX system the id should be the users UID for the Person
|
||||
// domain and the users GID for the Group. The meaning of id for
|
||||
// the domain Org or on non-POSIX systems is site defined.
|
||||
//
|
||||
// For a given domain/id pair the same token may be returned for up to
|
||||
// 7 minutes and 10 seconds.
|
||||
func NewDCESecurity(domain Domain, id uint32) (UUID, error) {
|
||||
uuid, err := NewUUID()
|
||||
if err == nil {
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
|
||||
uuid[9] = byte(domain)
|
||||
binary.BigEndian.PutUint32(uuid[0:], id)
|
||||
}
|
||||
return uuid, err
|
||||
}
|
||||
|
||||
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
|
||||
// domain with the id returned by os.Getuid.
|
||||
//
|
||||
// NewDCESecurity(Person, uint32(os.Getuid()))
|
||||
func NewDCEPerson() (UUID, error) {
|
||||
return NewDCESecurity(Person, uint32(os.Getuid()))
|
||||
}
|
||||
|
||||
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
|
||||
// domain with the id returned by os.Getgid.
|
||||
//
|
||||
// NewDCESecurity(Group, uint32(os.Getgid()))
|
||||
func NewDCEGroup() (UUID, error) {
|
||||
return NewDCESecurity(Group, uint32(os.Getgid()))
|
||||
}
|
||||
|
||||
// Domain returns the domain for a Version 2 UUID. Domains are only defined
|
||||
// for Version 2 UUIDs.
|
||||
func (uuid UUID) Domain() Domain {
|
||||
return Domain(uuid[9])
|
||||
}
|
||||
|
||||
// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2
|
||||
// UUIDs.
|
||||
func (uuid UUID) ID() uint32 {
|
||||
return binary.BigEndian.Uint32(uuid[0:4])
|
||||
}
|
||||
|
||||
func (d Domain) String() string {
|
||||
switch d {
|
||||
case Person:
|
||||
return "Person"
|
||||
case Group:
|
||||
return "Group"
|
||||
case Org:
|
||||
return "Org"
|
||||
}
|
||||
return fmt.Sprintf("Domain%d", int(d))
|
||||
}
|
||||
12
vendor/github.com/google/uuid/doc.go
generated
vendored
Normal file
12
vendor/github.com/google/uuid/doc.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package uuid generates and inspects UUIDs.
|
||||
//
|
||||
// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security
|
||||
// Services.
|
||||
//
|
||||
// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to
|
||||
// maps or compared directly.
|
||||
package uuid
|
||||
1
vendor/github.com/google/uuid/go.mod
generated
vendored
Normal file
1
vendor/github.com/google/uuid/go.mod
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
module github.com/google/uuid
|
||||
53
vendor/github.com/google/uuid/hash.go
generated
vendored
Normal file
53
vendor/github.com/google/uuid/hash.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// Well known namespace IDs and UUIDs
|
||||
var (
|
||||
NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
|
||||
NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
|
||||
NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
|
||||
NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
|
||||
Nil UUID // empty UUID, all zeros
|
||||
)
|
||||
|
||||
// NewHash returns a new UUID derived from the hash of space concatenated with
|
||||
// data generated by h. The hash should be at least 16 byte in length. The
|
||||
// first 16 bytes of the hash are used to form the UUID. The version of the
|
||||
// UUID will be the lower 4 bits of version. NewHash is used to implement
|
||||
// NewMD5 and NewSHA1.
|
||||
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
|
||||
h.Reset()
|
||||
h.Write(space[:])
|
||||
h.Write(data)
|
||||
s := h.Sum(nil)
|
||||
var uuid UUID
|
||||
copy(uuid[:], s)
|
||||
uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
|
||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
|
||||
return uuid
|
||||
}
|
||||
|
||||
// NewMD5 returns a new MD5 (Version 3) UUID based on the
|
||||
// supplied name space and data. It is the same as calling:
|
||||
//
|
||||
// NewHash(md5.New(), space, data, 3)
|
||||
func NewMD5(space UUID, data []byte) UUID {
|
||||
return NewHash(md5.New(), space, data, 3)
|
||||
}
|
||||
|
||||
// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
|
||||
// supplied name space and data. It is the same as calling:
|
||||
//
|
||||
// NewHash(sha1.New(), space, data, 5)
|
||||
func NewSHA1(space UUID, data []byte) UUID {
|
||||
return NewHash(sha1.New(), space, data, 5)
|
||||
}
|
||||
37
vendor/github.com/google/uuid/marshal.go
generated
vendored
Normal file
37
vendor/github.com/google/uuid/marshal.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import "fmt"
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler.
|
||||
func (uuid UUID) MarshalText() ([]byte, error) {
|
||||
var js [36]byte
|
||||
encodeHex(js[:], uuid)
|
||||
return js[:], nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (uuid *UUID) UnmarshalText(data []byte) error {
|
||||
id, err := ParseBytes(data)
|
||||
if err == nil {
|
||||
*uuid = id
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler.
|
||||
func (uuid UUID) MarshalBinary() ([]byte, error) {
|
||||
return uuid[:], nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
|
||||
func (uuid *UUID) UnmarshalBinary(data []byte) error {
|
||||
if len(data) != 16 {
|
||||
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
|
||||
}
|
||||
copy(uuid[:], data)
|
||||
return nil
|
||||
}
|
||||
90
vendor/github.com/google/uuid/node.go
generated
vendored
Normal file
90
vendor/github.com/google/uuid/node.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
nodeMu sync.Mutex
|
||||
ifname string // name of interface being used
|
||||
nodeID [6]byte // hardware for version 1 UUIDs
|
||||
zeroID [6]byte // nodeID with only 0's
|
||||
)
|
||||
|
||||
// NodeInterface returns the name of the interface from which the NodeID was
|
||||
// derived. The interface "user" is returned if the NodeID was set by
|
||||
// SetNodeID.
|
||||
func NodeInterface() string {
|
||||
defer nodeMu.Unlock()
|
||||
nodeMu.Lock()
|
||||
return ifname
|
||||
}
|
||||
|
||||
// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
|
||||
// If name is "" then the first usable interface found will be used or a random
|
||||
// Node ID will be generated. If a named interface cannot be found then false
|
||||
// is returned.
|
||||
//
|
||||
// SetNodeInterface never fails when name is "".
|
||||
func SetNodeInterface(name string) bool {
|
||||
defer nodeMu.Unlock()
|
||||
nodeMu.Lock()
|
||||
return setNodeInterface(name)
|
||||
}
|
||||
|
||||
func setNodeInterface(name string) bool {
|
||||
iname, addr := getHardwareInterface(name) // null implementation for js
|
||||
if iname != "" && addr != nil {
|
||||
ifname = iname
|
||||
copy(nodeID[:], addr)
|
||||
return true
|
||||
}
|
||||
|
||||
// We found no interfaces with a valid hardware address. If name
|
||||
// does not specify a specific interface generate a random Node ID
|
||||
// (section 4.1.6)
|
||||
if name == "" {
|
||||
ifname = "random"
|
||||
randomBits(nodeID[:])
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
|
||||
// if not already set.
|
||||
func NodeID() []byte {
|
||||
defer nodeMu.Unlock()
|
||||
nodeMu.Lock()
|
||||
if nodeID == zeroID {
|
||||
setNodeInterface("")
|
||||
}
|
||||
nid := nodeID
|
||||
return nid[:]
|
||||
}
|
||||
|
||||
// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
|
||||
// of id are used. If id is less than 6 bytes then false is returned and the
|
||||
// Node ID is not set.
|
||||
func SetNodeID(id []byte) bool {
|
||||
if len(id) < 6 {
|
||||
return false
|
||||
}
|
||||
defer nodeMu.Unlock()
|
||||
nodeMu.Lock()
|
||||
copy(nodeID[:], id)
|
||||
ifname = "user"
|
||||
return true
|
||||
}
|
||||
|
||||
// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
|
||||
// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
|
||||
func (uuid UUID) NodeID() []byte {
|
||||
var node [6]byte
|
||||
copy(node[:], uuid[10:])
|
||||
return node[:]
|
||||
}
|
||||
12
vendor/github.com/google/uuid/node_js.go
generated
vendored
Normal file
12
vendor/github.com/google/uuid/node_js.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build js
|
||||
|
||||
package uuid
|
||||
|
||||
// getHardwareInterface returns nil values for the JS version of the code.
|
||||
// This remvoves the "net" dependency, because it is not used in the browser.
|
||||
// Using the "net" library inflates the size of the transpiled JS code by 673k bytes.
|
||||
func getHardwareInterface(name string) (string, []byte) { return "", nil }
|
||||
33
vendor/github.com/google/uuid/node_net.go
generated
vendored
Normal file
33
vendor/github.com/google/uuid/node_net.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !js
|
||||
|
||||
package uuid
|
||||
|
||||
import "net"
|
||||
|
||||
var interfaces []net.Interface // cached list of interfaces
|
||||
|
||||
// getHardwareInterface returns the name and hardware address of interface name.
|
||||
// If name is "" then the name and hardware address of one of the system's
|
||||
// interfaces is returned. If no interfaces are found (name does not exist or
|
||||
// there are no interfaces) then "", nil is returned.
|
||||
//
|
||||
// Only addresses of at least 6 bytes are returned.
|
||||
func getHardwareInterface(name string) (string, []byte) {
|
||||
if interfaces == nil {
|
||||
var err error
|
||||
interfaces, err = net.Interfaces()
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
for _, ifs := range interfaces {
|
||||
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
|
||||
return ifs.Name, ifs.HardwareAddr
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
59
vendor/github.com/google/uuid/sql.go
generated
vendored
Normal file
59
vendor/github.com/google/uuid/sql.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Scan implements sql.Scanner so UUIDs can be read from databases transparently
|
||||
// Currently, database types that map to string and []byte are supported. Please
|
||||
// consult database-specific driver documentation for matching types.
|
||||
func (uuid *UUID) Scan(src interface{}) error {
|
||||
switch src := src.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
|
||||
case string:
|
||||
// if an empty UUID comes from a table, we return a null UUID
|
||||
if src == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// see Parse for required string format
|
||||
u, err := Parse(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Scan: %v", err)
|
||||
}
|
||||
|
||||
*uuid = u
|
||||
|
||||
case []byte:
|
||||
// if an empty UUID comes from a table, we return a null UUID
|
||||
if len(src) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// assumes a simple slice of bytes if 16 bytes
|
||||
// otherwise attempts to parse
|
||||
if len(src) != 16 {
|
||||
return uuid.Scan(string(src))
|
||||
}
|
||||
copy((*uuid)[:], src)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements sql.Valuer so that UUIDs can be written to databases
|
||||
// transparently. Currently, UUIDs map to strings. Please consult
|
||||
// database-specific driver documentation for matching types.
|
||||
func (uuid UUID) Value() (driver.Value, error) {
|
||||
return uuid.String(), nil
|
||||
}
|
||||
123
vendor/github.com/google/uuid/time.go
generated
vendored
Normal file
123
vendor/github.com/google/uuid/time.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
|
||||
// 1582.
|
||||
type Time int64
|
||||
|
||||
const (
|
||||
lillian = 2299160 // Julian day of 15 Oct 1582
|
||||
unix = 2440587 // Julian day of 1 Jan 1970
|
||||
epoch = unix - lillian // Days between epochs
|
||||
g1582 = epoch * 86400 // seconds between epochs
|
||||
g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
|
||||
)
|
||||
|
||||
var (
|
||||
timeMu sync.Mutex
|
||||
lasttime uint64 // last time we returned
|
||||
clockSeq uint16 // clock sequence for this run
|
||||
|
||||
timeNow = time.Now // for testing
|
||||
)
|
||||
|
||||
// UnixTime converts t the number of seconds and nanoseconds using the Unix
|
||||
// epoch of 1 Jan 1970.
|
||||
func (t Time) UnixTime() (sec, nsec int64) {
|
||||
sec = int64(t - g1582ns100)
|
||||
nsec = (sec % 10000000) * 100
|
||||
sec /= 10000000
|
||||
return sec, nsec
|
||||
}
|
||||
|
||||
// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
|
||||
// clock sequence as well as adjusting the clock sequence as needed. An error
|
||||
// is returned if the current time cannot be determined.
|
||||
func GetTime() (Time, uint16, error) {
|
||||
defer timeMu.Unlock()
|
||||
timeMu.Lock()
|
||||
return getTime()
|
||||
}
|
||||
|
||||
func getTime() (Time, uint16, error) {
|
||||
t := timeNow()
|
||||
|
||||
// If we don't have a clock sequence already, set one.
|
||||
if clockSeq == 0 {
|
||||
setClockSequence(-1)
|
||||
}
|
||||
now := uint64(t.UnixNano()/100) + g1582ns100
|
||||
|
||||
// If time has gone backwards with this clock sequence then we
|
||||
// increment the clock sequence
|
||||
if now <= lasttime {
|
||||
clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000
|
||||
}
|
||||
lasttime = now
|
||||
return Time(now), clockSeq, nil
|
||||
}
|
||||
|
||||
// ClockSequence returns the current clock sequence, generating one if not
|
||||
// already set. The clock sequence is only used for Version 1 UUIDs.
|
||||
//
|
||||
// The uuid package does not use global static storage for the clock sequence or
|
||||
// the last time a UUID was generated. Unless SetClockSequence is used, a new
|
||||
// random clock sequence is generated the first time a clock sequence is
|
||||
// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1)
|
||||
func ClockSequence() int {
|
||||
defer timeMu.Unlock()
|
||||
timeMu.Lock()
|
||||
return clockSequence()
|
||||
}
|
||||
|
||||
func clockSequence() int {
|
||||
if clockSeq == 0 {
|
||||
setClockSequence(-1)
|
||||
}
|
||||
return int(clockSeq & 0x3fff)
|
||||
}
|
||||
|
||||
// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to
|
||||
// -1 causes a new sequence to be generated.
|
||||
func SetClockSequence(seq int) {
|
||||
defer timeMu.Unlock()
|
||||
timeMu.Lock()
|
||||
setClockSequence(seq)
|
||||
}
|
||||
|
||||
func setClockSequence(seq int) {
|
||||
if seq == -1 {
|
||||
var b [2]byte
|
||||
randomBits(b[:]) // clock sequence
|
||||
seq = int(b[0])<<8 | int(b[1])
|
||||
}
|
||||
oldSeq := clockSeq
|
||||
clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant
|
||||
if oldSeq != clockSeq {
|
||||
lasttime = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
|
||||
// uuid. The time is only defined for version 1 and 2 UUIDs.
|
||||
func (uuid UUID) Time() Time {
|
||||
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
|
||||
return Time(time)
|
||||
}
|
||||
|
||||
// ClockSequence returns the clock sequence encoded in uuid.
|
||||
// The clock sequence is only well defined for version 1 and 2 UUIDs.
|
||||
func (uuid UUID) ClockSequence() int {
|
||||
return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff
|
||||
}
|
||||
43
vendor/github.com/google/uuid/util.go
generated
vendored
Normal file
43
vendor/github.com/google/uuid/util.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// randomBits completely fills slice b with random data.
|
||||
func randomBits(b []byte) {
|
||||
if _, err := io.ReadFull(rander, b); err != nil {
|
||||
panic(err.Error()) // rand should never fail
|
||||
}
|
||||
}
|
||||
|
||||
// xvalues returns the value of a byte as a hexadecimal digit or 255.
|
||||
var xvalues = [256]byte{
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
|
||||
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
}
|
||||
|
||||
// xtob converts hex characters x1 and x2 into a byte.
|
||||
func xtob(x1, x2 byte) (byte, bool) {
|
||||
b1 := xvalues[x1]
|
||||
b2 := xvalues[x2]
|
||||
return (b1 << 4) | b2, b1 != 255 && b2 != 255
|
||||
}
|
||||
245
vendor/github.com/google/uuid/uuid.go
generated
vendored
Normal file
245
vendor/github.com/google/uuid/uuid.go
generated
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
// Copyright 2018 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
|
||||
// 4122.
|
||||
type UUID [16]byte
|
||||
|
||||
// A Version represents a UUID's version.
|
||||
type Version byte
|
||||
|
||||
// A Variant represents a UUID's variant.
|
||||
type Variant byte
|
||||
|
||||
// Constants returned by Variant.
|
||||
const (
|
||||
Invalid = Variant(iota) // Invalid UUID
|
||||
RFC4122 // The variant specified in RFC4122
|
||||
Reserved // Reserved, NCS backward compatibility.
|
||||
Microsoft // Reserved, Microsoft Corporation backward compatibility.
|
||||
Future // Reserved for future definition.
|
||||
)
|
||||
|
||||
var rander = rand.Reader // random function
|
||||
|
||||
// Parse decodes s into a UUID or returns an error. Both the standard UUID
|
||||
// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the
|
||||
// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex
|
||||
// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
|
||||
func Parse(s string) (UUID, error) {
|
||||
var uuid UUID
|
||||
switch len(s) {
|
||||
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
case 36:
|
||||
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
case 36 + 9:
|
||||
if strings.ToLower(s[:9]) != "urn:uuid:" {
|
||||
return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
|
||||
}
|
||||
s = s[9:]
|
||||
|
||||
// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
|
||||
case 36 + 2:
|
||||
s = s[1:]
|
||||
|
||||
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
case 32:
|
||||
var ok bool
|
||||
for i := range uuid {
|
||||
uuid[i], ok = xtob(s[i*2], s[i*2+1])
|
||||
if !ok {
|
||||
return uuid, errors.New("invalid UUID format")
|
||||
}
|
||||
}
|
||||
return uuid, nil
|
||||
default:
|
||||
return uuid, fmt.Errorf("invalid UUID length: %d", len(s))
|
||||
}
|
||||
// s is now at least 36 bytes long
|
||||
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
||||
return uuid, errors.New("invalid UUID format")
|
||||
}
|
||||
for i, x := range [16]int{
|
||||
0, 2, 4, 6,
|
||||
9, 11,
|
||||
14, 16,
|
||||
19, 21,
|
||||
24, 26, 28, 30, 32, 34} {
|
||||
v, ok := xtob(s[x], s[x+1])
|
||||
if !ok {
|
||||
return uuid, errors.New("invalid UUID format")
|
||||
}
|
||||
uuid[i] = v
|
||||
}
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
// ParseBytes is like Parse, except it parses a byte slice instead of a string.
|
||||
func ParseBytes(b []byte) (UUID, error) {
|
||||
var uuid UUID
|
||||
switch len(b) {
|
||||
case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) {
|
||||
return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
|
||||
}
|
||||
b = b[9:]
|
||||
case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
|
||||
b = b[1:]
|
||||
case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
var ok bool
|
||||
for i := 0; i < 32; i += 2 {
|
||||
uuid[i/2], ok = xtob(b[i], b[i+1])
|
||||
if !ok {
|
||||
return uuid, errors.New("invalid UUID format")
|
||||
}
|
||||
}
|
||||
return uuid, nil
|
||||
default:
|
||||
return uuid, fmt.Errorf("invalid UUID length: %d", len(b))
|
||||
}
|
||||
// s is now at least 36 bytes long
|
||||
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' {
|
||||
return uuid, errors.New("invalid UUID format")
|
||||
}
|
||||
for i, x := range [16]int{
|
||||
0, 2, 4, 6,
|
||||
9, 11,
|
||||
14, 16,
|
||||
19, 21,
|
||||
24, 26, 28, 30, 32, 34} {
|
||||
v, ok := xtob(b[x], b[x+1])
|
||||
if !ok {
|
||||
return uuid, errors.New("invalid UUID format")
|
||||
}
|
||||
uuid[i] = v
|
||||
}
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
// MustParse is like Parse but panics if the string cannot be parsed.
|
||||
// It simplifies safe initialization of global variables holding compiled UUIDs.
|
||||
func MustParse(s string) UUID {
|
||||
uuid, err := Parse(s)
|
||||
if err != nil {
|
||||
panic(`uuid: Parse(` + s + `): ` + err.Error())
|
||||
}
|
||||
return uuid
|
||||
}
|
||||
|
||||
// FromBytes creates a new UUID from a byte slice. Returns an error if the slice
|
||||
// does not have a length of 16. The bytes are copied from the slice.
|
||||
func FromBytes(b []byte) (uuid UUID, err error) {
|
||||
err = uuid.UnmarshalBinary(b)
|
||||
return uuid, err
|
||||
}
|
||||
|
||||
// Must returns uuid if err is nil and panics otherwise.
|
||||
func Must(uuid UUID, err error) UUID {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return uuid
|
||||
}
|
||||
|
||||
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
// , or "" if uuid is invalid.
|
||||
func (uuid UUID) String() string {
|
||||
var buf [36]byte
|
||||
encodeHex(buf[:], uuid)
|
||||
return string(buf[:])
|
||||
}
|
||||
|
||||
// URN returns the RFC 2141 URN form of uuid,
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
|
||||
func (uuid UUID) URN() string {
|
||||
var buf [36 + 9]byte
|
||||
copy(buf[:], "urn:uuid:")
|
||||
encodeHex(buf[9:], uuid)
|
||||
return string(buf[:])
|
||||
}
|
||||
|
||||
func encodeHex(dst []byte, uuid UUID) {
|
||||
hex.Encode(dst, uuid[:4])
|
||||
dst[8] = '-'
|
||||
hex.Encode(dst[9:13], uuid[4:6])
|
||||
dst[13] = '-'
|
||||
hex.Encode(dst[14:18], uuid[6:8])
|
||||
dst[18] = '-'
|
||||
hex.Encode(dst[19:23], uuid[8:10])
|
||||
dst[23] = '-'
|
||||
hex.Encode(dst[24:], uuid[10:])
|
||||
}
|
||||
|
||||
// Variant returns the variant encoded in uuid.
|
||||
func (uuid UUID) Variant() Variant {
|
||||
switch {
|
||||
case (uuid[8] & 0xc0) == 0x80:
|
||||
return RFC4122
|
||||
case (uuid[8] & 0xe0) == 0xc0:
|
||||
return Microsoft
|
||||
case (uuid[8] & 0xe0) == 0xe0:
|
||||
return Future
|
||||
default:
|
||||
return Reserved
|
||||
}
|
||||
}
|
||||
|
||||
// Version returns the version of uuid.
|
||||
func (uuid UUID) Version() Version {
|
||||
return Version(uuid[6] >> 4)
|
||||
}
|
||||
|
||||
func (v Version) String() string {
|
||||
if v > 15 {
|
||||
return fmt.Sprintf("BAD_VERSION_%d", v)
|
||||
}
|
||||
return fmt.Sprintf("VERSION_%d", v)
|
||||
}
|
||||
|
||||
func (v Variant) String() string {
|
||||
switch v {
|
||||
case RFC4122:
|
||||
return "RFC4122"
|
||||
case Reserved:
|
||||
return "Reserved"
|
||||
case Microsoft:
|
||||
return "Microsoft"
|
||||
case Future:
|
||||
return "Future"
|
||||
case Invalid:
|
||||
return "Invalid"
|
||||
}
|
||||
return fmt.Sprintf("BadVariant%d", int(v))
|
||||
}
|
||||
|
||||
// SetRand sets the random number generator to r, which implements io.Reader.
|
||||
// If r.Read returns an error when the package requests random data then
|
||||
// a panic will be issued.
|
||||
//
|
||||
// Calling SetRand with nil sets the random number generator to the default
|
||||
// generator.
|
||||
func SetRand(r io.Reader) {
|
||||
if r == nil {
|
||||
rander = rand.Reader
|
||||
return
|
||||
}
|
||||
rander = r
|
||||
}
|
||||
44
vendor/github.com/google/uuid/version1.go
generated
vendored
Normal file
44
vendor/github.com/google/uuid/version1.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// NewUUID returns a Version 1 UUID based on the current NodeID and clock
|
||||
// sequence, and the current time. If the NodeID has not been set by SetNodeID
|
||||
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
|
||||
// be set NewUUID returns nil. If clock sequence has not been set by
|
||||
// SetClockSequence then it will be set automatically. If GetTime fails to
|
||||
// return the current NewUUID returns nil and an error.
|
||||
//
|
||||
// In most cases, New should be used.
|
||||
func NewUUID() (UUID, error) {
|
||||
nodeMu.Lock()
|
||||
if nodeID == zeroID {
|
||||
setNodeInterface("")
|
||||
}
|
||||
nodeMu.Unlock()
|
||||
|
||||
var uuid UUID
|
||||
now, seq, err := GetTime()
|
||||
if err != nil {
|
||||
return uuid, err
|
||||
}
|
||||
|
||||
timeLow := uint32(now & 0xffffffff)
|
||||
timeMid := uint16((now >> 32) & 0xffff)
|
||||
timeHi := uint16((now >> 48) & 0x0fff)
|
||||
timeHi |= 0x1000 // Version 1
|
||||
|
||||
binary.BigEndian.PutUint32(uuid[0:], timeLow)
|
||||
binary.BigEndian.PutUint16(uuid[4:], timeMid)
|
||||
binary.BigEndian.PutUint16(uuid[6:], timeHi)
|
||||
binary.BigEndian.PutUint16(uuid[8:], seq)
|
||||
copy(uuid[10:], nodeID[:])
|
||||
|
||||
return uuid, nil
|
||||
}
|
||||
38
vendor/github.com/google/uuid/version4.go
generated
vendored
Normal file
38
vendor/github.com/google/uuid/version4.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import "io"
|
||||
|
||||
// New creates a new random UUID or panics. New is equivalent to
|
||||
// the expression
|
||||
//
|
||||
// uuid.Must(uuid.NewRandom())
|
||||
func New() UUID {
|
||||
return Must(NewRandom())
|
||||
}
|
||||
|
||||
// NewRandom returns a Random (Version 4) UUID.
|
||||
//
|
||||
// The strength of the UUIDs is based on the strength of the crypto/rand
|
||||
// package.
|
||||
//
|
||||
// A note about uniqueness derived from the UUID Wikipedia entry:
|
||||
//
|
||||
// Randomly generated UUIDs have 122 random bits. One's annual risk of being
|
||||
// hit by a meteorite is estimated to be one chance in 17 billion, that
|
||||
// means the probability is about 0.00000000006 (6 × 10−11),
|
||||
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
|
||||
// year and having one duplicate.
|
||||
func NewRandom() (UUID, error) {
|
||||
var uuid UUID
|
||||
_, err := io.ReadFull(rander, uuid[:])
|
||||
if err != nil {
|
||||
return Nil, err
|
||||
}
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
|
||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
|
||||
return uuid, nil
|
||||
}
|
||||
354
vendor/github.com/hashicorp/go-cty-funcs/LICENSE
generated
vendored
Normal file
354
vendor/github.com/hashicorp/go-cty-funcs/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,354 @@
|
||||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. “Contributor”
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. “Contributor Version”
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor’s Contribution.
|
||||
|
||||
1.3. “Contribution”
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. “Covered Software”
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. “Incompatible With Secondary Licenses”
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of version
|
||||
1.1 or earlier of the License, but not also under the terms of a
|
||||
Secondary License.
|
||||
|
||||
1.6. “Executable Form”
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. “Larger Work”
|
||||
|
||||
means a work that combines Covered Software with other material, in a separate
|
||||
file or files, that is not Covered Software.
|
||||
|
||||
1.8. “License”
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. “Licensable”
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether at the
|
||||
time of the initial grant or subsequently, any and all of the rights conveyed by
|
||||
this License.
|
||||
|
||||
1.10. “Modifications”
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to, deletion
|
||||
from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. “Patent Claims” of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method, process,
|
||||
and apparatus claims, in any patent Licensable by such Contributor that
|
||||
would be infringed, but for the grant of the License, by the making,
|
||||
using, selling, offering for sale, having made, import, or transfer of
|
||||
either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. “Secondary License”
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. “Source Code Form”
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. “You” (or “Your”)
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, “You” includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, “control” means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or as
|
||||
part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its Contributions
|
||||
or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution become
|
||||
effective for each Contribution on the date the Contributor first distributes
|
||||
such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under this
|
||||
License. No additional rights or licenses will be implied from the distribution
|
||||
or licensing of Covered Software under this License. Notwithstanding Section
|
||||
2.1(b) above, no patent license is granted by a Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party’s
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of its
|
||||
Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks, or
|
||||
logos of any Contributor (except as may be necessary to comply with the
|
||||
notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this License
|
||||
(see Section 10.2) or under the terms of a Secondary License (if permitted
|
||||
under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its Contributions
|
||||
are its original creation(s) or it has sufficient rights to grant the
|
||||
rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under applicable
|
||||
copyright doctrines of fair use, fair dealing, or other equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under the
|
||||
terms of this License. You must inform recipients that the Source Code Form
|
||||
of the Covered Software is governed by the terms of this License, and how
|
||||
they can obtain a copy of this License. You may not attempt to alter or
|
||||
restrict the recipients’ rights in the Source Code Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this License,
|
||||
or sublicense it under different terms, provided that the license for
|
||||
the Executable Form does not attempt to limit or alter the recipients’
|
||||
rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for the
|
||||
Covered Software. If the Larger Work is a combination of Covered Software
|
||||
with a work governed by one or more Secondary Licenses, and the Covered
|
||||
Software is not Incompatible With Secondary Licenses, this License permits
|
||||
You to additionally distribute such Covered Software under the terms of
|
||||
such Secondary License(s), so that the recipient of the Larger Work may, at
|
||||
their option, further distribute the Covered Software under the terms of
|
||||
either this License or such Secondary License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices (including
|
||||
copyright notices, patent notices, disclaimers of warranty, or limitations
|
||||
of liability) contained within the Source Code Form of the Covered
|
||||
Software, except that You may alter any license notices to the extent
|
||||
required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on behalf
|
||||
of any Contributor. You must make it absolutely clear that any such
|
||||
warranty, support, indemnity, or liability obligation is offered by You
|
||||
alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute, judicial
|
||||
order, or regulation then You must: (a) comply with the terms of this License
|
||||
to the maximum extent possible; and (b) describe the limitations and the code
|
||||
they affect. Such description must be placed in a text file included with all
|
||||
distributions of the Covered Software under this License. Except to the
|
||||
extent prohibited by statute or regulation, such description must be
|
||||
sufficiently detailed for a recipient of ordinary skill to be able to
|
||||
understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing basis,
|
||||
if such Contributor fails to notify You of the non-compliance by some
|
||||
reasonable means prior to 60 days after You have come back into compliance.
|
||||
Moreover, Your grants from a particular Contributor are reinstated on an
|
||||
ongoing basis if such Contributor notifies You of the non-compliance by
|
||||
some reasonable means, this is the first time You have received notice of
|
||||
non-compliance with this License from such Contributor, and You become
|
||||
compliant prior to 30 days after Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions, counter-claims,
|
||||
and cross-claims) alleging that a Contributor Version directly or
|
||||
indirectly infringes any patent, then the rights granted to You by any and
|
||||
all Contributors for the Covered Software under Section 2.1 of this License
|
||||
shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an “as is” basis, without
|
||||
warranty of any kind, either expressed, implied, or statutory, including,
|
||||
without limitation, warranties that the Covered Software is free of defects,
|
||||
merchantable, fit for a particular purpose or non-infringing. The entire
|
||||
risk as to the quality and performance of the Covered Software is with You.
|
||||
Should any Covered Software prove defective in any respect, You (not any
|
||||
Contributor) assume the cost of any necessary servicing, repair, or
|
||||
correction. This disclaimer of warranty constitutes an essential part of this
|
||||
License. No use of any Covered Software is authorized under this License
|
||||
except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from such
|
||||
party’s negligence to the extent applicable law prohibits such limitation.
|
||||
Some jurisdictions do not allow the exclusion or limitation of incidental or
|
||||
consequential damages, so this exclusion and limitation may not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts of
|
||||
a jurisdiction where the defendant maintains its principal place of business
|
||||
and such litigation shall be governed by laws of that jurisdiction, without
|
||||
reference to its conflict-of-law provisions. Nothing in this Section shall
|
||||
prevent a party’s ability to bring cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject matter
|
||||
hereof. If any provision of this License is held to be unenforceable, such
|
||||
provision shall be reformed only to the extent necessary to make it
|
||||
enforceable. Any law or regulation which provides that the language of a
|
||||
contract shall be construed against the drafter shall not be used to construe
|
||||
this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version of
|
||||
the License under which You originally received the Covered Software, or
|
||||
under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a modified
|
||||
version of this License if you rename the license and remove any
|
||||
references to the name of the license steward (except to note that such
|
||||
modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file, then
|
||||
You may include the notice in a location (such as a LICENSE file in a relevant
|
||||
directory) where a recipient would be likely to look for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - “Incompatible With Secondary Licenses” Notice
|
||||
|
||||
This Source Code Form is “Incompatible
|
||||
With Secondary Licenses”, as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
||||
|
||||
49
vendor/github.com/hashicorp/go-cty-funcs/cidr/host.go
generated
vendored
Normal file
49
vendor/github.com/hashicorp/go-cty-funcs/cidr/host.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package cidr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/apparentlymart/go-cidr/cidr"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
)
|
||||
|
||||
// HostFunc is a function that calculates a full host IP address within a given
|
||||
// IP network address prefix.
|
||||
var HostFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "prefix",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "hostnum",
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
var hostNum int
|
||||
if err := gocty.FromCtyValue(args[1], &hostNum); err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
_, network, err := net.ParseCIDR(args[0].AsString())
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err)
|
||||
}
|
||||
|
||||
ip, err := cidr.Host(network, hostNum)
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
|
||||
return cty.StringVal(ip.String()), nil
|
||||
},
|
||||
})
|
||||
|
||||
// Host calculates a full host IP address within a given IP network address prefix.
|
||||
func Host(prefix, hostnum cty.Value) (cty.Value, error) {
|
||||
return HostFunc.Call([]cty.Value{prefix, hostnum})
|
||||
}
|
||||
34
vendor/github.com/hashicorp/go-cty-funcs/cidr/netmask.go
generated
vendored
Normal file
34
vendor/github.com/hashicorp/go-cty-funcs/cidr/netmask.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
package cidr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
// NetmaskFunc is a function that converts an IPv4 address prefix given in CIDR
|
||||
// notation into a subnet mask address.
|
||||
var NetmaskFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "prefix",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
_, network, err := net.ParseCIDR(args[0].AsString())
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err)
|
||||
}
|
||||
|
||||
return cty.StringVal(net.IP(network.Mask).String()), nil
|
||||
},
|
||||
})
|
||||
|
||||
// Netmask converts an IPv4 address prefix given in CIDR notation into a subnet mask address.
|
||||
func Netmask(prefix cty.Value) (cty.Value, error) {
|
||||
return NetmaskFunc.Call([]cty.Value{prefix})
|
||||
}
|
||||
66
vendor/github.com/hashicorp/go-cty-funcs/cidr/subnet.go
generated
vendored
Normal file
66
vendor/github.com/hashicorp/go-cty-funcs/cidr/subnet.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
package cidr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/apparentlymart/go-cidr/cidr"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
)
|
||||
|
||||
// SubnetFunc is a function that calculates a subnet address within a given
|
||||
// IP network address prefix.
|
||||
var SubnetFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "prefix",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "newbits",
|
||||
Type: cty.Number,
|
||||
},
|
||||
{
|
||||
Name: "netnum",
|
||||
Type: cty.Number,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
var newbits int
|
||||
if err := gocty.FromCtyValue(args[1], &newbits); err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
var netnum int
|
||||
if err := gocty.FromCtyValue(args[2], &netnum); err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
|
||||
_, network, err := net.ParseCIDR(args[0].AsString())
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err)
|
||||
}
|
||||
|
||||
// For portability with 32-bit systems where the subnet number will be
|
||||
// a 32-bit int, we only allow extension of 32 bits in one call even if
|
||||
// we're running on a 64-bit machine. (Of course, this is significant
|
||||
// only for IPv6.)
|
||||
if newbits > 32 {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("may not extend prefix by more than 32 bits")
|
||||
}
|
||||
|
||||
newNetwork, err := cidr.Subnet(network, newbits, netnum)
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
|
||||
return cty.StringVal(newNetwork.String()), nil
|
||||
},
|
||||
})
|
||||
|
||||
// Subnet calculates a subnet address within a given IP network address prefix.
|
||||
func Subnet(prefix, newbits, netnum cty.Value) (cty.Value, error) {
|
||||
return SubnetFunc.Call([]cty.Value{prefix, newbits, netnum})
|
||||
}
|
||||
99
vendor/github.com/hashicorp/go-cty-funcs/cidr/subnets.go
generated
vendored
Normal file
99
vendor/github.com/hashicorp/go-cty-funcs/cidr/subnets.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
package cidr
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/apparentlymart/go-cidr/cidr"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
)
|
||||
|
||||
// SubnetsFunc is similar to SubnetFunc but calculates many consecutive subnet
|
||||
// addresses at once, rather than just a single subnet extension.
|
||||
var SubnetsFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "prefix",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "newbits",
|
||||
Type: cty.Number,
|
||||
},
|
||||
Type: function.StaticReturnType(cty.List(cty.String)),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
_, network, err := net.ParseCIDR(args[0].AsString())
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "invalid CIDR expression: %s", err)
|
||||
}
|
||||
startPrefixLen, _ := network.Mask.Size()
|
||||
|
||||
prefixLengthArgs := args[1:]
|
||||
if len(prefixLengthArgs) == 0 {
|
||||
return cty.ListValEmpty(cty.String), nil
|
||||
}
|
||||
|
||||
var firstLength int
|
||||
if err := gocty.FromCtyValue(prefixLengthArgs[0], &firstLength); err != nil {
|
||||
return cty.UnknownVal(cty.String), function.NewArgError(1, err)
|
||||
}
|
||||
firstLength += startPrefixLen
|
||||
|
||||
retVals := make([]cty.Value, len(prefixLengthArgs))
|
||||
|
||||
current, _ := cidr.PreviousSubnet(network, firstLength)
|
||||
for i, lengthArg := range prefixLengthArgs {
|
||||
var length int
|
||||
if err := gocty.FromCtyValue(lengthArg, &length); err != nil {
|
||||
return cty.UnknownVal(cty.String), function.NewArgError(i+1, err)
|
||||
}
|
||||
|
||||
if length < 1 {
|
||||
return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "must extend prefix by at least one bit")
|
||||
}
|
||||
// For portability with 32-bit systems where the subnet number
|
||||
// will be a 32-bit int, we only allow extension of 32 bits in
|
||||
// one call even if we're running on a 64-bit machine.
|
||||
// (Of course, this is significant only for IPv6.)
|
||||
if length > 32 {
|
||||
return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "may not extend prefix by more than 32 bits")
|
||||
}
|
||||
length += startPrefixLen
|
||||
if length > (len(network.IP) * 8) {
|
||||
protocol := "IP"
|
||||
switch len(network.IP) * 8 {
|
||||
case 32:
|
||||
protocol = "IPv4"
|
||||
case 128:
|
||||
protocol = "IPv6"
|
||||
}
|
||||
return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "would extend prefix to %d bits, which is too long for an %s address", length, protocol)
|
||||
}
|
||||
|
||||
next, rollover := cidr.NextSubnet(current, length)
|
||||
if rollover || !network.Contains(next.IP) {
|
||||
// If we run out of suffix bits in the base CIDR prefix then
|
||||
// NextSubnet will start incrementing the prefix bits, which
|
||||
// we don't allow because it would then allocate addresses
|
||||
// outside of the caller's given prefix.
|
||||
return cty.UnknownVal(cty.String), function.NewArgErrorf(i+1, "not enough remaining address space for a subnet with a prefix of %d bits after %s", length, current.String())
|
||||
}
|
||||
|
||||
current = next
|
||||
retVals[i] = cty.StringVal(current.String())
|
||||
}
|
||||
|
||||
return cty.ListVal(retVals), nil
|
||||
},
|
||||
})
|
||||
|
||||
// Subnets calculates a sequence of consecutive subnet prefixes that may be of
|
||||
// different prefix lengths under a common base prefix.
|
||||
func Subnets(prefix cty.Value, newbits ...cty.Value) (cty.Value, error) {
|
||||
args := make([]cty.Value, len(newbits)+1)
|
||||
args[0] = prefix
|
||||
copy(args[1:], newbits)
|
||||
return SubnetsFunc.Call(args)
|
||||
}
|
||||
59
vendor/github.com/hashicorp/go-cty-funcs/crypto/bcrypt.go
generated
vendored
Normal file
59
vendor/github.com/hashicorp/go-cty-funcs/crypto/bcrypt.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// BcryptFunc is a function that computes a hash of the given string using the
|
||||
// Blowfish cipher.
|
||||
var BcryptFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "cost",
|
||||
Type: cty.Number,
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
defaultCost := 10
|
||||
|
||||
if len(args) > 1 {
|
||||
var val int
|
||||
if err := gocty.FromCtyValue(args[1], &val); err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
defaultCost = val
|
||||
}
|
||||
|
||||
if len(args) > 2 {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("bcrypt() takes no more than two arguments")
|
||||
}
|
||||
|
||||
input := args[0].AsString()
|
||||
out, err := bcrypt.GenerateFromPassword([]byte(input), defaultCost)
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("error occured generating password %s", err.Error())
|
||||
}
|
||||
|
||||
return cty.StringVal(string(out)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// Bcrypt computes a hash of the given string using the Blowfish cipher,
|
||||
// returning a string in the Modular Crypt Format usually expected in the
|
||||
// shadow password file on many Unix systems.
|
||||
func Bcrypt(str cty.Value, cost ...cty.Value) (cty.Value, error) {
|
||||
args := make([]cty.Value, len(cost)+1)
|
||||
args[0] = str
|
||||
copy(args[1:], cost)
|
||||
return BcryptFunc.Call(args)
|
||||
}
|
||||
27
vendor/github.com/hashicorp/go-cty-funcs/crypto/hash.go
generated
vendored
Normal file
27
vendor/github.com/hashicorp/go-cty-funcs/crypto/hash.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"hash"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
func makeStringHashFunction(hf func() hash.Hash, enc func([]byte) string) function.Function {
|
||||
return function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
s := args[0].AsString()
|
||||
h := hf()
|
||||
h.Write([]byte(s))
|
||||
rv := enc(h.Sum(nil))
|
||||
return cty.StringVal(rv), nil
|
||||
},
|
||||
})
|
||||
}
|
||||
18
vendor/github.com/hashicorp/go-cty-funcs/crypto/md5.go
generated
vendored
Normal file
18
vendor/github.com/hashicorp/go-cty-funcs/crypto/md5.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// Md5Func is a function that computes the MD5 hash of a given string and
|
||||
// encodes it with hexadecimal digits.
|
||||
var Md5Func = makeStringHashFunction(md5.New, hex.EncodeToString)
|
||||
|
||||
// Md5 computes the MD5 hash of a given string and encodes it with hexadecimal
|
||||
// digits.
|
||||
func Md5(str cty.Value) (cty.Value, error) {
|
||||
return Md5Func.Call([]cty.Value{str})
|
||||
}
|
||||
64
vendor/github.com/hashicorp/go-cty-funcs/crypto/rsa.go
generated
vendored
Normal file
64
vendor/github.com/hashicorp/go-cty-funcs/crypto/rsa.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
// RsaDecryptFunc is a function that decrypts an RSA-encrypted ciphertext.
|
||||
var RsaDecryptFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "ciphertext",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "privatekey",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
s := args[0].AsString()
|
||||
key := args[1].AsString()
|
||||
|
||||
b, err := base64.StdEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("failed to decode input %q: cipher text must be base64-encoded", s)
|
||||
}
|
||||
|
||||
block, _ := pem.Decode([]byte(key))
|
||||
if block == nil {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("failed to parse key: no key found")
|
||||
}
|
||||
if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf(
|
||||
"failed to parse key: password protected keys are not supported. Please decrypt the key prior to use",
|
||||
)
|
||||
}
|
||||
|
||||
x509Key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
|
||||
out, err := rsa.DecryptPKCS1v15(nil, x509Key, b)
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
|
||||
return cty.StringVal(string(out)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// RsaDecrypt decrypts an RSA-encrypted ciphertext, returning the corresponding
|
||||
// cleartext.
|
||||
func RsaDecrypt(ciphertext, privatekey cty.Value) (cty.Value, error) {
|
||||
return RsaDecryptFunc.Call([]cty.Value{ciphertext, privatekey})
|
||||
}
|
||||
40
vendor/github.com/hashicorp/go-cty-funcs/crypto/sha.go
generated
vendored
Normal file
40
vendor/github.com/hashicorp/go-cty-funcs/crypto/sha.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// Sha1Func is a function that computes the SHA1 hash of a given string and
|
||||
// encodes it with hexadecimal digits.
|
||||
var Sha1Func = makeStringHashFunction(sha1.New, hex.EncodeToString)
|
||||
|
||||
// Sha256Func is a function that computes the SHA256 hash of a given string and
|
||||
// encodes it with hexadecimal digits.
|
||||
var Sha256Func = makeStringHashFunction(sha256.New, hex.EncodeToString)
|
||||
|
||||
// Sha512Func is a function that computes the SHA512 hash of a given string and
|
||||
// encodes it with hexadecimal digits.
|
||||
var Sha512Func = makeStringHashFunction(sha512.New, hex.EncodeToString)
|
||||
|
||||
// Sha1 computes the SHA1 hash of a given string and encodes it with
|
||||
// hexadecimal digits.
|
||||
func Sha1(str cty.Value) (cty.Value, error) {
|
||||
return Sha1Func.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// Sha256 computes the SHA256 hash of a given string and encodes it with
|
||||
// hexadecimal digits.
|
||||
func Sha256(str cty.Value) (cty.Value, error) {
|
||||
return Sha256Func.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// Sha512 computes the SHA512 hash of a given string and encodes it with
|
||||
// hexadecimal digits.
|
||||
func Sha512(str cty.Value) (cty.Value, error) {
|
||||
return Sha512Func.Call([]cty.Value{str})
|
||||
}
|
||||
71
vendor/github.com/hashicorp/go-cty-funcs/encoding/base64.go
generated
vendored
Normal file
71
vendor/github.com/hashicorp/go-cty-funcs/encoding/base64.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
// Base64DecodeFunc is a function that decodes a string containing a base64 sequence.
|
||||
var Base64DecodeFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
s := args[0].AsString()
|
||||
sDec, err := base64.StdEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("failed to decode base64 data '%s'", s)
|
||||
}
|
||||
if !utf8.Valid([]byte(sDec)) {
|
||||
log.Printf("[DEBUG] the result of decoding the the provided string is not valid UTF-8: %s", sDec)
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("the result of decoding the the provided string is not valid UTF-8")
|
||||
}
|
||||
return cty.StringVal(string(sDec)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// Base64EncodeFunc is a function that encodes a string to a base64 sequence.
|
||||
var Base64EncodeFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return cty.StringVal(base64.StdEncoding.EncodeToString([]byte(args[0].AsString()))), nil
|
||||
},
|
||||
})
|
||||
|
||||
// Base64Decode decodes a string containing a base64 sequence.
|
||||
//
|
||||
// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
|
||||
//
|
||||
// Strings in the Terraform language are sequences of unicode characters rather
|
||||
// than bytes, so this function will also interpret the resulting bytes as
|
||||
// UTF-8. If the bytes after Base64 decoding are _not_ valid UTF-8, this function
|
||||
// produces an error.
|
||||
func Base64Decode(str cty.Value) (cty.Value, error) {
|
||||
return Base64DecodeFunc.Call([]cty.Value{str})
|
||||
}
|
||||
|
||||
// Base64Encode applies Base64 encoding to a string.
|
||||
//
|
||||
// Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
|
||||
//
|
||||
// Strings in the Terraform language are sequences of unicode characters rather
|
||||
// than bytes, so this function will first encode the characters from the string
|
||||
// as UTF-8, and then apply Base64 encoding to the result.
|
||||
func Base64Encode(str cty.Value) (cty.Value, error) {
|
||||
return Base64EncodeFunc.Call([]cty.Value{str})
|
||||
}
|
||||
34
vendor/github.com/hashicorp/go-cty-funcs/encoding/url.go
generated
vendored
Normal file
34
vendor/github.com/hashicorp/go-cty-funcs/encoding/url.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
// URLEncodeFunc is a function that applies URL encoding to a given string.
|
||||
var URLEncodeFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "str",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return cty.StringVal(url.QueryEscape(args[0].AsString())), nil
|
||||
},
|
||||
})
|
||||
|
||||
// URLEncode applies URL encoding to a given string.
|
||||
//
|
||||
// This function identifies characters in the given string that would have a
|
||||
// special meaning when included as a query string argument in a URL and
|
||||
// escapes them using RFC 3986 "percent encoding".
|
||||
//
|
||||
// If the given string contains non-ASCII characters, these are first encoded as
|
||||
// UTF-8 and then percent encoding is applied separately to each UTF-8 byte.
|
||||
func URLEncode(str cty.Value) (cty.Value, error) {
|
||||
return URLEncodeFunc.Call([]cty.Value{str})
|
||||
}
|
||||
312
vendor/github.com/hashicorp/go-cty-funcs/filesystem/filesystem.go
generated
vendored
Normal file
312
vendor/github.com/hashicorp/go-cty-funcs/filesystem/filesystem.go
generated
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/bmatcuk/doublestar"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
// MakeFileFunc constructs a function that takes a file path and returns the
|
||||
// contents of that file, either directly as a string (where valid UTF-8 is
|
||||
// required) or as a string containing base64 bytes.
|
||||
func MakeFileFunc(baseDir string, encBase64 bool) function.Function {
|
||||
return function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "path",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
path := args[0].AsString()
|
||||
src, err := readFileBytes(baseDir, path)
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
|
||||
switch {
|
||||
case encBase64:
|
||||
enc := base64.StdEncoding.EncodeToString(src)
|
||||
return cty.StringVal(enc), nil
|
||||
default:
|
||||
if !utf8.Valid(src) {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("contents of %s are not valid UTF-8; use the filebase64 function to obtain the Base64 encoded contents or the other file functions (e.g. filemd5, filesha256) to obtain file hashing results instead", path)
|
||||
}
|
||||
return cty.StringVal(string(src)), nil
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// MakeFileExistsFunc is a function that takes a path and determines whether a
|
||||
// file exists at that path.
|
||||
//
|
||||
// MakeFileExistsFunc will try to expand a path starting with a '~' to the home
|
||||
// folder using github.com/mitchellh/go-homedir
|
||||
func MakeFileExistsFunc(baseDir string) function.Function {
|
||||
return function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "path",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
path := args[0].AsString()
|
||||
path, err := homedir.Expand(path)
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.Bool), fmt.Errorf("failed to expand ~: %s", err)
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(path) {
|
||||
path = filepath.Join(baseDir, path)
|
||||
}
|
||||
|
||||
// Ensure that the path is canonical for the host OS
|
||||
path = filepath.Clean(path)
|
||||
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return cty.False, nil
|
||||
}
|
||||
return cty.UnknownVal(cty.Bool), fmt.Errorf("failed to stat %s", path)
|
||||
}
|
||||
|
||||
if fi.Mode().IsRegular() {
|
||||
return cty.True, nil
|
||||
}
|
||||
|
||||
return cty.False, fmt.Errorf("%s is not a regular file, but %q",
|
||||
path, fi.Mode().String())
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// MakeFileSetFunc is a function that takes a glob pattern
|
||||
// and enumerates a file set from that pattern
|
||||
func MakeFileSetFunc(baseDir string) function.Function {
|
||||
return function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "path",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "pattern",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Set(cty.String)),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
path := args[0].AsString()
|
||||
pattern := args[1].AsString()
|
||||
|
||||
if !filepath.IsAbs(path) {
|
||||
path = filepath.Join(baseDir, path)
|
||||
}
|
||||
|
||||
// Join the path to the glob pattern, while ensuring the full
|
||||
// pattern is canonical for the host OS. The joined path is
|
||||
// automatically cleaned during this operation.
|
||||
pattern = filepath.Join(path, pattern)
|
||||
|
||||
matches, err := doublestar.Glob(pattern)
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.Set(cty.String)), fmt.Errorf("failed to glob pattern (%s): %s", pattern, err)
|
||||
}
|
||||
|
||||
var matchVals []cty.Value
|
||||
for _, match := range matches {
|
||||
fi, err := os.Stat(match)
|
||||
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.Set(cty.String)), fmt.Errorf("failed to stat (%s): %s", match, err)
|
||||
}
|
||||
|
||||
if !fi.Mode().IsRegular() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove the path and file separator from matches.
|
||||
match, err = filepath.Rel(path, match)
|
||||
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.Set(cty.String)), fmt.Errorf("failed to trim path of match (%s): %s", match, err)
|
||||
}
|
||||
|
||||
// Replace any remaining file separators with forward slash (/)
|
||||
// separators for cross-system compatibility.
|
||||
match = filepath.ToSlash(match)
|
||||
|
||||
matchVals = append(matchVals, cty.StringVal(match))
|
||||
}
|
||||
|
||||
if len(matchVals) == 0 {
|
||||
return cty.SetValEmpty(cty.String), nil
|
||||
}
|
||||
|
||||
return cty.SetVal(matchVals), nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// BasenameFunc is a function that takes a string containing a filesystem path
|
||||
// and removes all except the last portion from it.
|
||||
var BasenameFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "path",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return cty.StringVal(filepath.Base(args[0].AsString())), nil
|
||||
},
|
||||
})
|
||||
|
||||
// DirnameFunc is a function that takes a string containing a filesystem path
|
||||
// and removes the last portion from it.
|
||||
var DirnameFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "path",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return cty.StringVal(filepath.Dir(args[0].AsString())), nil
|
||||
},
|
||||
})
|
||||
|
||||
// AbsPathFunc is a function that converts a filesystem path to an absolute path
|
||||
var AbsPathFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "path",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
absPath, err := filepath.Abs(args[0].AsString())
|
||||
return cty.StringVal(filepath.ToSlash(absPath)), err
|
||||
},
|
||||
})
|
||||
|
||||
// PathExpandFunc is a function that expands a leading ~ character to the current user's home directory.
|
||||
var PathExpandFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "path",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
|
||||
homePath, err := homedir.Expand(args[0].AsString())
|
||||
return cty.StringVal(homePath), err
|
||||
},
|
||||
})
|
||||
|
||||
func readFileBytes(baseDir, path string) ([]byte, error) {
|
||||
path, err := homedir.Expand(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to expand ~: %s", err)
|
||||
}
|
||||
|
||||
if !filepath.IsAbs(path) {
|
||||
path = filepath.Join(baseDir, path)
|
||||
}
|
||||
|
||||
// Ensure that the path is canonical for the host OS
|
||||
path = filepath.Clean(path)
|
||||
|
||||
src, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
// ReadFile does not return Terraform-user-friendly error
|
||||
// messages, so we'll provide our own.
|
||||
if os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("no file exists at %s", path)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to read %s", path)
|
||||
}
|
||||
|
||||
return src, nil
|
||||
}
|
||||
|
||||
// File reads the contents of the file at the given path.
|
||||
//
|
||||
// The file must contain valid UTF-8 bytes, or this function will return an error.
|
||||
//
|
||||
// The underlying function implementation works relative to a particular base
|
||||
// directory, so this wrapper takes a base directory string and uses it to
|
||||
// construct the underlying function before calling it.
|
||||
func File(baseDir string, path cty.Value) (cty.Value, error) {
|
||||
fn := MakeFileFunc(baseDir, false)
|
||||
return fn.Call([]cty.Value{path})
|
||||
}
|
||||
|
||||
// FileExists determines whether a file exists at the given path.
|
||||
//
|
||||
// The underlying function implementation works relative to a particular base
|
||||
// directory, so this wrapper takes a base directory string and uses it to
|
||||
// construct the underlying function before calling it.
|
||||
func FileExists(baseDir string, path cty.Value) (cty.Value, error) {
|
||||
fn := MakeFileExistsFunc(baseDir)
|
||||
return fn.Call([]cty.Value{path})
|
||||
}
|
||||
|
||||
// FileSet enumerates a set of files given a glob pattern
|
||||
//
|
||||
// The underlying function implementation works relative to a particular base
|
||||
// directory, so this wrapper takes a base directory string and uses it to
|
||||
// construct the underlying function before calling it.
|
||||
func FileSet(baseDir string, path, pattern cty.Value) (cty.Value, error) {
|
||||
fn := MakeFileSetFunc(baseDir)
|
||||
return fn.Call([]cty.Value{path, pattern})
|
||||
}
|
||||
|
||||
// Basename takes a string containing a filesystem path and removes all except the last portion from it.
|
||||
//
|
||||
// The underlying function implementation works only with the path string and does not access the filesystem itself.
|
||||
// It is therefore unable to take into account filesystem features such as symlinks.
|
||||
//
|
||||
// If the path is empty then the result is ".", representing the current working directory.
|
||||
func Basename(path cty.Value) (cty.Value, error) {
|
||||
return BasenameFunc.Call([]cty.Value{path})
|
||||
}
|
||||
|
||||
// Dirname takes a string containing a filesystem path and removes the last portion from it.
|
||||
//
|
||||
// The underlying function implementation works only with the path string and does not access the filesystem itself.
|
||||
// It is therefore unable to take into account filesystem features such as symlinks.
|
||||
//
|
||||
// If the path is empty then the result is ".", representing the current working directory.
|
||||
func Dirname(path cty.Value) (cty.Value, error) {
|
||||
return DirnameFunc.Call([]cty.Value{path})
|
||||
}
|
||||
|
||||
// Pathexpand takes a string that might begin with a `~` segment, and if so it replaces that segment with
|
||||
// the current user's home directory path.
|
||||
//
|
||||
// The underlying function implementation works only with the path string and does not access the filesystem itself.
|
||||
// It is therefore unable to take into account filesystem features such as symlinks.
|
||||
//
|
||||
// If the leading segment in the path is not `~` then the given path is returned unmodified.
|
||||
func Pathexpand(path cty.Value) (cty.Value, error) {
|
||||
return PathExpandFunc.Call([]cty.Value{path})
|
||||
}
|
||||
28
vendor/github.com/hashicorp/go-cty-funcs/uuid/uuid_v4.go
generated
vendored
Normal file
28
vendor/github.com/hashicorp/go-cty-funcs/uuid/uuid_v4.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var V4Func = function.New(&function.Spec{
|
||||
Params: []function.Parameter{},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
uuid, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
}
|
||||
return cty.StringVal(uuid.String()), nil
|
||||
},
|
||||
})
|
||||
|
||||
// V4 generates and returns a Type-4 UUID in the standard hexadecimal string
|
||||
// format.
|
||||
//
|
||||
// This is not a "pure" function: it will generate a different result for each
|
||||
// call.
|
||||
func V4() (cty.Value, error) {
|
||||
return V4Func.Call(nil)
|
||||
}
|
||||
51
vendor/github.com/hashicorp/go-cty-funcs/uuid/uuid_v5.go
generated
vendored
Normal file
51
vendor/github.com/hashicorp/go-cty-funcs/uuid/uuid_v5.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
uuidv5 "github.com/google/uuid"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
var V5Func = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "namespace",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "name",
|
||||
Type: cty.String,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||
var namespace uuidv5.UUID
|
||||
switch {
|
||||
case args[0].AsString() == "dns":
|
||||
namespace = uuidv5.NameSpaceDNS
|
||||
case args[0].AsString() == "url":
|
||||
namespace = uuidv5.NameSpaceURL
|
||||
case args[0].AsString() == "oid":
|
||||
namespace = uuidv5.NameSpaceOID
|
||||
case args[0].AsString() == "x500":
|
||||
namespace = uuidv5.NameSpaceX500
|
||||
default:
|
||||
if namespace, err = uuidv5.Parse(args[0].AsString()); err != nil {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("uuidv5() doesn't support namespace %s (%v)", args[0].AsString(), err)
|
||||
}
|
||||
}
|
||||
val := args[1].AsString()
|
||||
return cty.StringVal(uuidv5.NewSHA1(namespace, []byte(val)).String()), nil
|
||||
},
|
||||
})
|
||||
|
||||
// V5 generates and returns a Type-5 UUID in the standard hexadecimal
|
||||
// string format.
|
||||
//
|
||||
// This is not a "pure" function: it will generate a different result for each
|
||||
// call.
|
||||
func V5(namespace cty.Value, name cty.Value) (cty.Value, error) {
|
||||
return V5Func.Call([]cty.Value{namespace, name})
|
||||
}
|
||||
18
vendor/github.com/hashicorp/hcl/.gitignore
generated
vendored
18
vendor/github.com/hashicorp/hcl/.gitignore
generated
vendored
@@ -1,9 +1,9 @@
|
||||
y.output
|
||||
|
||||
# ignore intellij files
|
||||
.idea
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
*.test
|
||||
y.output
|
||||
|
||||
# ignore intellij files
|
||||
.idea
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
*.test
|
||||
|
||||
13
vendor/github.com/hashicorp/hcl/.travis.yml
generated
vendored
13
vendor/github.com/hashicorp/hcl/.travis.yml
generated
vendored
@@ -1,13 +0,0 @@
|
||||
sudo: false
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.x
|
||||
- tip
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
script: make test
|
||||
36
vendor/github.com/hashicorp/hcl/Makefile
generated
vendored
36
vendor/github.com/hashicorp/hcl/Makefile
generated
vendored
@@ -1,18 +1,18 @@
|
||||
TEST?=./...
|
||||
|
||||
default: test
|
||||
|
||||
fmt: generate
|
||||
go fmt ./...
|
||||
|
||||
test: generate
|
||||
go get -t ./...
|
||||
go test $(TEST) $(TESTARGS)
|
||||
|
||||
generate:
|
||||
go generate ./...
|
||||
|
||||
updatedeps:
|
||||
go get -u golang.org/x/tools/cmd/stringer
|
||||
|
||||
.PHONY: default generate test updatedeps
|
||||
TEST?=./...
|
||||
|
||||
default: test
|
||||
|
||||
fmt: generate
|
||||
go fmt ./...
|
||||
|
||||
test: generate
|
||||
go get -t ./...
|
||||
go test $(TEST) $(TESTARGS)
|
||||
|
||||
generate:
|
||||
go generate ./...
|
||||
|
||||
updatedeps:
|
||||
go get -u golang.org/x/tools/cmd/stringer
|
||||
|
||||
.PHONY: default generate test updatedeps
|
||||
|
||||
19
vendor/github.com/hashicorp/hcl/appveyor.yml
generated
vendored
19
vendor/github.com/hashicorp/hcl/appveyor.yml
generated
vendored
@@ -1,19 +0,0 @@
|
||||
version: "build-{branch}-{build}"
|
||||
image: Visual Studio 2015
|
||||
clone_folder: c:\gopath\src\github.com\hashicorp\hcl
|
||||
environment:
|
||||
GOPATH: c:\gopath
|
||||
init:
|
||||
- git config --global core.autocrlf false
|
||||
install:
|
||||
- cmd: >-
|
||||
echo %Path%
|
||||
|
||||
go version
|
||||
|
||||
go env
|
||||
|
||||
go get -t ./...
|
||||
|
||||
build_script:
|
||||
- cmd: go test -v ./...
|
||||
16
vendor/github.com/hashicorp/hcl/decoder.go
generated
vendored
16
vendor/github.com/hashicorp/hcl/decoder.go
generated
vendored
@@ -13,9 +13,6 @@ import (
|
||||
"github.com/hashicorp/hcl/hcl/token"
|
||||
)
|
||||
|
||||
// This is the tag to use with structures to have settings for HCL
|
||||
const tagName = "hcl"
|
||||
|
||||
var (
|
||||
// nodeType holds a reference to the type of ast.Node
|
||||
nodeType reflect.Type = findNodeType()
|
||||
@@ -597,7 +594,7 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value)
|
||||
structType := structVal.Type()
|
||||
for i := 0; i < structType.NumField(); i++ {
|
||||
fieldType := structType.Field(i)
|
||||
tagParts := strings.Split(fieldType.Tag.Get(tagName), ",")
|
||||
tagParts := strings.Split(fieldTag(fieldType), ",")
|
||||
|
||||
// Ignore fields with tag name "-"
|
||||
if tagParts[0] == "-" {
|
||||
@@ -664,7 +661,7 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value)
|
||||
|
||||
fieldName := field.Name
|
||||
|
||||
tagValue := field.Tag.Get(tagName)
|
||||
tagValue := fieldTag(field)
|
||||
tagParts := strings.SplitN(tagValue, ",", 2)
|
||||
if len(tagParts) >= 2 {
|
||||
switch tagParts[1] {
|
||||
@@ -765,3 +762,12 @@ func removeCaseFold(xs []string, y string) []string {
|
||||
}
|
||||
return xs
|
||||
}
|
||||
|
||||
// read the tag for HCL settings: check `hcl1` first and fallback to `hcl`
|
||||
func fieldTag(fieldType reflect.StructField) string {
|
||||
tag := fieldType.Tag.Get("hcl1")
|
||||
if tag == "" {
|
||||
tag = fieldType.Tag.Get("hcl")
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
2
vendor/github.com/hashicorp/hcl/go.mod
generated
vendored
2
vendor/github.com/hashicorp/hcl/go.mod
generated
vendored
@@ -1,3 +1,5 @@
|
||||
module github.com/hashicorp/hcl
|
||||
|
||||
require github.com/davecgh/go-spew v1.1.1
|
||||
|
||||
go 1.14
|
||||
|
||||
15
vendor/github.com/hashicorp/hcl/hcl/ast/ast.go
generated
vendored
15
vendor/github.com/hashicorp/hcl/hcl/ast/ast.go
generated
vendored
@@ -25,6 +25,8 @@ func (ObjectType) node() {}
|
||||
func (LiteralType) node() {}
|
||||
func (ListType) node() {}
|
||||
|
||||
var unknownPos token.Pos
|
||||
|
||||
// File represents a single HCL file
|
||||
type File struct {
|
||||
Node Node // usually a *ObjectList
|
||||
@@ -108,7 +110,12 @@ func (o *ObjectList) Elem() *ObjectList {
|
||||
}
|
||||
|
||||
func (o *ObjectList) Pos() token.Pos {
|
||||
// always returns the uninitiliazed position
|
||||
// If an Object has no members, it won't have a first item
|
||||
// to use as position
|
||||
if len(o.Items) == 0 {
|
||||
return unknownPos
|
||||
}
|
||||
// Return the uninitialized position
|
||||
return o.Items[0].Pos()
|
||||
}
|
||||
|
||||
@@ -133,10 +140,10 @@ type ObjectItem struct {
|
||||
}
|
||||
|
||||
func (o *ObjectItem) Pos() token.Pos {
|
||||
// I'm not entirely sure what causes this, but removing this causes
|
||||
// a test failure. We should investigate at some point.
|
||||
// If a parsed object has no keys, there is no position
|
||||
// for its first element.
|
||||
if len(o.Keys) == 0 {
|
||||
return token.Pos{}
|
||||
return unknownPos
|
||||
}
|
||||
|
||||
return o.Keys[0].Pos()
|
||||
|
||||
24
vendor/github.com/hashicorp/hcl/v2/CHANGELOG.md
generated
vendored
24
vendor/github.com/hashicorp/hcl/v2/CHANGELOG.md
generated
vendored
@@ -1,5 +1,29 @@
|
||||
# HCL Changelog
|
||||
|
||||
## v2.7.0 (October 14, 2020)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* json: There is a new function `ParseWithStartPos`, which allows overriding the starting position for parsing in case the given JSON bytes are a fragment of a larger document, such as might happen when decoding with `encoding/json` into a `json.RawMessage`. ([#389](https://github.com/hashicorp/hcl/pull/389))
|
||||
* json: There is a new function `ParseExpression`, which allows parsing a JSON string directly in expression mode, whereas previously it was only possible to parse a JSON string in body mode. ([#381](https://github.com/hashicorp/hcl/pull/381))
|
||||
* hclwrite: `Block` type now supports `SetType` and `SetLabels`, allowing surgical changes to the type and labels of an existing block without having to reconstruct the entire block. ([#340](https://github.com/hashicorp/hcl/pull/340))
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* hclsyntax: Fix confusing error message for bitwise OR operator ([#380](https://github.com/hashicorp/hcl/pull/380))
|
||||
* hclsyntax: Several bug fixes for using HCL with values containing cty "marks" ([#404](https://github.com/hashicorp/hcl/pull/404), [#406](https://github.com/hashicorp/hcl/pull/404), [#407](https://github.com/hashicorp/hcl/pull/404))
|
||||
|
||||
## v2.6.0 (June 4, 2020)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* hcldec: Add a new `Spec`, `ValidateSpec`, which allows custom validation of values at decode-time. ([#387](https://github.com/hashicorp/hcl/pull/387))
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
* hclsyntax: Fix panic with combination of sequences and null arguments ([#386](https://github.com/hashicorp/hcl/pull/386))
|
||||
* hclsyntax: Fix handling of unknown values and sequences ([#386](https://github.com/hashicorp/hcl/pull/386))
|
||||
|
||||
## v2.5.1 (May 14, 2020)
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
16
vendor/github.com/hashicorp/hcl/v2/README.md
generated
vendored
16
vendor/github.com/hashicorp/hcl/v2/README.md
generated
vendored
@@ -33,11 +33,25 @@ package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hclsimple"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
LogLevel string `hcl:"log_level"`
|
||||
IOMode string `hcl:"io_mode"`
|
||||
Service ServiceConfig `hcl:"service,block"`
|
||||
}
|
||||
|
||||
type ServiceConfig struct {
|
||||
Protocol string `hcl:"protocol,label"`
|
||||
Type string `hcl:"type,label"`
|
||||
ListenAddr string `hcl:"listen_addr"`
|
||||
Processes []ProcessConfig `hcl:"process,block"`
|
||||
}
|
||||
|
||||
type ProcessConfig struct {
|
||||
Type string `hcl:"type,label"`
|
||||
Command []string `hcl:"command"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
4
vendor/github.com/hashicorp/hcl/v2/diagnostic.go
generated
vendored
4
vendor/github.com/hashicorp/hcl/v2/diagnostic.go
generated
vendored
@@ -22,14 +22,14 @@ const (
|
||||
)
|
||||
|
||||
// Diagnostic represents information to be presented to a user about an
|
||||
// error or anomoly in parsing or evaluating configuration.
|
||||
// error or anomaly in parsing or evaluating configuration.
|
||||
type Diagnostic struct {
|
||||
Severity DiagnosticSeverity
|
||||
|
||||
// Summary and Detail contain the English-language description of the
|
||||
// problem. Summary is a terse description of the general problem and
|
||||
// detail is a more elaborate, often-multi-sentence description of
|
||||
// the probem and what might be done to solve it.
|
||||
// the problem and what might be done to solve it.
|
||||
Summary string
|
||||
Detail string
|
||||
|
||||
|
||||
2
vendor/github.com/hashicorp/hcl/v2/eval_context.go
generated
vendored
2
vendor/github.com/hashicorp/hcl/v2/eval_context.go
generated
vendored
@@ -11,6 +11,8 @@ type EvalContext struct {
|
||||
Variables map[string]cty.Value
|
||||
Functions map[string]function.Function
|
||||
parent *EvalContext
|
||||
|
||||
UnknownVariable func(expr string) (cty.Value, error)
|
||||
}
|
||||
|
||||
// NewChild returns a new EvalContext that is a child of the receiver.
|
||||
|
||||
184
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/README.md
generated
vendored
Normal file
184
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/README.md
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
# HCL Dynamic Blocks Extension
|
||||
|
||||
This HCL extension implements a special block type named "dynamic" that can
|
||||
be used to dynamically generate blocks of other types by iterating over
|
||||
collection values.
|
||||
|
||||
Normally the block structure in an HCL configuration file is rigid, even
|
||||
though dynamic expressions can be used within attribute values. This is
|
||||
convenient for most applications since it allows the overall structure of
|
||||
the document to be decoded easily, but in some applications it is desirable
|
||||
to allow dynamic block generation within certain portions of the configuration.
|
||||
|
||||
Dynamic block generation is performed using the `dynamic` block type:
|
||||
|
||||
```hcl
|
||||
toplevel {
|
||||
nested {
|
||||
foo = "static block 1"
|
||||
}
|
||||
|
||||
dynamic "nested" {
|
||||
for_each = ["a", "b", "c"]
|
||||
iterator = nested
|
||||
content {
|
||||
foo = "dynamic block ${nested.value}"
|
||||
}
|
||||
}
|
||||
|
||||
nested {
|
||||
foo = "static block 2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The above is interpreted as if it were written as follows:
|
||||
|
||||
```hcl
|
||||
toplevel {
|
||||
nested {
|
||||
foo = "static block 1"
|
||||
}
|
||||
|
||||
nested {
|
||||
foo = "dynamic block a"
|
||||
}
|
||||
|
||||
nested {
|
||||
foo = "dynamic block b"
|
||||
}
|
||||
|
||||
nested {
|
||||
foo = "dynamic block c"
|
||||
}
|
||||
|
||||
nested {
|
||||
foo = "static block 2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Since HCL block syntax is not normally exposed to the possibility of unknown
|
||||
values, this extension must make some compromises when asked to iterate over
|
||||
an unknown collection. If the length of the collection cannot be statically
|
||||
recognized (because it is an unknown value of list, map, or set type) then
|
||||
the `dynamic` construct will generate a _single_ dynamic block whose iterator
|
||||
key and value are both unknown values of the dynamic pseudo-type, thus causing
|
||||
any attribute values derived from iteration to appear as unknown values. There
|
||||
is no explicit representation of the fact that the length of the collection may
|
||||
eventually be different than one.
|
||||
|
||||
## Usage
|
||||
|
||||
Pass a body to function `Expand` to obtain a new body that will, on access
|
||||
to its content, evaluate and expand any nested `dynamic` blocks.
|
||||
Dynamic block processing is also automatically propagated into any nested
|
||||
blocks that are returned, allowing users to nest dynamic blocks inside
|
||||
one another and to nest dynamic blocks inside other static blocks.
|
||||
|
||||
HCL structural decoding does not normally have access to an `EvalContext`, so
|
||||
any variables and functions that should be available to the `for_each`
|
||||
and `labels` expressions must be passed in when calling `Expand`. Expressions
|
||||
within the `content` block are evaluated separately and so can be passed a
|
||||
separate `EvalContext` if desired, during normal attribute expression
|
||||
evaluation.
|
||||
|
||||
## Detecting Variables
|
||||
|
||||
Some applications dynamically generate an `EvalContext` by analyzing which
|
||||
variables are referenced by an expression before evaluating it.
|
||||
|
||||
This unfortunately requires some extra effort when this analysis is required
|
||||
for the context passed to `Expand`: the HCL API requires a schema to be
|
||||
provided in order to do any analysis of the blocks in a body, but the low-level
|
||||
schema model provides a description of only one level of nested blocks at
|
||||
a time, and thus a new schema must be provided for each additional level of
|
||||
nesting.
|
||||
|
||||
To make this arduous process as convenient as possible, this package provides
|
||||
a helper function `WalkForEachVariables`, which returns a `WalkVariablesNode`
|
||||
instance that can be used to find variables directly in a given body and also
|
||||
determine which nested blocks require recursive calls. Using this mechanism
|
||||
requires that the caller be able to look up a schema given a nested block type.
|
||||
For _simple_ formats where a specific block type name always has the same schema
|
||||
regardless of context, a walk can be implemented as follows:
|
||||
|
||||
```go
|
||||
func walkVariables(node dynblock.WalkVariablesNode, schema *hcl.BodySchema) []hcl.Traversal {
|
||||
vars, children := node.Visit(schema)
|
||||
|
||||
for _, child := range children {
|
||||
var childSchema *hcl.BodySchema
|
||||
switch child.BlockTypeName {
|
||||
case "a":
|
||||
childSchema = &hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "b",
|
||||
LabelNames: []string{"key"},
|
||||
},
|
||||
},
|
||||
}
|
||||
case "b":
|
||||
childSchema = &hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "val",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
default:
|
||||
// Should never happen, because the above cases should be exhaustive
|
||||
// for the application's configuration format.
|
||||
panic(fmt.Errorf("can't find schema for unknown block type %q", child.BlockTypeName))
|
||||
}
|
||||
|
||||
vars = append(vars, testWalkAndAccumVars(child.Node, childSchema)...)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Detecting Variables with `hcldec` Specifications
|
||||
|
||||
For applications that use the higher-level `hcldec` package to decode nested
|
||||
configuration structures into `cty` values, the same specification can be used
|
||||
to automatically drive the recursive variable-detection walk described above.
|
||||
|
||||
The helper function `ForEachVariablesHCLDec` allows an entire recursive
|
||||
configuration structure to be analyzed in a single call given a `hcldec.Spec`
|
||||
that describes the nested block structure. This means a `hcldec`-based
|
||||
application can support dynamic blocks with only a little additional effort:
|
||||
|
||||
```go
|
||||
func decodeBody(body hcl.Body, spec hcldec.Spec) (cty.Value, hcl.Diagnostics) {
|
||||
// Determine which variables are needed to expand dynamic blocks
|
||||
neededForDynamic := dynblock.ForEachVariablesHCLDec(body, spec)
|
||||
|
||||
// Build a suitable EvalContext and expand dynamic blocks
|
||||
dynCtx := buildEvalContext(neededForDynamic)
|
||||
dynBody := dynblock.Expand(body, dynCtx)
|
||||
|
||||
// Determine which variables are needed to fully decode the expanded body
|
||||
// This will analyze expressions that came both from static blocks in the
|
||||
// original body and from blocks that were dynamically added by Expand.
|
||||
neededForDecode := hcldec.Variables(dynBody, spec)
|
||||
|
||||
// Build a suitable EvalContext and then fully decode the body as per the
|
||||
// hcldec specification.
|
||||
decCtx := buildEvalContext(neededForDecode)
|
||||
return hcldec.Decode(dynBody, spec, decCtx)
|
||||
}
|
||||
|
||||
func buildEvalContext(needed []hcl.Traversal) *hcl.EvalContext {
|
||||
// (to be implemented by your application)
|
||||
}
|
||||
```
|
||||
|
||||
# Performance
|
||||
|
||||
This extension is going quite harshly against the grain of the HCL API, and
|
||||
so it uses lots of wrapping objects and temporary data structures to get its
|
||||
work done. HCL in general is not suitable for use in high-performance situations
|
||||
or situations sensitive to memory pressure, but that is _especially_ true for
|
||||
this extension.
|
||||
262
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/expand_body.go
generated
vendored
Normal file
262
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/expand_body.go
generated
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
package dynblock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// expandBody wraps another hcl.Body and expands any "dynamic" blocks found
|
||||
// inside whenever Content or PartialContent is called.
|
||||
type expandBody struct {
|
||||
original hcl.Body
|
||||
forEachCtx *hcl.EvalContext
|
||||
iteration *iteration // non-nil if we're nested inside another "dynamic" block
|
||||
|
||||
// These are used with PartialContent to produce a "remaining items"
|
||||
// body to return. They are nil on all bodies fresh out of the transformer.
|
||||
//
|
||||
// Note that this is re-implemented here rather than delegating to the
|
||||
// existing support required by the underlying body because we need to
|
||||
// retain access to the entire original body on subsequent decode operations
|
||||
// so we can retain any "dynamic" blocks for types we didn't take consume
|
||||
// on the first pass.
|
||||
hiddenAttrs map[string]struct{}
|
||||
hiddenBlocks map[string]hcl.BlockHeaderSchema
|
||||
}
|
||||
|
||||
func (b *expandBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
|
||||
extSchema := b.extendSchema(schema)
|
||||
rawContent, diags := b.original.Content(extSchema)
|
||||
|
||||
blocks, blockDiags := b.expandBlocks(schema, rawContent.Blocks, false)
|
||||
diags = append(diags, blockDiags...)
|
||||
attrs := b.prepareAttributes(rawContent.Attributes)
|
||||
|
||||
content := &hcl.BodyContent{
|
||||
Attributes: attrs,
|
||||
Blocks: blocks,
|
||||
MissingItemRange: b.original.MissingItemRange(),
|
||||
}
|
||||
|
||||
return content, diags
|
||||
}
|
||||
|
||||
func (b *expandBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
|
||||
extSchema := b.extendSchema(schema)
|
||||
rawContent, _, diags := b.original.PartialContent(extSchema)
|
||||
// We discard the "remain" argument above because we're going to construct
|
||||
// our own remain that also takes into account remaining "dynamic" blocks.
|
||||
|
||||
blocks, blockDiags := b.expandBlocks(schema, rawContent.Blocks, true)
|
||||
diags = append(diags, blockDiags...)
|
||||
attrs := b.prepareAttributes(rawContent.Attributes)
|
||||
|
||||
content := &hcl.BodyContent{
|
||||
Attributes: attrs,
|
||||
Blocks: blocks,
|
||||
MissingItemRange: b.original.MissingItemRange(),
|
||||
}
|
||||
|
||||
remain := &expandBody{
|
||||
original: b.original,
|
||||
forEachCtx: b.forEachCtx,
|
||||
iteration: b.iteration,
|
||||
hiddenAttrs: make(map[string]struct{}),
|
||||
hiddenBlocks: make(map[string]hcl.BlockHeaderSchema),
|
||||
}
|
||||
for name := range b.hiddenAttrs {
|
||||
remain.hiddenAttrs[name] = struct{}{}
|
||||
}
|
||||
for typeName, blockS := range b.hiddenBlocks {
|
||||
remain.hiddenBlocks[typeName] = blockS
|
||||
}
|
||||
for _, attrS := range schema.Attributes {
|
||||
remain.hiddenAttrs[attrS.Name] = struct{}{}
|
||||
}
|
||||
for _, blockS := range schema.Blocks {
|
||||
remain.hiddenBlocks[blockS.Type] = blockS
|
||||
}
|
||||
|
||||
return content, remain, diags
|
||||
}
|
||||
|
||||
func (b *expandBody) extendSchema(schema *hcl.BodySchema) *hcl.BodySchema {
|
||||
// We augment the requested schema to also include our special "dynamic"
|
||||
// block type, since then we'll get instances of it interleaved with
|
||||
// all of the literal child blocks we must also include.
|
||||
extSchema := &hcl.BodySchema{
|
||||
Attributes: schema.Attributes,
|
||||
Blocks: make([]hcl.BlockHeaderSchema, len(schema.Blocks), len(schema.Blocks)+len(b.hiddenBlocks)+1),
|
||||
}
|
||||
copy(extSchema.Blocks, schema.Blocks)
|
||||
extSchema.Blocks = append(extSchema.Blocks, dynamicBlockHeaderSchema)
|
||||
|
||||
// If we have any hiddenBlocks then we also need to register those here
|
||||
// so that a call to "Content" on the underlying body won't fail.
|
||||
// (We'll filter these out again once we process the result of either
|
||||
// Content or PartialContent.)
|
||||
for _, blockS := range b.hiddenBlocks {
|
||||
extSchema.Blocks = append(extSchema.Blocks, blockS)
|
||||
}
|
||||
|
||||
// If we have any hiddenAttrs then we also need to register these, for
|
||||
// the same reason as we deal with hiddenBlocks above.
|
||||
if len(b.hiddenAttrs) != 0 {
|
||||
newAttrs := make([]hcl.AttributeSchema, len(schema.Attributes), len(schema.Attributes)+len(b.hiddenAttrs))
|
||||
copy(newAttrs, extSchema.Attributes)
|
||||
for name := range b.hiddenAttrs {
|
||||
newAttrs = append(newAttrs, hcl.AttributeSchema{
|
||||
Name: name,
|
||||
Required: false,
|
||||
})
|
||||
}
|
||||
extSchema.Attributes = newAttrs
|
||||
}
|
||||
|
||||
return extSchema
|
||||
}
|
||||
|
||||
func (b *expandBody) prepareAttributes(rawAttrs hcl.Attributes) hcl.Attributes {
|
||||
if len(b.hiddenAttrs) == 0 && b.iteration == nil {
|
||||
// Easy path: just pass through the attrs from the original body verbatim
|
||||
return rawAttrs
|
||||
}
|
||||
|
||||
// Otherwise we have some work to do: we must filter out any attributes
|
||||
// that are hidden (since a previous PartialContent call already saw these)
|
||||
// and wrap the expressions of the inner attributes so that they will
|
||||
// have access to our iteration variables.
|
||||
attrs := make(hcl.Attributes, len(rawAttrs))
|
||||
for name, rawAttr := range rawAttrs {
|
||||
if _, hidden := b.hiddenAttrs[name]; hidden {
|
||||
continue
|
||||
}
|
||||
if b.iteration != nil {
|
||||
attr := *rawAttr // shallow copy so we can mutate it
|
||||
attr.Expr = exprWrap{
|
||||
Expression: attr.Expr,
|
||||
i: b.iteration,
|
||||
}
|
||||
attrs[name] = &attr
|
||||
} else {
|
||||
// If we have no active iteration then no wrapping is required.
|
||||
attrs[name] = rawAttr
|
||||
}
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
func (b *expandBody) expandBlocks(schema *hcl.BodySchema, rawBlocks hcl.Blocks, partial bool) (hcl.Blocks, hcl.Diagnostics) {
|
||||
var blocks hcl.Blocks
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
for _, rawBlock := range rawBlocks {
|
||||
switch rawBlock.Type {
|
||||
case "dynamic":
|
||||
realBlockType := rawBlock.Labels[0]
|
||||
if _, hidden := b.hiddenBlocks[realBlockType]; hidden {
|
||||
continue
|
||||
}
|
||||
|
||||
var blockS *hcl.BlockHeaderSchema
|
||||
for _, candidate := range schema.Blocks {
|
||||
if candidate.Type == realBlockType {
|
||||
blockS = &candidate
|
||||
break
|
||||
}
|
||||
}
|
||||
if blockS == nil {
|
||||
// Not a block type that the caller requested.
|
||||
if !partial {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Unsupported block type",
|
||||
Detail: fmt.Sprintf("Blocks of type %q are not expected here.", realBlockType),
|
||||
Subject: &rawBlock.LabelRanges[0],
|
||||
})
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
spec, specDiags := b.decodeSpec(blockS, rawBlock)
|
||||
diags = append(diags, specDiags...)
|
||||
if specDiags.HasErrors() {
|
||||
continue
|
||||
}
|
||||
|
||||
if spec.forEachVal.IsKnown() {
|
||||
for it := spec.forEachVal.ElementIterator(); it.Next(); {
|
||||
key, value := it.Element()
|
||||
i := b.iteration.MakeChild(spec.iteratorName, key, value)
|
||||
|
||||
block, blockDiags := spec.newBlock(i, b.forEachCtx)
|
||||
diags = append(diags, blockDiags...)
|
||||
if block != nil {
|
||||
// Attach our new iteration context so that attributes
|
||||
// and other nested blocks can refer to our iterator.
|
||||
block.Body = b.expandChild(block.Body, i)
|
||||
blocks = append(blocks, block)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If our top-level iteration value isn't known then we're forced
|
||||
// to compromise since HCL doesn't have any concept of an
|
||||
// "unknown block". In this case then, we'll produce a single
|
||||
// dynamic block with the iterator values set to DynamicVal,
|
||||
// which at least makes the potential for a block visible
|
||||
// in our result, even though it's not represented in a fully-accurate
|
||||
// way.
|
||||
i := b.iteration.MakeChild(spec.iteratorName, cty.DynamicVal, cty.DynamicVal)
|
||||
block, blockDiags := spec.newBlock(i, b.forEachCtx)
|
||||
diags = append(diags, blockDiags...)
|
||||
if block != nil {
|
||||
block.Body = b.expandChild(block.Body, i)
|
||||
|
||||
// We additionally force all of the leaf attribute values
|
||||
// in the result to be unknown so the calling application
|
||||
// can, if necessary, use that as a heuristic to detect
|
||||
// when a single nested block might be standing in for
|
||||
// multiple blocks yet to be expanded. This retains the
|
||||
// structure of the generated body but forces all of its
|
||||
// leaf attribute values to be unknown.
|
||||
block.Body = unknownBody{block.Body}
|
||||
|
||||
blocks = append(blocks, block)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if _, hidden := b.hiddenBlocks[rawBlock.Type]; !hidden {
|
||||
// A static block doesn't create a new iteration context, but
|
||||
// it does need to inherit _our own_ iteration context in
|
||||
// case it contains expressions that refer to our inherited
|
||||
// iterators, or nested "dynamic" blocks.
|
||||
expandedBlock := *rawBlock // shallow copy
|
||||
expandedBlock.Body = b.expandChild(rawBlock.Body, b.iteration)
|
||||
blocks = append(blocks, &expandedBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blocks, diags
|
||||
}
|
||||
|
||||
func (b *expandBody) expandChild(child hcl.Body, i *iteration) hcl.Body {
|
||||
chiCtx := i.EvalContext(b.forEachCtx)
|
||||
ret := Expand(child, chiCtx)
|
||||
ret.(*expandBody).iteration = i
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b *expandBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
|
||||
// blocks aren't allowed in JustAttributes mode and this body can
|
||||
// only produce blocks, so we'll just pass straight through to our
|
||||
// underlying body here.
|
||||
return b.original.JustAttributes()
|
||||
}
|
||||
|
||||
func (b *expandBody) MissingItemRange() hcl.Range {
|
||||
return b.original.MissingItemRange()
|
||||
}
|
||||
215
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/expand_spec.go
generated
vendored
Normal file
215
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/expand_spec.go
generated
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
package dynblock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
)
|
||||
|
||||
type expandSpec struct {
|
||||
blockType string
|
||||
blockTypeRange hcl.Range
|
||||
defRange hcl.Range
|
||||
forEachVal cty.Value
|
||||
iteratorName string
|
||||
labelExprs []hcl.Expression
|
||||
contentBody hcl.Body
|
||||
inherited map[string]*iteration
|
||||
}
|
||||
|
||||
func (b *expandBody) decodeSpec(blockS *hcl.BlockHeaderSchema, rawSpec *hcl.Block) (*expandSpec, hcl.Diagnostics) {
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
var schema *hcl.BodySchema
|
||||
if len(blockS.LabelNames) != 0 {
|
||||
schema = dynamicBlockBodySchemaLabels
|
||||
} else {
|
||||
schema = dynamicBlockBodySchemaNoLabels
|
||||
}
|
||||
|
||||
specContent, specDiags := rawSpec.Body.Content(schema)
|
||||
diags = append(diags, specDiags...)
|
||||
if specDiags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
//// for_each attribute
|
||||
|
||||
eachAttr := specContent.Attributes["for_each"]
|
||||
eachVal, eachDiags := eachAttr.Expr.Value(b.forEachCtx)
|
||||
diags = append(diags, eachDiags...)
|
||||
|
||||
if !eachVal.CanIterateElements() && eachVal.Type() != cty.DynamicPseudoType {
|
||||
// We skip this error for DynamicPseudoType because that means we either
|
||||
// have a null (which is checked immediately below) or an unknown
|
||||
// (which is handled in the expandBody Content methods).
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic for_each value",
|
||||
Detail: fmt.Sprintf("Cannot use a %s value in for_each. An iterable collection is required.", eachVal.Type().FriendlyName()),
|
||||
Subject: eachAttr.Expr.Range().Ptr(),
|
||||
Expression: eachAttr.Expr,
|
||||
EvalContext: b.forEachCtx,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
if eachVal.IsNull() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic for_each value",
|
||||
Detail: "Cannot use a null value in for_each.",
|
||||
Subject: eachAttr.Expr.Range().Ptr(),
|
||||
Expression: eachAttr.Expr,
|
||||
EvalContext: b.forEachCtx,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
//// iterator attribute
|
||||
|
||||
iteratorName := blockS.Type
|
||||
if iteratorAttr := specContent.Attributes["iterator"]; iteratorAttr != nil {
|
||||
itTraversal, itDiags := hcl.AbsTraversalForExpr(iteratorAttr.Expr)
|
||||
diags = append(diags, itDiags...)
|
||||
if itDiags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
if len(itTraversal) != 1 {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic iterator name",
|
||||
Detail: "Dynamic iterator must be a single variable name.",
|
||||
Subject: itTraversal.SourceRange().Ptr(),
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
iteratorName = itTraversal.RootName()
|
||||
}
|
||||
|
||||
var labelExprs []hcl.Expression
|
||||
if labelsAttr := specContent.Attributes["labels"]; labelsAttr != nil {
|
||||
var labelDiags hcl.Diagnostics
|
||||
labelExprs, labelDiags = hcl.ExprList(labelsAttr.Expr)
|
||||
diags = append(diags, labelDiags...)
|
||||
if labelDiags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
if len(labelExprs) > len(blockS.LabelNames) {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Extraneous dynamic block label",
|
||||
Detail: fmt.Sprintf("Blocks of type %q require %d label(s).", blockS.Type, len(blockS.LabelNames)),
|
||||
Subject: labelExprs[len(blockS.LabelNames)].Range().Ptr(),
|
||||
})
|
||||
return nil, diags
|
||||
} else if len(labelExprs) < len(blockS.LabelNames) {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Insufficient dynamic block labels",
|
||||
Detail: fmt.Sprintf("Blocks of type %q require %d label(s).", blockS.Type, len(blockS.LabelNames)),
|
||||
Subject: labelsAttr.Expr.Range().Ptr(),
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
}
|
||||
|
||||
// Since our schema requests only blocks of type "content", we can assume
|
||||
// that all entries in specContent.Blocks are content blocks.
|
||||
if len(specContent.Blocks) == 0 {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Missing dynamic content block",
|
||||
Detail: "A dynamic block must have a nested block of type \"content\" to describe the body of each generated block.",
|
||||
Subject: &specContent.MissingItemRange,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
if len(specContent.Blocks) > 1 {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Extraneous dynamic content block",
|
||||
Detail: "Only one nested content block is allowed for each dynamic block.",
|
||||
Subject: &specContent.Blocks[1].DefRange,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
return &expandSpec{
|
||||
blockType: blockS.Type,
|
||||
blockTypeRange: rawSpec.LabelRanges[0],
|
||||
defRange: rawSpec.DefRange,
|
||||
forEachVal: eachVal,
|
||||
iteratorName: iteratorName,
|
||||
labelExprs: labelExprs,
|
||||
contentBody: specContent.Blocks[0].Body,
|
||||
}, diags
|
||||
}
|
||||
|
||||
func (s *expandSpec) newBlock(i *iteration, ctx *hcl.EvalContext) (*hcl.Block, hcl.Diagnostics) {
|
||||
var diags hcl.Diagnostics
|
||||
var labels []string
|
||||
var labelRanges []hcl.Range
|
||||
lCtx := i.EvalContext(ctx)
|
||||
for _, labelExpr := range s.labelExprs {
|
||||
labelVal, labelDiags := labelExpr.Value(lCtx)
|
||||
diags = append(diags, labelDiags...)
|
||||
if labelDiags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
var convErr error
|
||||
labelVal, convErr = convert.Convert(labelVal, cty.String)
|
||||
if convErr != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic block label",
|
||||
Detail: fmt.Sprintf("Cannot use this value as a dynamic block label: %s.", convErr),
|
||||
Subject: labelExpr.Range().Ptr(),
|
||||
Expression: labelExpr,
|
||||
EvalContext: lCtx,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
if labelVal.IsNull() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic block label",
|
||||
Detail: "Cannot use a null value as a dynamic block label.",
|
||||
Subject: labelExpr.Range().Ptr(),
|
||||
Expression: labelExpr,
|
||||
EvalContext: lCtx,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
if !labelVal.IsKnown() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid dynamic block label",
|
||||
Detail: "This value is not yet known. Dynamic block labels must be immediately-known values.",
|
||||
Subject: labelExpr.Range().Ptr(),
|
||||
Expression: labelExpr,
|
||||
EvalContext: lCtx,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
labels = append(labels, labelVal.AsString())
|
||||
labelRanges = append(labelRanges, labelExpr.Range())
|
||||
}
|
||||
|
||||
block := &hcl.Block{
|
||||
Type: s.blockType,
|
||||
TypeRange: s.blockTypeRange,
|
||||
Labels: labels,
|
||||
LabelRanges: labelRanges,
|
||||
DefRange: s.defRange,
|
||||
Body: s.contentBody,
|
||||
}
|
||||
|
||||
return block, diags
|
||||
}
|
||||
42
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/expr_wrap.go
generated
vendored
Normal file
42
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/expr_wrap.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package dynblock
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
type exprWrap struct {
|
||||
hcl.Expression
|
||||
i *iteration
|
||||
}
|
||||
|
||||
func (e exprWrap) Variables() []hcl.Traversal {
|
||||
raw := e.Expression.Variables()
|
||||
ret := make([]hcl.Traversal, 0, len(raw))
|
||||
|
||||
// Filter out traversals that refer to our iterator name or any
|
||||
// iterator we've inherited; we're going to provide those in
|
||||
// our Value wrapper, so the caller doesn't need to know about them.
|
||||
for _, traversal := range raw {
|
||||
rootName := traversal.RootName()
|
||||
if rootName == e.i.IteratorName {
|
||||
continue
|
||||
}
|
||||
if _, inherited := e.i.Inherited[rootName]; inherited {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, traversal)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (e exprWrap) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
||||
extCtx := e.i.EvalContext(ctx)
|
||||
return e.Expression.Value(extCtx)
|
||||
}
|
||||
|
||||
// UnwrapExpression returns the expression being wrapped by this instance.
|
||||
// This allows the original expression to be recovered by hcl.UnwrapExpression.
|
||||
func (e exprWrap) UnwrapExpression() hcl.Expression {
|
||||
return e.Expression
|
||||
}
|
||||
66
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/iteration.go
generated
vendored
Normal file
66
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/iteration.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
package dynblock
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
type iteration struct {
|
||||
IteratorName string
|
||||
Key cty.Value
|
||||
Value cty.Value
|
||||
Inherited map[string]*iteration
|
||||
}
|
||||
|
||||
func (s *expandSpec) MakeIteration(key, value cty.Value) *iteration {
|
||||
return &iteration{
|
||||
IteratorName: s.iteratorName,
|
||||
Key: key,
|
||||
Value: value,
|
||||
Inherited: s.inherited,
|
||||
}
|
||||
}
|
||||
|
||||
func (i *iteration) Object() cty.Value {
|
||||
return cty.ObjectVal(map[string]cty.Value{
|
||||
"key": i.Key,
|
||||
"value": i.Value,
|
||||
})
|
||||
}
|
||||
|
||||
func (i *iteration) EvalContext(base *hcl.EvalContext) *hcl.EvalContext {
|
||||
new := base.NewChild()
|
||||
|
||||
if i != nil {
|
||||
new.Variables = map[string]cty.Value{}
|
||||
for name, otherIt := range i.Inherited {
|
||||
new.Variables[name] = otherIt.Object()
|
||||
}
|
||||
new.Variables[i.IteratorName] = i.Object()
|
||||
}
|
||||
|
||||
return new
|
||||
}
|
||||
|
||||
func (i *iteration) MakeChild(iteratorName string, key, value cty.Value) *iteration {
|
||||
if i == nil {
|
||||
// Create entirely new root iteration, then
|
||||
return &iteration{
|
||||
IteratorName: iteratorName,
|
||||
Key: key,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
inherited := map[string]*iteration{}
|
||||
for name, otherIt := range i.Inherited {
|
||||
inherited[name] = otherIt
|
||||
}
|
||||
inherited[i.IteratorName] = i
|
||||
return &iteration{
|
||||
IteratorName: iteratorName,
|
||||
Key: key,
|
||||
Value: value,
|
||||
Inherited: inherited,
|
||||
}
|
||||
}
|
||||
47
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/public.go
generated
vendored
Normal file
47
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/public.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Package dynblock provides an extension to HCL that allows dynamic
|
||||
// declaration of nested blocks in certain contexts via a special block type
|
||||
// named "dynamic".
|
||||
package dynblock
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
)
|
||||
|
||||
// Expand "dynamic" blocks in the given body, returning a new body that
|
||||
// has those blocks expanded.
|
||||
//
|
||||
// The given EvalContext is used when evaluating "for_each" and "labels"
|
||||
// attributes within dynamic blocks, allowing those expressions access to
|
||||
// variables and functions beyond the iterator variable created by the
|
||||
// iteration.
|
||||
//
|
||||
// Expand returns no diagnostics because no blocks are actually expanded
|
||||
// until a call to Content or PartialContent on the returned body, which
|
||||
// will then expand only the blocks selected by the schema.
|
||||
//
|
||||
// "dynamic" blocks are also expanded automatically within nested blocks
|
||||
// in the given body, including within other dynamic blocks, thus allowing
|
||||
// multi-dimensional iteration. However, it is not possible to
|
||||
// dynamically-generate the "dynamic" blocks themselves except through nesting.
|
||||
//
|
||||
// parent {
|
||||
// dynamic "child" {
|
||||
// for_each = child_objs
|
||||
// content {
|
||||
// dynamic "grandchild" {
|
||||
// for_each = child.value.children
|
||||
// labels = [grandchild.key]
|
||||
// content {
|
||||
// parent_key = child.key
|
||||
// value = grandchild.value
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
func Expand(body hcl.Body, ctx *hcl.EvalContext) hcl.Body {
|
||||
return &expandBody{
|
||||
original: body,
|
||||
forEachCtx: ctx,
|
||||
}
|
||||
}
|
||||
50
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/schema.go
generated
vendored
Normal file
50
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/schema.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
package dynblock
|
||||
|
||||
import "github.com/hashicorp/hcl/v2"
|
||||
|
||||
var dynamicBlockHeaderSchema = hcl.BlockHeaderSchema{
|
||||
Type: "dynamic",
|
||||
LabelNames: []string{"type"},
|
||||
}
|
||||
|
||||
var dynamicBlockBodySchemaLabels = &hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "for_each",
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "iterator",
|
||||
Required: false,
|
||||
},
|
||||
{
|
||||
Name: "labels",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "content",
|
||||
LabelNames: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var dynamicBlockBodySchemaNoLabels = &hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "for_each",
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "iterator",
|
||||
Required: false,
|
||||
},
|
||||
},
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "content",
|
||||
LabelNames: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
84
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/unknown_body.go
generated
vendored
Normal file
84
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/unknown_body.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
package dynblock
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// unknownBody is a funny body that just reports everything inside it as
|
||||
// unknown. It uses a given other body as a sort of template for what attributes
|
||||
// and blocks are inside -- including source location information -- but
|
||||
// subsitutes unknown values of unknown type for all attributes.
|
||||
//
|
||||
// This rather odd process is used to handle expansion of dynamic blocks whose
|
||||
// for_each expression is unknown. Since a block cannot itself be unknown,
|
||||
// we instead arrange for everything _inside_ the block to be unknown instead,
|
||||
// to give the best possible approximation.
|
||||
type unknownBody struct {
|
||||
template hcl.Body
|
||||
}
|
||||
|
||||
var _ hcl.Body = unknownBody{}
|
||||
|
||||
func (b unknownBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
|
||||
content, diags := b.template.Content(schema)
|
||||
content = b.fixupContent(content)
|
||||
|
||||
// We're intentionally preserving the diagnostics reported from the
|
||||
// inner body so that we can still report where the template body doesn't
|
||||
// match the requested schema.
|
||||
return content, diags
|
||||
}
|
||||
|
||||
func (b unknownBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
|
||||
content, remain, diags := b.template.PartialContent(schema)
|
||||
content = b.fixupContent(content)
|
||||
remain = unknownBody{remain} // remaining content must also be wrapped
|
||||
|
||||
// We're intentionally preserving the diagnostics reported from the
|
||||
// inner body so that we can still report where the template body doesn't
|
||||
// match the requested schema.
|
||||
return content, remain, diags
|
||||
}
|
||||
|
||||
func (b unknownBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
|
||||
attrs, diags := b.template.JustAttributes()
|
||||
attrs = b.fixupAttrs(attrs)
|
||||
|
||||
// We're intentionally preserving the diagnostics reported from the
|
||||
// inner body so that we can still report where the template body doesn't
|
||||
// match the requested schema.
|
||||
return attrs, diags
|
||||
}
|
||||
|
||||
func (b unknownBody) MissingItemRange() hcl.Range {
|
||||
return b.template.MissingItemRange()
|
||||
}
|
||||
|
||||
func (b unknownBody) fixupContent(got *hcl.BodyContent) *hcl.BodyContent {
|
||||
ret := &hcl.BodyContent{}
|
||||
ret.Attributes = b.fixupAttrs(got.Attributes)
|
||||
if len(got.Blocks) > 0 {
|
||||
ret.Blocks = make(hcl.Blocks, 0, len(got.Blocks))
|
||||
for _, gotBlock := range got.Blocks {
|
||||
new := *gotBlock // shallow copy
|
||||
new.Body = unknownBody{gotBlock.Body} // nested content must also be marked unknown
|
||||
ret.Blocks = append(ret.Blocks, &new)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b unknownBody) fixupAttrs(got hcl.Attributes) hcl.Attributes {
|
||||
if len(got) == 0 {
|
||||
return nil
|
||||
}
|
||||
ret := make(hcl.Attributes, len(got))
|
||||
for name, gotAttr := range got {
|
||||
new := *gotAttr // shallow copy
|
||||
new.Expr = hcl.StaticExpr(cty.DynamicVal, gotAttr.Expr.Range())
|
||||
ret[name] = &new
|
||||
}
|
||||
return ret
|
||||
}
|
||||
209
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/variables.go
generated
vendored
Normal file
209
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/variables.go
generated
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
package dynblock
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// WalkVariables begins the recursive process of walking all expressions and
|
||||
// nested blocks in the given body and its child bodies while taking into
|
||||
// account any "dynamic" blocks.
|
||||
//
|
||||
// This function requires that the caller walk through the nested block
|
||||
// structure in the given body level-by-level so that an appropriate schema
|
||||
// can be provided at each level to inform further processing. This workflow
|
||||
// is thus easiest to use for calling applications that have some higher-level
|
||||
// schema representation available with which to drive this multi-step
|
||||
// process. If your application uses the hcldec package, you may be able to
|
||||
// use VariablesHCLDec instead for a more automatic approach.
|
||||
func WalkVariables(body hcl.Body) WalkVariablesNode {
|
||||
return WalkVariablesNode{
|
||||
body: body,
|
||||
includeContent: true,
|
||||
}
|
||||
}
|
||||
|
||||
// WalkExpandVariables is like Variables but it includes only the variables
|
||||
// required for successful block expansion, ignoring any variables referenced
|
||||
// inside block contents. The result is the minimal set of all variables
|
||||
// required for a call to Expand, excluding variables that would only be
|
||||
// needed to subsequently call Content or PartialContent on the expanded
|
||||
// body.
|
||||
func WalkExpandVariables(body hcl.Body) WalkVariablesNode {
|
||||
return WalkVariablesNode{
|
||||
body: body,
|
||||
}
|
||||
}
|
||||
|
||||
type WalkVariablesNode struct {
|
||||
body hcl.Body
|
||||
it *iteration
|
||||
|
||||
includeContent bool
|
||||
}
|
||||
|
||||
type WalkVariablesChild struct {
|
||||
BlockTypeName string
|
||||
Node WalkVariablesNode
|
||||
}
|
||||
|
||||
// Body returns the HCL Body associated with the child node, in case the caller
|
||||
// wants to do some sort of inspection of it in order to decide what schema
|
||||
// to pass to Visit.
|
||||
//
|
||||
// Most implementations should just fetch a fixed schema based on the
|
||||
// BlockTypeName field and not access this. Deciding on a schema dynamically
|
||||
// based on the body is a strange thing to do and generally necessary only if
|
||||
// your caller is already doing other bizarre things with HCL bodies.
|
||||
func (c WalkVariablesChild) Body() hcl.Body {
|
||||
return c.Node.body
|
||||
}
|
||||
|
||||
// Visit returns the variable traversals required for any "dynamic" blocks
|
||||
// directly in the body associated with this node, and also returns any child
|
||||
// nodes that must be visited in order to continue the walk.
|
||||
//
|
||||
// Each child node has its associated block type name given in its BlockTypeName
|
||||
// field, which the calling application should use to determine the appropriate
|
||||
// schema for the content of each child node and pass it to the child node's
|
||||
// own Visit method to continue the walk recursively.
|
||||
func (n WalkVariablesNode) Visit(schema *hcl.BodySchema) (vars []hcl.Traversal, children []WalkVariablesChild) {
|
||||
extSchema := n.extendSchema(schema)
|
||||
container, _, _ := n.body.PartialContent(extSchema)
|
||||
if container == nil {
|
||||
return vars, children
|
||||
}
|
||||
|
||||
children = make([]WalkVariablesChild, 0, len(container.Blocks))
|
||||
|
||||
if n.includeContent {
|
||||
for _, attr := range container.Attributes {
|
||||
for _, traversal := range attr.Expr.Variables() {
|
||||
var ours, inherited bool
|
||||
if n.it != nil {
|
||||
ours = traversal.RootName() == n.it.IteratorName
|
||||
_, inherited = n.it.Inherited[traversal.RootName()]
|
||||
}
|
||||
|
||||
if !(ours || inherited) {
|
||||
vars = append(vars, traversal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, block := range container.Blocks {
|
||||
switch block.Type {
|
||||
|
||||
case "dynamic":
|
||||
blockTypeName := block.Labels[0]
|
||||
inner, _, _ := block.Body.PartialContent(variableDetectionInnerSchema)
|
||||
if inner == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
iteratorName := blockTypeName
|
||||
if attr, exists := inner.Attributes["iterator"]; exists {
|
||||
iterTraversal, _ := hcl.AbsTraversalForExpr(attr.Expr)
|
||||
if len(iterTraversal) == 0 {
|
||||
// Ignore this invalid dynamic block, since it'll produce
|
||||
// an error if someone tries to extract content from it
|
||||
// later anyway.
|
||||
continue
|
||||
}
|
||||
iteratorName = iterTraversal.RootName()
|
||||
}
|
||||
blockIt := n.it.MakeChild(iteratorName, cty.DynamicVal, cty.DynamicVal)
|
||||
|
||||
if attr, exists := inner.Attributes["for_each"]; exists {
|
||||
// Filter out iterator names inherited from parent blocks
|
||||
for _, traversal := range attr.Expr.Variables() {
|
||||
if _, inherited := blockIt.Inherited[traversal.RootName()]; !inherited {
|
||||
vars = append(vars, traversal)
|
||||
}
|
||||
}
|
||||
}
|
||||
if attr, exists := inner.Attributes["labels"]; exists {
|
||||
// Filter out both our own iterator name _and_ those inherited
|
||||
// from parent blocks, since we provide _both_ of these to the
|
||||
// label expressions.
|
||||
for _, traversal := range attr.Expr.Variables() {
|
||||
ours := traversal.RootName() == iteratorName
|
||||
_, inherited := blockIt.Inherited[traversal.RootName()]
|
||||
|
||||
if !(ours || inherited) {
|
||||
vars = append(vars, traversal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, contentBlock := range inner.Blocks {
|
||||
// We only request "content" blocks in our schema, so we know
|
||||
// any blocks we find here will be content blocks. We require
|
||||
// exactly one content block for actual expansion, but we'll
|
||||
// be more liberal here so that callers can still collect
|
||||
// variables from erroneous "dynamic" blocks.
|
||||
children = append(children, WalkVariablesChild{
|
||||
BlockTypeName: blockTypeName,
|
||||
Node: WalkVariablesNode{
|
||||
body: contentBlock.Body,
|
||||
it: blockIt,
|
||||
includeContent: n.includeContent,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
default:
|
||||
children = append(children, WalkVariablesChild{
|
||||
BlockTypeName: block.Type,
|
||||
Node: WalkVariablesNode{
|
||||
body: block.Body,
|
||||
it: n.it,
|
||||
includeContent: n.includeContent,
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return vars, children
|
||||
}
|
||||
|
||||
func (n WalkVariablesNode) extendSchema(schema *hcl.BodySchema) *hcl.BodySchema {
|
||||
// We augment the requested schema to also include our special "dynamic"
|
||||
// block type, since then we'll get instances of it interleaved with
|
||||
// all of the literal child blocks we must also include.
|
||||
extSchema := &hcl.BodySchema{
|
||||
Attributes: schema.Attributes,
|
||||
Blocks: make([]hcl.BlockHeaderSchema, len(schema.Blocks), len(schema.Blocks)+1),
|
||||
}
|
||||
copy(extSchema.Blocks, schema.Blocks)
|
||||
extSchema.Blocks = append(extSchema.Blocks, dynamicBlockHeaderSchema)
|
||||
|
||||
return extSchema
|
||||
}
|
||||
|
||||
// This is a more relaxed schema than what's in schema.go, since we
|
||||
// want to maximize the amount of variables we can find even if there
|
||||
// are erroneous blocks.
|
||||
var variableDetectionInnerSchema = &hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "for_each",
|
||||
Required: false,
|
||||
},
|
||||
{
|
||||
Name: "labels",
|
||||
Required: false,
|
||||
},
|
||||
{
|
||||
Name: "iterator",
|
||||
Required: false,
|
||||
},
|
||||
},
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "content",
|
||||
},
|
||||
},
|
||||
}
|
||||
43
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/variables_hcldec.go
generated
vendored
Normal file
43
vendor/github.com/hashicorp/hcl/v2/ext/dynblock/variables_hcldec.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package dynblock
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
)
|
||||
|
||||
// VariablesHCLDec is a wrapper around WalkVariables that uses the given hcldec
|
||||
// specification to automatically drive the recursive walk through nested
|
||||
// blocks in the given body.
|
||||
//
|
||||
// This is a drop-in replacement for hcldec.Variables which is able to treat
|
||||
// blocks of type "dynamic" in the same special way that dynblock.Expand would,
|
||||
// exposing both the variables referenced in the "for_each" and "labels"
|
||||
// arguments and variables used in the nested "content" block.
|
||||
func VariablesHCLDec(body hcl.Body, spec hcldec.Spec) []hcl.Traversal {
|
||||
rootNode := WalkVariables(body)
|
||||
return walkVariablesWithHCLDec(rootNode, spec)
|
||||
}
|
||||
|
||||
// ExpandVariablesHCLDec is like VariablesHCLDec but it includes only the
|
||||
// minimal set of variables required to call Expand, ignoring variables that
|
||||
// are referenced only inside normal block contents. See WalkExpandVariables
|
||||
// for more information.
|
||||
func ExpandVariablesHCLDec(body hcl.Body, spec hcldec.Spec) []hcl.Traversal {
|
||||
rootNode := WalkExpandVariables(body)
|
||||
return walkVariablesWithHCLDec(rootNode, spec)
|
||||
}
|
||||
|
||||
func walkVariablesWithHCLDec(node WalkVariablesNode, spec hcldec.Spec) []hcl.Traversal {
|
||||
vars, children := node.Visit(hcldec.ImpliedSchema(spec))
|
||||
|
||||
if len(children) > 0 {
|
||||
childSpecs := hcldec.ChildBlockTypes(spec)
|
||||
for _, child := range children {
|
||||
if childSpec, exists := childSpecs[child.BlockTypeName]; exists {
|
||||
vars = append(vars, walkVariablesWithHCLDec(child.Node, childSpec)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vars
|
||||
}
|
||||
44
vendor/github.com/hashicorp/hcl/v2/ext/tryfunc/README.md
generated
vendored
Normal file
44
vendor/github.com/hashicorp/hcl/v2/ext/tryfunc/README.md
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
# "Try" and "can" functions
|
||||
|
||||
This Go package contains two `cty` functions intended for use in an
|
||||
`hcl.EvalContext` when evaluating HCL native syntax expressions.
|
||||
|
||||
The first function `try` attempts to evaluate each of its argument expressions
|
||||
in order until one produces a result without any errors.
|
||||
|
||||
```hcl
|
||||
try(non_existent_variable, 2) # returns 2
|
||||
```
|
||||
|
||||
If none of the expressions succeed, the function call fails with all of the
|
||||
errors it encountered.
|
||||
|
||||
The second function `can` is similar except that it ignores the result of
|
||||
the given expression altogether and simply returns `true` if the expression
|
||||
produced a successful result or `false` if it produced errors.
|
||||
|
||||
Both of these are primarily intended for working with deep data structures
|
||||
which might not have a dependable shape. For example, we can use `try` to
|
||||
attempt to fetch a value from deep inside a data structure but produce a
|
||||
default value if any step of the traversal fails:
|
||||
|
||||
```hcl
|
||||
result = try(foo.deep[0].lots.of["traversals"], null)
|
||||
```
|
||||
|
||||
The final result to `try` should generally be some sort of constant value that
|
||||
will always evaluate successfully.
|
||||
|
||||
## Using these functions
|
||||
|
||||
Languages built on HCL can make `try` and `can` available to user code by
|
||||
exporting them in the `hcl.EvalContext` used for expression evaluation:
|
||||
|
||||
```go
|
||||
ctx := &hcl.EvalContext{
|
||||
Functions: map[string]function.Function{
|
||||
"try": tryfunc.TryFunc,
|
||||
"can": tryfunc.CanFunc,
|
||||
},
|
||||
}
|
||||
```
|
||||
150
vendor/github.com/hashicorp/hcl/v2/ext/tryfunc/tryfunc.go
generated
vendored
Normal file
150
vendor/github.com/hashicorp/hcl/v2/ext/tryfunc/tryfunc.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
// Package tryfunc contains some optional functions that can be exposed in
|
||||
// HCL-based languages to allow authors to test whether a particular expression
|
||||
// can succeed and take dynamic action based on that result.
|
||||
//
|
||||
// These functions are implemented in terms of the customdecode extension from
|
||||
// the sibling directory "customdecode", and so they are only useful when
|
||||
// used within an HCL EvalContext. Other systems using cty functions are
|
||||
// unlikely to support the HCL-specific "customdecode" extension.
|
||||
package tryfunc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/ext/customdecode"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
// TryFunc is a variadic function that tries to evaluate all of is arguments
|
||||
// in sequence until one succeeds, in which case it returns that result, or
|
||||
// returns an error if none of them succeed.
|
||||
var TryFunc function.Function
|
||||
|
||||
// CanFunc tries to evaluate the expression given in its first argument.
|
||||
var CanFunc function.Function
|
||||
|
||||
func init() {
|
||||
TryFunc = function.New(&function.Spec{
|
||||
VarParam: &function.Parameter{
|
||||
Name: "expressions",
|
||||
Type: customdecode.ExpressionClosureType,
|
||||
},
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
v, err := try(args)
|
||||
if err != nil {
|
||||
return cty.NilType, err
|
||||
}
|
||||
return v.Type(), nil
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return try(args)
|
||||
},
|
||||
})
|
||||
CanFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{
|
||||
{
|
||||
Name: "expression",
|
||||
Type: customdecode.ExpressionClosureType,
|
||||
},
|
||||
},
|
||||
Type: function.StaticReturnType(cty.Bool),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
return can(args[0])
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func try(args []cty.Value) (cty.Value, error) {
|
||||
if len(args) == 0 {
|
||||
return cty.NilVal, errors.New("at least one argument is required")
|
||||
}
|
||||
|
||||
// We'll collect up all of the diagnostics we encounter along the way
|
||||
// and report them all if none of the expressions succeed, so that the
|
||||
// user might get some hints on how to make at least one succeed.
|
||||
var diags hcl.Diagnostics
|
||||
for _, arg := range args {
|
||||
closure := customdecode.ExpressionClosureFromVal(arg)
|
||||
if dependsOnUnknowns(closure.Expression, closure.EvalContext) {
|
||||
// We can't safely decide if this expression will succeed yet,
|
||||
// and so our entire result must be unknown until we have
|
||||
// more information.
|
||||
return cty.DynamicVal, nil
|
||||
}
|
||||
|
||||
v, moreDiags := closure.Value()
|
||||
diags = append(diags, moreDiags...)
|
||||
if moreDiags.HasErrors() {
|
||||
continue // try the next one, if there is one to try
|
||||
}
|
||||
return v, nil // ignore any accumulated diagnostics if one succeeds
|
||||
}
|
||||
|
||||
// If we fall out here then none of the expressions succeeded, and so
|
||||
// we must have at least one diagnostic and we'll return all of them
|
||||
// so that the user can see the errors related to whichever one they
|
||||
// were expecting to have succeeded in this case.
|
||||
//
|
||||
// Because our function must return a single error value rather than
|
||||
// diagnostics, we'll construct a suitable error message string
|
||||
// that will make sense in the context of the function call failure
|
||||
// diagnostic HCL will eventually wrap this in.
|
||||
var buf strings.Builder
|
||||
buf.WriteString("no expression succeeded:\n")
|
||||
for _, diag := range diags {
|
||||
if diag.Subject != nil {
|
||||
buf.WriteString(fmt.Sprintf("- %s (at %s)\n %s\n", diag.Summary, diag.Subject, diag.Detail))
|
||||
} else {
|
||||
buf.WriteString(fmt.Sprintf("- %s\n %s\n", diag.Summary, diag.Detail))
|
||||
}
|
||||
}
|
||||
buf.WriteString("\nAt least one expression must produce a successful result")
|
||||
return cty.NilVal, errors.New(buf.String())
|
||||
}
|
||||
|
||||
func can(arg cty.Value) (cty.Value, error) {
|
||||
closure := customdecode.ExpressionClosureFromVal(arg)
|
||||
if dependsOnUnknowns(closure.Expression, closure.EvalContext) {
|
||||
// Can't decide yet, then.
|
||||
return cty.UnknownVal(cty.Bool), nil
|
||||
}
|
||||
|
||||
_, diags := closure.Value()
|
||||
if diags.HasErrors() {
|
||||
return cty.False, nil
|
||||
}
|
||||
return cty.True, nil
|
||||
}
|
||||
|
||||
// dependsOnUnknowns returns true if any of the variables that the given
|
||||
// expression might access are unknown values or contain unknown values.
|
||||
//
|
||||
// This is a conservative result that prefers to return true if there's any
|
||||
// chance that the expression might derive from an unknown value during its
|
||||
// evaluation; it is likely to produce false-positives for more complex
|
||||
// expressions involving deep data structures.
|
||||
func dependsOnUnknowns(expr hcl.Expression, ctx *hcl.EvalContext) bool {
|
||||
for _, traversal := range expr.Variables() {
|
||||
val, diags := traversal.TraverseAbs(ctx)
|
||||
if diags.HasErrors() {
|
||||
// If the traversal returned a definitive error then it must
|
||||
// not traverse through any unknowns.
|
||||
continue
|
||||
}
|
||||
if !val.IsWhollyKnown() {
|
||||
// The value will be unknown if either it refers directly to
|
||||
// an unknown value or if the traversal moves through an unknown
|
||||
// collection. We're using IsWhollyKnown, so this also catches
|
||||
// situations where the traversal refers to a compound data
|
||||
// structure that contains any unknown values. That's important,
|
||||
// because during evaluation the expression might evaluate more
|
||||
// deeply into this structure and encounter the unknowns.
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
135
vendor/github.com/hashicorp/hcl/v2/ext/typeexpr/README.md
generated
vendored
Normal file
135
vendor/github.com/hashicorp/hcl/v2/ext/typeexpr/README.md
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
# HCL Type Expressions Extension
|
||||
|
||||
This HCL extension defines a convention for describing HCL types using function
|
||||
call and variable reference syntax, allowing configuration formats to include
|
||||
type information provided by users.
|
||||
|
||||
The type syntax is processed statically from a hcl.Expression, so it cannot
|
||||
use any of the usual language operators. This is similar to type expressions
|
||||
in statically-typed programming languages.
|
||||
|
||||
```hcl
|
||||
variable "example" {
|
||||
type = list(string)
|
||||
}
|
||||
```
|
||||
|
||||
The extension is built using the `hcl.ExprAsKeyword` and `hcl.ExprCall`
|
||||
functions, and so it relies on the underlying syntax to define how "keyword"
|
||||
and "call" are interpreted. The above shows how they are interpreted in
|
||||
the HCL native syntax, while the following shows the same information
|
||||
expressed in JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"variable": {
|
||||
"example": {
|
||||
"type": "list(string)"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notice that since we have additional contextual information that we intend
|
||||
to allow only calls and keywords the JSON syntax is able to parse the given
|
||||
string directly as an expression, rather than as a template as would be
|
||||
the case for normal expression evaluation.
|
||||
|
||||
For more information, see [the godoc reference](http://godoc.org/github.com/hashicorp/hcl/v2/ext/typeexpr).
|
||||
|
||||
## Type Expression Syntax
|
||||
|
||||
When expressed in the native syntax, the following expressions are permitted
|
||||
in a type expression:
|
||||
|
||||
* `string` - string
|
||||
* `bool` - boolean
|
||||
* `number` - number
|
||||
* `any` - `cty.DynamicPseudoType` (in function `TypeConstraint` only)
|
||||
* `list(<type_expr>)` - list of the type given as an argument
|
||||
* `set(<type_expr>)` - set of the type given as an argument
|
||||
* `map(<type_expr>)` - map of the type given as an argument
|
||||
* `tuple([<type_exprs...>])` - tuple with the element types given in the single list argument
|
||||
* `object({<attr_name>=<type_expr>, ...}` - object with the attributes and corresponding types given in the single map argument
|
||||
|
||||
For example:
|
||||
|
||||
* `list(string)`
|
||||
* `object({name=string,age=number})`
|
||||
* `map(object({name=string,age=number}))`
|
||||
|
||||
Note that the object constructor syntax is not fully-general for all possible
|
||||
object types because it requires the attribute names to be valid identifiers.
|
||||
In practice it is expected that any time an object type is being fixed for
|
||||
type checking it will be one that has identifiers as its attributes; object
|
||||
types with weird attributes generally show up only from arbitrary object
|
||||
constructors in configuration files, which are usually treated either as maps
|
||||
or as the dynamic pseudo-type.
|
||||
|
||||
## Type Constraints as Values
|
||||
|
||||
Along with defining a convention for writing down types using HCL expression
|
||||
constructs, this package also includes a mechanism for representing types as
|
||||
values that can be used as data within an HCL-based language.
|
||||
|
||||
`typeexpr.TypeConstraintType` is a
|
||||
[`cty` capsule type](https://github.com/zclconf/go-cty/blob/master/docs/types.md#capsule-types)
|
||||
that encapsulates `cty.Type` values. You can construct such a value directly
|
||||
using the `TypeConstraintVal` function:
|
||||
|
||||
```go
|
||||
tyVal := typeexpr.TypeConstraintVal(cty.String)
|
||||
|
||||
// We can unpack the type from a value using TypeConstraintFromVal
|
||||
ty := typeExpr.TypeConstraintFromVal(tyVal)
|
||||
```
|
||||
|
||||
However, the primary purpose of `typeexpr.TypeConstraintType` is to be
|
||||
specified as the type constraint for an argument, in which case it serves
|
||||
as a signal for HCL to treat the argument expression as a type constraint
|
||||
expression as defined above, rather than as a normal value expression.
|
||||
|
||||
"An argument" in the above in practice means the following two locations:
|
||||
|
||||
* As the type constraint for a parameter of a cty function that will be
|
||||
used in an `hcl.EvalContext`. In that case, function calls in the HCL
|
||||
native expression syntax will require the argument to be valid type constraint
|
||||
expression syntax and the function implementation will receive a
|
||||
`TypeConstraintType` value as the argument value for that parameter.
|
||||
|
||||
* As the type constraint for a `hcldec.AttrSpec` or `hcldec.BlockAttrsSpec`
|
||||
when decoding an HCL body using `hcldec`. In that case, the attributes
|
||||
with that type constraint will be required to be valid type constraint
|
||||
expression syntax and the result will be a `TypeConstraintType` value.
|
||||
|
||||
Note that the special handling of these arguments means that an argument
|
||||
marked in this way must use the type constraint syntax directly. It is not
|
||||
valid to pass in a value of `TypeConstraintType` that has been obtained
|
||||
dynamically via some other expression result.
|
||||
|
||||
`TypeConstraintType` is provided with the intent of using it internally within
|
||||
application code when incorporating type constraint expression syntax into
|
||||
an HCL-based language, not to be used for dynamic "programming with types". A
|
||||
calling application could support programming with types by defining its _own_
|
||||
capsule type, but that is not the purpose of `TypeConstraintType`.
|
||||
|
||||
## The "convert" `cty` Function
|
||||
|
||||
Building on the `TypeConstraintType` described in the previous section, this
|
||||
package also provides `typeexpr.ConvertFunc` which is a cty function that
|
||||
can be placed into a `cty.EvalContext` (conventionally named "convert") in
|
||||
order to provide a general type conversion function in an HCL-based language:
|
||||
|
||||
```hcl
|
||||
foo = convert("true", bool)
|
||||
```
|
||||
|
||||
The second parameter uses the mechanism described in the previous section to
|
||||
require its argument to be a type constraint expression rather than a value
|
||||
expression. In doing so, it allows converting with any type constraint that
|
||||
can be expressed in this package's type constraint syntax. In the above example,
|
||||
the `foo` argument would receive a boolean true, or `cty.True` in `cty` terms.
|
||||
|
||||
The target type constraint must always be provided statically using inline
|
||||
type constraint syntax. There is no way to _dynamically_ select a type
|
||||
constraint using this function.
|
||||
11
vendor/github.com/hashicorp/hcl/v2/ext/typeexpr/doc.go
generated
vendored
Normal file
11
vendor/github.com/hashicorp/hcl/v2/ext/typeexpr/doc.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Package typeexpr extends HCL with a convention for describing HCL types
|
||||
// within configuration files.
|
||||
//
|
||||
// The type syntax is processed statically from a hcl.Expression, so it cannot
|
||||
// use any of the usual language operators. This is similar to type expressions
|
||||
// in statically-typed programming languages.
|
||||
//
|
||||
// variable "example" {
|
||||
// type = list(string)
|
||||
// }
|
||||
package typeexpr
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user