diff --git a/go.mod b/go.mod index 8a71050..a0a2123 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/AGLEnergyPublic/tfectl go 1.18 require ( - github.com/hashicorp/go-tfe v1.30.0 + github.com/hashicorp/go-tfe v1.31.0 github.com/itchyny/gojq v0.12.13 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 @@ -15,7 +15,7 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.4 // indirect - github.com/hashicorp/go-slug v0.11.1 // indirect + github.com/hashicorp/go-slug v0.12.0 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect diff --git a/go.sum b/go.sum index f5071f8..992e7e8 100644 --- a/go.sum +++ b/go.sum @@ -3,7 +3,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= @@ -12,10 +12,10 @@ github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxC github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= -github.com/hashicorp/go-slug v0.11.1 h1:c6lLdQnlhUWbS5I7hw8SvfymoFuy6EmiFDedy6ir994= -github.com/hashicorp/go-slug v0.11.1/go.mod h1:Ib+IWBYfEfJGI1ZyXMGNbu2BU+aa3Dzu41RKLH301v4= -github.com/hashicorp/go-tfe v1.30.0 h1:vEieLxZ0Xly4+njypVwHH0RcUip7za1p6Pw52iqLOAY= -github.com/hashicorp/go-tfe v1.30.0/go.mod h1:z0182DGE/63AKUaWblUVBIrt+xdSmsuuXg5AoxGqDF4= +github.com/hashicorp/go-slug v0.12.0 h1:y1ArGp5RFF85uvD8nq5VZug/bup/kGN5Ft4xFOQ5GPM= +github.com/hashicorp/go-slug v0.12.0/go.mod h1:JZVtycnZZbiJ4oxpJ/zfhyfBD8XxT4f0uOSyjNLCqFY= +github.com/hashicorp/go-tfe v1.31.0 h1:R1CokrAVBHxrsvRw1vKes7RQxTRTWcula7gjQK7Jfsk= +github.com/hashicorp/go-tfe v1.31.0/go.mod h1:vcfy2u52JQ4sYLFi941qcQXQYfUq2RjEW466tZ+m97Y= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= diff --git a/vendor/github.com/hashicorp/go-slug/internal/ignorefiles/ignorerules.go b/vendor/github.com/hashicorp/go-slug/internal/ignorefiles/ignorerules.go new file mode 100644 index 0000000..25c2677 --- /dev/null +++ b/vendor/github.com/hashicorp/go-slug/internal/ignorefiles/ignorerules.go @@ -0,0 +1,100 @@ +// Package ignorefiles deals with the ".terraformignore" file format, which +// is a convention similar to ".gitignore" that specifies path patterns that +// match files Terraform should discard or ignore when interpreting a package +// fetched from a remote location. +package ignorefiles + +import ( + "fmt" + "io" + "os" + "path/filepath" +) + +// A Ruleset is the result of reading, parsing, and compiling a +// ".terraformignore" file. +type Ruleset struct { + rules []rule +} + +// ParseIgnoreFileContent takes a reader over the content of a .terraformignore +// file and returns the Ruleset described by that file, or an error if the +// file is invalid. +func ParseIgnoreFileContent(r io.Reader) (*Ruleset, error) { + rules, err := readRules(r) + if err != nil { + return nil, err + } + return &Ruleset{rules: rules}, nil +} + +// LoadPackageIgnoreRules implements reasonable default behavior for finding +// ignore rules for a particular package root directory: if .terraformignore is +// present then use it, or otherwise just return DefaultRuleset. +// +// This function will return an error only if an ignore file is present but +// unreadable, or if an ignore file is present but contains invalid syntax. +func LoadPackageIgnoreRules(packageDir string) (*Ruleset, error) { + file, err := os.Open(filepath.Join(packageDir, ".terraformignore")) + if err != nil { + if os.IsNotExist(err) { + return DefaultRuleset, nil + } + return nil, fmt.Errorf("cannot read .terraformignore: %s", err) + } + defer file.Close() + + ret, err := ParseIgnoreFileContent(file) + if err != nil { + // The parse errors already mention that they were parsing ignore rules, + // so don't need an additional prefix added. + return nil, err + } + return ret, nil +} + +// Excludes tests whether the given path matches the set of paths that are +// excluded by the rules in the ruleset. +// +// If any of the rules in the ruleset have invalid syntax then Excludes will +// return an error, but it will also still return a boolean result which +// considers all of the remaining valid rules, to support callers that want to +// just ignore invalid exclusions. Such callers can safely ignore the error +// result: +// +// exc, _ = ruleset.Excludes(path) +func (r *Ruleset) Excludes(path string) (bool, error) { + if r == nil { + return false, nil + } + + var retErr error + foundMatch := false + for _, rule := range r.rules { + match, err := rule.match(path) + if err != nil { + // We'll remember the first error we encounter, but continue + // matching anyway to support callers that want to ignore invalid + // lines and just match with whatever's left. + if retErr == nil { + retErr = fmt.Errorf("invalid ignore rule %q", rule.val) + } + } + if match { + foundMatch = !rule.excluded + } + } + return foundMatch, retErr +} + +// Includes is the inverse of [Ruleset.Excludes]. +func (r *Ruleset) Includes(path string) (bool, error) { + notRet, err := r.Excludes(path) + return !notRet, err +} + +var DefaultRuleset *Ruleset + +func init() { + DefaultRuleset = &Ruleset{rules: defaultExclusions} +} diff --git a/vendor/github.com/hashicorp/go-slug/internal/ignorefiles/terraformignore.go b/vendor/github.com/hashicorp/go-slug/internal/ignorefiles/terraformignore.go new file mode 100644 index 0000000..0eda3a0 --- /dev/null +++ b/vendor/github.com/hashicorp/go-slug/internal/ignorefiles/terraformignore.go @@ -0,0 +1,186 @@ +package ignorefiles + +import ( + "bufio" + "fmt" + "io" + "os" + "path/filepath" + "regexp" + "strings" + "text/scanner" +) + +func readRules(input io.Reader) ([]rule, error) { + rules := defaultExclusions + scanner := bufio.NewScanner(input) + scanner.Split(bufio.ScanLines) + + for scanner.Scan() { + pattern := scanner.Text() + // Ignore blank lines + if len(pattern) == 0 { + continue + } + // Trim spaces + pattern = strings.TrimSpace(pattern) + // Ignore comments + if pattern[0] == '#' { + continue + } + // New rule structure + rule := rule{} + // Exclusions + if pattern[0] == '!' { + rule.excluded = true + pattern = pattern[1:] + } + // If it is a directory, add ** so we catch descendants + if pattern[len(pattern)-1] == os.PathSeparator { + pattern = pattern + "**" + } + // If it starts with /, it is absolute + if pattern[0] == os.PathSeparator { + pattern = pattern[1:] + } else { + // Otherwise prepend **/ + pattern = "**" + string(os.PathSeparator) + pattern + } + rule.val = pattern + rule.dirs = strings.Split(pattern, string(os.PathSeparator)) + rules = append(rules, rule) + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("syntax error in .terraformignore: %w", err) + } + return rules, nil +} + +type rule struct { + val string // the value of the rule itself + excluded bool // ! is present, an exclusion rule + dirs []string // directories of the rule + regex *regexp.Regexp // regular expression to match for the rule +} + +func (r *rule) match(path string) (bool, error) { + if r.regex == nil { + if err := r.compile(); err != nil { + return false, filepath.ErrBadPattern + } + } + + b := r.regex.MatchString(path) + return b, nil +} + +func (r *rule) compile() error { + regStr := "^" + pattern := r.val + // Go through the pattern and convert it to a regexp. + // Use a scanner to support utf-8 chars. + var scan scanner.Scanner + scan.Init(strings.NewReader(pattern)) + + sl := string(os.PathSeparator) + escSL := sl + if sl == `\` { + escSL += `\` + } + + for scan.Peek() != scanner.EOF { + ch := scan.Next() + if ch == '*' { + if scan.Peek() == '*' { + // is some flavor of "**" + scan.Next() + + // Treat **/ as ** so eat the "/" + if string(scan.Peek()) == sl { + scan.Next() + } + + if scan.Peek() == scanner.EOF { + // is "**EOF" - to align with .gitignore just accept all + regStr += ".*" + } else { + // is "**" + // Note that this allows for any # of /'s (even 0) because + // the .* will eat everything, even /'s + regStr += "(.*" + escSL + ")?" + } + } else { + // is "*" so map it to anything but "/" + regStr += "[^" + escSL + "]*" + } + } else if ch == '?' { + // "?" is any char except "/" + regStr += "[^" + escSL + "]" + } else if ch == '.' || ch == '$' { + // Escape some regexp special chars that have no meaning + // in golang's filepath.Match + regStr += `\` + string(ch) + } else if ch == '\\' { + // escape next char. Note that a trailing \ in the pattern + // will be left alone (but need to escape it) + if sl == `\` { + // On windows map "\" to "\\", meaning an escaped backslash, + // and then just continue because filepath.Match on + // Windows doesn't allow escaping at all + regStr += escSL + continue + } + if scan.Peek() != scanner.EOF { + regStr += `\` + string(scan.Next()) + } else { + regStr += `\` + } + } else { + regStr += string(ch) + } + } + + regStr += "$" + re, err := regexp.Compile(regStr) + if err != nil { + return err + } + + r.regex = re + return nil +} + +/* + Default rules as they would appear in .terraformignore: + .git/ + .terraform/ + !.terraform/modules/ +*/ + +var defaultExclusions = []rule{ + { + val: strings.Join([]string{"**", ".git", "**"}, string(os.PathSeparator)), + excluded: false, + }, + { + val: strings.Join([]string{"**", ".terraform", "**"}, string(os.PathSeparator)), + excluded: false, + }, + { + val: strings.Join([]string{"**", ".terraform", "modules", "**"}, string(os.PathSeparator)), + excluded: true, + }, +} + +func init() { + // We'll precompile all of the default rules at initialization, so we + // don't need to recompile them every time we encounter a package that + // doesn't have any rules (the common case). + for _, r := range defaultExclusions { + err := r.compile() + if err != nil { + panic(fmt.Sprintf("invalid default rule %q: %s", r.val, err)) + } + } +} diff --git a/vendor/github.com/hashicorp/go-slug/slug.go b/vendor/github.com/hashicorp/go-slug/slug.go index fd28d27..09d8fbe 100644 --- a/vendor/github.com/hashicorp/go-slug/slug.go +++ b/vendor/github.com/hashicorp/go-slug/slug.go @@ -8,6 +8,8 @@ import ( "os" "path/filepath" "strings" + + "github.com/hashicorp/go-slug/internal/ignorefiles" ) // Meta provides detailed information about a slug. @@ -151,7 +153,7 @@ func (p *Packer) Pack(src string, w io.Writer) (*Meta, error) { // Load the ignore rule configuration, which will use // defaults if no .terraformignore is configured - var ignoreRules []rule + var ignoreRules *ignorefiles.Ruleset if p.applyTerraformIgnore { ignoreRules = parseIgnoreFile(src) } @@ -175,7 +177,7 @@ func (p *Packer) Pack(src string, w io.Writer) (*Meta, error) { return meta, nil } -func (p *Packer) packWalkFn(root, src, dst string, tarW *tar.Writer, meta *Meta, ignoreRules []rule) filepath.WalkFunc { +func (p *Packer) packWalkFn(root, src, dst string, tarW *tar.Writer, meta *Meta, ignoreRules *ignorefiles.Ruleset) filepath.WalkFunc { return func(path string, info os.FileInfo, err error) error { if err != nil { return err @@ -190,14 +192,14 @@ func (p *Packer) packWalkFn(root, src, dst string, tarW *tar.Writer, meta *Meta, return nil } - if m := matchIgnoreRule(subpath, ignoreRules); m { + if m := matchIgnoreRules(subpath, ignoreRules); m { return nil } // Catch directories so we don't end up with empty directories, // the files are ignored correctly if info.IsDir() { - if m := matchIgnoreRule(subpath+string(os.PathSeparator), ignoreRules); m { + if m := matchIgnoreRules(subpath+string(os.PathSeparator), ignoreRules); m { return nil } } diff --git a/vendor/github.com/hashicorp/go-slug/terraformignore.go b/vendor/github.com/hashicorp/go-slug/terraformignore.go index 6803313..0863167 100644 --- a/vendor/github.com/hashicorp/go-slug/terraformignore.go +++ b/vendor/github.com/hashicorp/go-slug/terraformignore.go @@ -1,17 +1,14 @@ package slug import ( - "bufio" "fmt" - "io" "os" "path/filepath" - "regexp" - "strings" - "text/scanner" + + "github.com/hashicorp/go-slug/internal/ignorefiles" ) -func parseIgnoreFile(rootPath string) []rule { +func parseIgnoreFile(rootPath string) *ignorefiles.Ruleset { // Look for .terraformignore at our root path/src file, err := os.Open(filepath.Join(rootPath, ".terraformignore")) defer file.Close() @@ -22,209 +19,22 @@ func parseIgnoreFile(rootPath string) []rule { if !os.IsNotExist(err) { fmt.Fprintf(os.Stderr, "Error reading .terraformignore, default exclusions will apply: %v \n", err) } - return defaultExclusions - } - return readRules(file) -} - -func readRules(input io.Reader) []rule { - rules := defaultExclusions - scanner := bufio.NewScanner(input) - scanner.Split(bufio.ScanLines) - - for scanner.Scan() { - pattern := scanner.Text() - // Ignore blank lines - if len(pattern) == 0 { - continue - } - // Trim spaces - pattern = strings.TrimSpace(pattern) - // Ignore comments - if pattern[0] == '#' { - continue - } - // New rule structure - rule := rule{} - // Exclusions - if pattern[0] == '!' { - rule.excluded = true - pattern = pattern[1:] - } - // If it is a directory, add ** so we catch descendants - if pattern[len(pattern)-1] == os.PathSeparator { - pattern = pattern + "**" - } - // If it starts with /, it is absolute - if pattern[0] == os.PathSeparator { - pattern = pattern[1:] - } else { - // Otherwise prepend **/ - pattern = "**" + string(os.PathSeparator) + pattern - } - rule.val = pattern - rule.dirs = strings.Split(pattern, string(os.PathSeparator)) - rules = append(rules, rule) + return ignorefiles.DefaultRuleset } - if err := scanner.Err(); err != nil { - fmt.Fprintf(os.Stderr, "Error reading .terraformignore, default exclusions will apply: %v \n", err) - return defaultExclusions - } - return rules -} - -func matchIgnoreRule(path string, rules []rule) bool { - matched := false - path = filepath.FromSlash(path) - for _, rule := range rules { - match, _ := rule.match(path) - - if match { - matched = !rule.excluded - } - } - - if matched { - debug(true, path, "Skipping excluded path:", path) - } - - return matched -} - -type rule struct { - val string // the value of the rule itself - excluded bool // ! is present, an exclusion rule - dirs []string // directories of the rule - regex *regexp.Regexp // regular expression to match for the rule -} - -func (r *rule) match(path string) (bool, error) { - if r.regex == nil { - if err := r.compile(); err != nil { - return false, filepath.ErrBadPattern - } - } - - b := r.regex.MatchString(path) - debug(false, path, path, r.regex, b) - return b, nil -} - -func (r *rule) compile() error { - regStr := "^" - pattern := r.val - // Go through the pattern and convert it to a regexp. - // Use a scanner to support utf-8 chars. - var scan scanner.Scanner - scan.Init(strings.NewReader(pattern)) - - sl := string(os.PathSeparator) - escSL := sl - if sl == `\` { - escSL += `\` - } - - for scan.Peek() != scanner.EOF { - ch := scan.Next() - if ch == '*' { - if scan.Peek() == '*' { - // is some flavor of "**" - scan.Next() - - // Treat **/ as ** so eat the "/" - if string(scan.Peek()) == sl { - scan.Next() - } - - if scan.Peek() == scanner.EOF { - // is "**EOF" - to align with .gitignore just accept all - regStr += ".*" - } else { - // is "**" - // Note that this allows for any # of /'s (even 0) because - // the .* will eat everything, even /'s - regStr += "(.*" + escSL + ")?" - } - } else { - // is "*" so map it to anything but "/" - regStr += "[^" + escSL + "]*" - } - } else if ch == '?' { - // "?" is any char except "/" - regStr += "[^" + escSL + "]" - } else if ch == '.' || ch == '$' { - // Escape some regexp special chars that have no meaning - // in golang's filepath.Match - regStr += `\` + string(ch) - } else if ch == '\\' { - // escape next char. Note that a trailing \ in the pattern - // will be left alone (but need to escape it) - if sl == `\` { - // On windows map "\" to "\\", meaning an escaped backslash, - // and then just continue because filepath.Match on - // Windows doesn't allow escaping at all - regStr += escSL - continue - } - if scan.Peek() != scanner.EOF { - regStr += `\` + string(scan.Next()) - } else { - regStr += `\` - } - } else { - regStr += string(ch) - } - } - - regStr += "$" - re, err := regexp.Compile(regStr) + ret, err := ignorefiles.ParseIgnoreFileContent(file) if err != nil { - return err + fmt.Fprintf(os.Stderr, "Error reading .terraformignore, default exclusions will apply: %v \n", err) + return ignorefiles.DefaultRuleset } - r.regex = re - return nil + return ret } -/* - Default rules as they would appear in .terraformignore: - .git/ - .terraform/ - !.terraform/modules/ - terraform.tfstate -*/ - -var defaultExclusions = []rule{ - { - val: filepath.Join("**", ".git", "**"), - excluded: false, - }, - { - val: filepath.Join("**", ".terraform", "**"), - excluded: false, - }, - { - val: filepath.Join("**", ".terraform", "modules", "**"), - excluded: true, - }, - { - val: filepath.Join("**", "terraform.tfstate"), - excluded: false, - }, -} - -func debug(printAll bool, path string, message ...interface{}) { - logLevel := os.Getenv("TF_IGNORE") == "trace" - debugPath := os.Getenv("TF_IGNORE_DEBUG") - isPath := debugPath != "" - if isPath { - isPath = strings.Contains(path, debugPath) - } - - if logLevel { - if printAll || isPath { - fmt.Println(message...) - } - } +func matchIgnoreRules(path string, ruleset *ignorefiles.Ruleset) bool { + // Ruleset.Excludes explicitly allows ignoring its error, in which + // case we are ignoring any individual invalid rules in the set + // but still taking all of the others into account. + ret, _ := ruleset.Excludes(path) + return ret } diff --git a/vendor/github.com/hashicorp/go-tfe/CHANGELOG.md b/vendor/github.com/hashicorp/go-tfe/CHANGELOG.md index 5aa27c6..cf52672 100644 --- a/vendor/github.com/hashicorp/go-tfe/CHANGELOG.md +++ b/vendor/github.com/hashicorp/go-tfe/CHANGELOG.md @@ -1,5 +1,11 @@ # UNRELEASED +# v1.31.0 + +## Enhancements +* Added BETA support for including `projects` relationship and `projects-count` attribute to policy_set on create by @hs26gill [#737](https://github.com/hashicorp/go-tfe/pull/737) +* Added BETA method `AddProjects` and `RemoveProjects` for attaching/detaching policy set to projects by @Netra2104 [#735](https://github.com/hashicorp/go-tfe/pull/735) + # v1.30.0 ## Enhancements diff --git a/vendor/github.com/hashicorp/go-tfe/errors.go b/vendor/github.com/hashicorp/go-tfe/errors.go index cc8c07a..bca97b3 100644 --- a/vendor/github.com/hashicorp/go-tfe/errors.go +++ b/vendor/github.com/hashicorp/go-tfe/errors.go @@ -284,6 +284,8 @@ var ( ErrWorkspaceMinLimit = errors.New("must provide at least one workspace") + ErrProjectMinLimit = errors.New("must provide at least one project") + ErrRequiredPlan = errors.New("plan is required") ErrRequiredPolicies = errors.New("policies is required") diff --git a/vendor/github.com/hashicorp/go-tfe/policy_set.go b/vendor/github.com/hashicorp/go-tfe/policy_set.go index d805829..8c1bdfe 100644 --- a/vendor/github.com/hashicorp/go-tfe/policy_set.go +++ b/vendor/github.com/hashicorp/go-tfe/policy_set.go @@ -56,6 +56,12 @@ type PolicySets interface { // Remove workspaces from a policy set. RemoveWorkspaces(ctx context.Context, policySetID string, options PolicySetRemoveWorkspacesOptions) error + // Add projects to a policy set. + AddProjects(ctx context.Context, policySetID string, options PolicySetAddProjectsOptions) error + + // Remove projects from a policy set. + RemoveProjects(ctx context.Context, policySetID string, options PolicySetRemoveProjectsOptions) error + // Delete a policy set by its ID. Delete(ctx context.Context, policyID string) error } @@ -73,18 +79,20 @@ type PolicySetList struct { // PolicySet represents a Terraform Enterprise policy set. type PolicySet struct { - ID string `jsonapi:"primary,policy-sets"` - Name string `jsonapi:"attr,name"` - Description string `jsonapi:"attr,description"` - Kind PolicyKind `jsonapi:"attr,kind"` - Overridable *bool `jsonapi:"attr,overridable"` - Global bool `jsonapi:"attr,global"` - PoliciesPath string `jsonapi:"attr,policies-path"` - PolicyCount int `jsonapi:"attr,policy-count"` - VCSRepo *VCSRepo `jsonapi:"attr,vcs-repo"` - WorkspaceCount int `jsonapi:"attr,workspace-count"` - CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"` - UpdatedAt time.Time `jsonapi:"attr,updated-at,iso8601"` + ID string `jsonapi:"primary,policy-sets"` + Name string `jsonapi:"attr,name"` + Description string `jsonapi:"attr,description"` + Kind PolicyKind `jsonapi:"attr,kind"` + Overridable *bool `jsonapi:"attr,overridable"` + Global bool `jsonapi:"attr,global"` + PoliciesPath string `jsonapi:"attr,policies-path"` + // **Note: This field is still in BETA and subject to change.** + PolicyCount int `jsonapi:"attr,policy-count"` + VCSRepo *VCSRepo `jsonapi:"attr,vcs-repo"` + WorkspaceCount int `jsonapi:"attr,workspace-count"` + ProjectCount int `jsonapi:"attr,project-count"` + CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"` + UpdatedAt time.Time `jsonapi:"attr,updated-at,iso8601"` // Relations // The organization to which the policy set belongs to. @@ -99,6 +107,9 @@ type PolicySet struct { NewestVersion *PolicySetVersion `jsonapi:"relation,newest-version"` // The most recent successful policy set version. CurrentVersion *PolicySetVersion `jsonapi:"relation,current-version"` + // **Note: This field is still in BETA and subject to change.** + // The projects to which the policy set applies. + Projects []*Project `jsonapi:"relation,projects"` } // PolicySetIncludeOpt represents the available options for include query params. @@ -110,6 +121,8 @@ const ( PolicySetWorkspaces PolicySetIncludeOpt = "workspaces" PolicySetCurrentVersion PolicySetIncludeOpt = "current_version" PolicySetNewestVersion PolicySetIncludeOpt = "newest_version" + // **Note: This field is still in BETA and subject to change.** + PolicySetProjects PolicySetIncludeOpt = "projects" ) // PolicySetListOptions represents the options for listing policy sets. @@ -179,6 +192,10 @@ type PolicySetCreateOptions struct { // Optional: The initial list of workspaces for which the policy set should be enforced. Workspaces []*Workspace `jsonapi:"relation,workspaces,omitempty"` + + // **Note: This field is still in BETA and subject to change.** + // Optional: The initial list of projects for which the policy set should be enforced. + Projects []*Project `jsonapi:"relation,projects,omitempty"` } // PolicySetUpdateOptions represents the options for updating a policy set. @@ -244,6 +261,20 @@ type PolicySetRemoveWorkspacesOptions struct { Workspaces []*Workspace } +// PolicySetAddProjectsOptions represents the options for adding projects +// to a policy set. +type PolicySetAddProjectsOptions struct { + // The projects to add to the policy set. + Projects []*Project +} + +// PolicySetRemoveProjectsOptions represents the options for removing +// projects from a policy set. +type PolicySetRemoveProjectsOptions struct { + // The projects to remove from the policy set. + Projects []*Project +} + // List all the policies for a given organization. func (s *policySets) List(ctx context.Context, organization string, options *PolicySetListOptions) (*PolicySetList, error) { if !validStringID(&organization) { @@ -414,6 +445,42 @@ func (s *policySets) RemoveWorkspaces(ctx context.Context, policySetID string, o return req.Do(ctx, nil) } +// AddProjects adds projects to a given policy set. +func (s *policySets) AddProjects(ctx context.Context, policySetID string, options PolicySetAddProjectsOptions) error { + if !validStringID(&policySetID) { + return ErrInvalidPolicySetID + } + if err := options.valid(); err != nil { + return err + } + + u := fmt.Sprintf("policy-sets/%s/relationships/projects", url.QueryEscape(policySetID)) + req, err := s.client.NewRequest("POST", u, options.Projects) + if err != nil { + return err + } + + return req.Do(ctx, nil) +} + +// RemoveProjects removes projects from a policy set. +func (s *policySets) RemoveProjects(ctx context.Context, policySetID string, options PolicySetRemoveProjectsOptions) error { + if !validStringID(&policySetID) { + return ErrInvalidPolicySetID + } + if err := options.valid(); err != nil { + return err + } + + u := fmt.Sprintf("policy-sets/%s/relationships/projects", url.QueryEscape(policySetID)) + req, err := s.client.NewRequest("DELETE", u, options.Projects) + if err != nil { + return err + } + + return req.Do(ctx, nil) +} + // Delete a policy set by its ID. func (s *policySets) Delete(ctx context.Context, policySetID string) error { if !validStringID(&policySetID) { @@ -449,6 +516,16 @@ func (o PolicySetRemoveWorkspacesOptions) valid() error { return nil } +func (o PolicySetRemoveProjectsOptions) valid() error { + if o.Projects == nil { + return ErrRequiredProject + } + if len(o.Projects) == 0 { + return ErrProjectMinLimit + } + return nil +} + func (o PolicySetUpdateOptions) valid() error { if o.Name != nil && !validStringID(o.Name) { return ErrInvalidName @@ -486,6 +563,16 @@ func (o PolicySetAddWorkspacesOptions) valid() error { return nil } +func (o PolicySetAddProjectsOptions) valid() error { + if o.Projects == nil { + return ErrRequiredProject + } + if len(o.Projects) == 0 { + return ErrProjectMinLimit + } + return nil +} + func (o *PolicySetReadOptions) valid() error { if o == nil { return nil // nothing to validate @@ -501,7 +588,7 @@ func (o *PolicySetReadOptions) valid() error { func validatePolicySetIncludeParams(params []PolicySetIncludeOpt) error { for _, p := range params { switch p { - case PolicySetPolicies, PolicySetWorkspaces, PolicySetCurrentVersion, PolicySetNewestVersion: + case PolicySetPolicies, PolicySetWorkspaces, PolicySetCurrentVersion, PolicySetNewestVersion, PolicySetProjects: // do nothing default: return ErrInvalidIncludeValue diff --git a/vendor/github.com/hashicorp/go-tfe/state_version.go b/vendor/github.com/hashicorp/go-tfe/state_version.go index 9b399c9..c1a070c 100644 --- a/vendor/github.com/hashicorp/go-tfe/state_version.go +++ b/vendor/github.com/hashicorp/go-tfe/state_version.go @@ -223,8 +223,8 @@ type ProviderData struct { type StateVersionResources struct { Name string `jsonapi:"attr,name"` - Count string `jsonapi:"attr,count"` - Type int `jsonapi:"attr,type"` + Count int `jsonapi:"attr,count"` + Type string `jsonapi:"attr,type"` Module string `jsonapi:"attr,module"` Provider string `jsonapi:"attr,provider"` } diff --git a/vendor/modules.txt b/vendor/modules.txt index c960a91..ee611b1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -10,10 +10,11 @@ github.com/hashicorp/go-cleanhttp # github.com/hashicorp/go-retryablehttp v0.7.4 ## explicit; go 1.13 github.com/hashicorp/go-retryablehttp -# github.com/hashicorp/go-slug v0.11.1 -## explicit; go 1.15 +# github.com/hashicorp/go-slug v0.12.0 +## explicit; go 1.20 github.com/hashicorp/go-slug -# github.com/hashicorp/go-tfe v1.30.0 +github.com/hashicorp/go-slug/internal/ignorefiles +# github.com/hashicorp/go-tfe v1.31.0 ## explicit; go 1.19 github.com/hashicorp/go-tfe # github.com/hashicorp/go-version v1.6.0