Skip to content

Commit

Permalink
TFC Telemetry
Browse files Browse the repository at this point in the history
  • Loading branch information
jpogran committed Mar 7, 2023
1 parent 36401eb commit 25c9e61
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 2 deletions.
2 changes: 1 addition & 1 deletion backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func (*UnknownBackendData) Equals(d BackendData) bool {
}

type Remote struct {
Hostname string
Hostname string
}

func (r *Remote) Copy() BackendData {
Expand Down
40 changes: 40 additions & 0 deletions backend/cloud.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package backend

type CloudData interface {
Copy() CloudData
Equals(CloudData) bool
}

type UnknownCloudData struct{}

func (*UnknownCloudData) Copy() CloudData {
return &UnknownCloudData{}
}

func (*UnknownCloudData) Equals(d CloudData) bool {
_, ok := d.(*UnknownCloudData)
return ok
}

type Cloud struct {
Organization string
Hostname string
}

func (r *Cloud) Copy() CloudData {
return &Cloud{
Organization: r.Organization,
}
}

func (r *Cloud) Equals(d CloudData) bool {
data, ok := d.(*Cloud)
if !ok {
return false
}

return data.Organization == r.Organization
}
21 changes: 21 additions & 0 deletions earlydecoder/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,24 @@ func decodeBackendsBlock(block *hcl.Block) (backend.BackendData, hcl.Diagnostics

return &backend.UnknownBackendData{}, diags
}

func decodeCloudBlock(block *hcl.Block) (backend.CloudData, hcl.Diagnostics) {
attrs, diags := block.Body.JustAttributes()

if attr, ok := attrs["hostname"]; ok {
val, vDiags := attr.Expr.Value(nil)
diags = append(diags, vDiags...)
if val.IsWhollyKnown() && val.Type() == cty.String {
return &backend.Cloud{
Hostname: val.AsString(),
}, nil
}
}

// https://developer.hashicorp.com/terraform/language/settings/terraform-cloud#usage-example
// Required for Terraform Enterprise;
// Defaults to app.terraform.io for Terraform Cloud
return &backend.Cloud{
Hostname: "app.terraform.io",
}, nil
}
8 changes: 8 additions & 0 deletions earlydecoder/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ func LoadModule(path string, files map[string]*hcl.File) (*module.Meta, hcl.Diag
coreRequirements = append(coreRequirements, c...)
}

var tfCloud *module.CloudBackend
if mod.CloudBackend != nil {
tfCloud = &module.CloudBackend{
Data: mod.CloudBackend,
}
}

var backend *module.Backend
if len(mod.Backends) == 1 {
for bType, data := range mod.Backends {
Expand Down Expand Up @@ -208,6 +215,7 @@ func LoadModule(path string, files map[string]*hcl.File) (*module.Meta, hcl.Diag
return &module.Meta{
Path: path,
Backend: backend,
CloudBackend: tfCloud,
ProviderReferences: refs,
ProviderRequirements: providerRequirements,
CoreRequirements: coreRequirements,
Expand Down
82 changes: 82 additions & 0 deletions earlydecoder/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,88 @@ terraform {
runTestCases(testCases, t, path)
}

func TestLoadModule_cloud(t *testing.T) {
path := t.TempDir()

testCases := []testCase{
{
"cloud backend",
`
terraform {
cloud {
hostname = "app.terraform.io"
organization = "example_corp"
workspaces {
tags = ["app"]
}
}
}`,
&module.Meta{
Path: path,
// Backend: &module.Backend{
// Type: "cloud",
// Data: &backend.Remote{
// Hostname: "app.terraform.io",
// },
// },
Backend: nil,
CloudBackend: &module.CloudBackend{
Type: "cloud",
Data: &backend.Cloud{
Hostname: "app.terraform.io",
},
},
ProviderReferences: map[module.ProviderRef]tfaddr.Provider{},
ProviderRequirements: map[tfaddr.Provider]version.Constraints{},
Variables: map[string]module.Variable{},
Outputs: map[string]module.Output{},
Filenames: []string{"test.tf"},
ModuleCalls: map[string]module.DeclaredModuleCall{},
},
nil,
},
{
"cloud backend empy hostname",
`
terraform {
cloud {
organization = "example_corp"
workspaces {
tags = ["app"]
}
}
}`,
&module.Meta{
Path: path,
// Backend: &module.Backend{
// Type: "cloud",
// Data: &backend.Remote{
// Hostname: "app.terraform.io",
// },
// },
Backend: nil,
CloudBackend: &module.CloudBackend{
Type: "cloud",
Data: &backend.Cloud{
Hostname: "app.terraform.io",
},
},
ProviderReferences: map[module.ProviderRef]tfaddr.Provider{},
ProviderRequirements: map[tfaddr.Provider]version.Constraints{},
Variables: map[string]module.Variable{},
Outputs: map[string]module.Output{},
Filenames: []string{"test.tf"},
ModuleCalls: map[string]module.DeclaredModuleCall{},
},
nil,
},
}

runTestCases(testCases, t, path)
}

func TestLoadModule_Modules(t *testing.T) {
path := t.TempDir()

Expand Down
19 changes: 18 additions & 1 deletion earlydecoder/load_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
type decodedModule struct {
RequiredCore []string
Backends map[string]backend.BackendData
CloudBackend backend.CloudData
ProviderRequirements map[string]*providerRequirement
ProviderConfigs map[string]*providerConfig
Resources map[string]*resource
Expand Down Expand Up @@ -78,6 +79,23 @@ func loadModuleFromFile(file *hcl.File, mod *decodedModule) hcl.Diagnostics {

for _, innerBlock := range content.Blocks {
switch innerBlock.Type {
case "cloud":
bType := innerBlock.Type

data, bDiags := decodeCloudBlock(innerBlock)
diags = append(diags, bDiags...)

if _, exists := mod.Backends[bType]; exists {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Multiple cloud definitions",
Detail: fmt.Sprintf("Found multiple cloud definitions for %q. Only one is allowed.", bType),
Subject: &innerBlock.DefRange,
})
continue
}

mod.CloudBackend = data
case "backend":
bType := innerBlock.Labels[0]

Expand All @@ -95,7 +113,6 @@ func loadModuleFromFile(file *hcl.File, mod *decodedModule) hcl.Diagnostics {
}

mod.Backends[bType] = data

case "required_providers":
reqs, reqsDiags := decodeRequiredProvidersBlock(innerBlock)
diags = append(diags, reqsDiags...)
Expand Down
3 changes: 3 additions & 0 deletions earlydecoder/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ var terraformBlockSchema = &hcl.BodySchema{
{
Type: "required_providers",
},
{
Type: "cloud",
},
{
Type: "backend",
LabelNames: []string{"type"},
Expand Down
22 changes: 22 additions & 0 deletions module/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Meta struct {
Filenames []string

Backend *Backend
CloudBackend *CloudBackend
ProviderReferences map[ProviderRef]tfaddr.Provider
ProviderRequirements ProviderRequirements
CoreRequirements version.Constraints
Expand Down Expand Up @@ -42,6 +43,27 @@ func (pr ProviderRequirements) Equals(reqs ProviderRequirements) bool {
return true
}

type CloudBackend struct {
Type string
Data backend.CloudData
}

func (be *CloudBackend) Equals(b *CloudBackend) bool {
if be == nil && b == nil {
return true
}

if be == nil || b == nil {
return false
}

if be.Type != b.Type {
return false
}

return be.Data.Equals(b.Data)
}

type Backend struct {
Type string
Data backend.BackendData
Expand Down

0 comments on commit 25c9e61

Please sign in to comment.