config: Data source loading
This allows the config loader to read "data" blocks from the config and turn them into DataSource objects. This just reads the data from the config file. It doesn't validate the data nor do anything useful with it.
This commit is contained in:
parent
fc4fa10981
commit
860140074f
|
@ -20,6 +20,7 @@ type hclConfigurable struct {
|
|||
func (t *hclConfigurable) Config() (*Config, error) {
|
||||
validKeys := map[string]struct{}{
|
||||
"atlas": struct{}{},
|
||||
"data": struct{}{},
|
||||
"module": struct{}{},
|
||||
"output": struct{}{},
|
||||
"provider": struct{}{},
|
||||
|
@ -113,12 +114,27 @@ func (t *hclConfigurable) Config() (*Config, error) {
|
|||
}
|
||||
|
||||
// Build the resources
|
||||
if resources := list.Filter("resource"); len(resources.Items) > 0 {
|
||||
{
|
||||
var err error
|
||||
config.Resources, err = loadResourcesHcl(resources)
|
||||
managedResourceConfigs := list.Filter("resource")
|
||||
dataResourceConfigs := list.Filter("data")
|
||||
|
||||
config.Resources = make(
|
||||
[]*Resource, 0,
|
||||
len(managedResourceConfigs.Items)+len(dataResourceConfigs.Items),
|
||||
)
|
||||
|
||||
managedResources, err := loadManagedResourcesHcl(managedResourceConfigs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dataResources, err := loadDataResourcesHcl(dataResourceConfigs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config.Resources = append(config.Resources, dataResources...)
|
||||
config.Resources = append(config.Resources, managedResources...)
|
||||
}
|
||||
|
||||
// Build the outputs
|
||||
|
@ -395,12 +411,130 @@ func loadProvidersHcl(list *ast.ObjectList) ([]*ProviderConfig, error) {
|
|||
}
|
||||
|
||||
// Given a handle to a HCL object, this recurses into the structure
|
||||
// and pulls out a list of resources.
|
||||
// and pulls out a list of data sources.
|
||||
//
|
||||
// The resulting data sources may not be unique, but each one
|
||||
// represents exactly one data definition in the HCL configuration.
|
||||
// We leave it up to another pass to merge them together.
|
||||
func loadDataResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
|
||||
list = list.Children()
|
||||
if len(list.Items) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Where all the results will go
|
||||
var result []*Resource
|
||||
|
||||
// Now go over all the types and their children in order to get
|
||||
// all of the actual resources.
|
||||
for _, item := range list.Items {
|
||||
if len(item.Keys) != 2 {
|
||||
return nil, fmt.Errorf(
|
||||
"position %s: 'data' must be followed by exactly two strings: a type and a name",
|
||||
item.Pos())
|
||||
}
|
||||
|
||||
t := item.Keys[0].Token.Value().(string)
|
||||
k := item.Keys[1].Token.Value().(string)
|
||||
|
||||
var listVal *ast.ObjectList
|
||||
if ot, ok := item.Val.(*ast.ObjectType); ok {
|
||||
listVal = ot.List
|
||||
} else {
|
||||
return nil, fmt.Errorf("data sources %s[%s]: should be an object", t, k)
|
||||
}
|
||||
|
||||
var config map[string]interface{}
|
||||
if err := hcl.DecodeObject(&config, item.Val); err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Error reading config for %s[%s]: %s",
|
||||
t,
|
||||
k,
|
||||
err)
|
||||
}
|
||||
|
||||
// Remove the fields we handle specially
|
||||
delete(config, "depends_on")
|
||||
delete(config, "provider")
|
||||
|
||||
rawConfig, err := NewRawConfig(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Error reading config for %s[%s]: %s",
|
||||
t,
|
||||
k,
|
||||
err)
|
||||
}
|
||||
|
||||
// If we have a count, then figure it out
|
||||
var count string = "1"
|
||||
if o := listVal.Filter("count"); len(o.Items) > 0 {
|
||||
err = hcl.DecodeObject(&count, o.Items[0].Val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Error parsing count for %s[%s]: %s",
|
||||
t,
|
||||
k,
|
||||
err)
|
||||
}
|
||||
}
|
||||
countConfig, err := NewRawConfig(map[string]interface{}{
|
||||
"count": count,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
countConfig.Key = "count"
|
||||
|
||||
// If we have depends fields, then add those in
|
||||
var dependsOn []string
|
||||
if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
|
||||
err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Error reading depends_on for %s[%s]: %s",
|
||||
t,
|
||||
k,
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a provider, then parse it out
|
||||
var provider string
|
||||
if o := listVal.Filter("provider"); len(o.Items) > 0 {
|
||||
err := hcl.DecodeObject(&provider, o.Items[0].Val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Error reading provider for %s[%s]: %s",
|
||||
t,
|
||||
k,
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, &Resource{
|
||||
Mode: DataResourceMode,
|
||||
Name: k,
|
||||
Type: t,
|
||||
RawCount: countConfig,
|
||||
RawConfig: rawConfig,
|
||||
Provider: provider,
|
||||
Provisioners: []*Provisioner{},
|
||||
DependsOn: dependsOn,
|
||||
Lifecycle: ResourceLifecycle{},
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Given a handle to a HCL object, this recurses into the structure
|
||||
// and pulls out a list of managed resources.
|
||||
//
|
||||
// The resulting resources may not be unique, but each resource
|
||||
// represents exactly one resource definition in the HCL configuration.
|
||||
// represents exactly one "resource" block in the HCL configuration.
|
||||
// We leave it up to another pass to merge them together.
|
||||
func loadResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
|
||||
func loadManagedResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
|
||||
list = list.Children()
|
||||
if len(list.Items) == 0 {
|
||||
return nil, nil
|
||||
|
|
|
@ -65,6 +65,17 @@ func TestLoadFile_resourceArityMistake(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestLoadFile_dataSourceArityMistake(t *testing.T) {
|
||||
_, err := LoadFile(filepath.Join(fixtureDir, "data-source-arity-mistake.tf"))
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
expected := "Error loading test-fixtures/data-source-arity-mistake.tf: position 2:6: 'data' must be followed by exactly two strings: a type and a name"
|
||||
if err.Error() != expected {
|
||||
t.Fatalf("expected:\n%s\ngot:\n%s", expected, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFileWindowsLineEndings(t *testing.T) {
|
||||
testFile := filepath.Join(fixtureDir, "windows-line-endings.tf")
|
||||
|
||||
|
@ -823,6 +834,11 @@ aws_instance.web (x1)
|
|||
resource: aws_security_group.firewall.foo
|
||||
user: var.foo
|
||||
aws_security_group.firewall (x5)
|
||||
data.do.depends (x1)
|
||||
dependsOn
|
||||
data.do.simple
|
||||
data.do.simple (x1)
|
||||
foo
|
||||
`
|
||||
|
||||
const basicVariablesStr = `
|
||||
|
@ -866,6 +882,11 @@ aws_instance.web (x1)
|
|||
resource: aws_security_group.firewall.foo
|
||||
user: var.foo
|
||||
aws_security_group.firewall (x5)
|
||||
data.do.depends (x1)
|
||||
dependsOn
|
||||
data.do.simple
|
||||
data.do.simple (x1)
|
||||
foo
|
||||
`
|
||||
|
||||
const dirBasicVariablesStr = `
|
||||
|
@ -903,6 +924,12 @@ aws_instance.web (x1)
|
|||
resource: aws_security_group.firewall.foo
|
||||
user: var.foo
|
||||
aws_security_group.firewall (x5)
|
||||
data.do.depends (x1)
|
||||
hello
|
||||
dependsOn
|
||||
data.do.simple
|
||||
data.do.simple (x1)
|
||||
foo
|
||||
`
|
||||
|
||||
const dirOverrideVariablesStr = `
|
||||
|
|
|
@ -24,6 +24,14 @@ provider "do" {
|
|||
api_key = "${var.foo}"
|
||||
}
|
||||
|
||||
data "do" "simple" {
|
||||
foo = "baz"
|
||||
}
|
||||
|
||||
data "do" "depends" {
|
||||
depends_on = ["data.do.simple"]
|
||||
}
|
||||
|
||||
resource "aws_security_group" "firewall" {
|
||||
count = 5
|
||||
}
|
||||
|
|
|
@ -26,6 +26,17 @@
|
|||
}
|
||||
},
|
||||
|
||||
"data": {
|
||||
"do": {
|
||||
"simple": {
|
||||
"foo": "baz"
|
||||
},
|
||||
"depends": {
|
||||
"depends_on": ["data.do.simple"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"resource": {
|
||||
"aws_instance": {
|
||||
"db": {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# I forgot the data source name!
|
||||
data "null" {
|
||||
}
|
|
@ -8,6 +8,10 @@ provider "aws" {
|
|||
secret_key = "bar"
|
||||
}
|
||||
|
||||
data "do" "simple" {
|
||||
foo = "baz"
|
||||
}
|
||||
|
||||
resource "aws_instance" "db" {
|
||||
security_groups = "${aws_security_group.firewall.*.id}"
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@ provider "do" {
|
|||
api_key = "${var.foo}"
|
||||
}
|
||||
|
||||
data "do" "depends" {
|
||||
depends_on = ["data.do.simple"]
|
||||
}
|
||||
|
||||
resource "aws_security_group" "firewall" {
|
||||
count = 5
|
||||
}
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
{
|
||||
"data": {
|
||||
"do": {
|
||||
"depends": {
|
||||
"hello": "world"
|
||||
}
|
||||
}
|
||||
},
|
||||
"resource": {
|
||||
"aws_instance": {
|
||||
"web": {
|
||||
|
|
|
@ -8,6 +8,11 @@ provider "aws" {
|
|||
secret_key = "bar"
|
||||
}
|
||||
|
||||
|
||||
data "do" "simple" {
|
||||
foo = "baz"
|
||||
}
|
||||
|
||||
resource "aws_instance" "db" {
|
||||
security_groups = "${aws_security_group.firewall.*.id}"
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@ provider "do" {
|
|||
api_key = "${var.foo}"
|
||||
}
|
||||
|
||||
data "do" "depends" {
|
||||
depends_on = ["data.do.simple"]
|
||||
}
|
||||
|
||||
resource "aws_security_group" "firewall" {
|
||||
count = 5
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue