Skip to content

Commit 1220c10

Browse files
authored
Merge pull request #4 from bytebase/feat/api
feat: support data source for instance and environment
2 parents 8633841 + 6feffc4 commit 1220c10

File tree

8 files changed

+319
-50
lines changed

8 files changed

+319
-50
lines changed

examples/main.tf

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,15 @@ output "dev_instance" {
5656
# But we can still print the instance via `terraform output -json dev_instance`
5757
sensitive = true
5858
}
59+
60+
# List data source
61+
data "bytebase_environments" "all" {}
62+
data "bytebase_instances" "all" {}
63+
64+
output "all_environments" {
65+
value = data.bytebase_environments.all.environments
66+
}
67+
68+
output "all_instances" {
69+
value = data.bytebase_instances.all.instances
70+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"strconv"
6+
"time"
7+
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10+
11+
"github.com/bytebase/terraform-provider-bytebase/api"
12+
)
13+
14+
func dataSourceEnvironmentList() *schema.Resource {
15+
return &schema.Resource{
16+
ReadContext: dataSourceEnvironmentRead,
17+
Schema: map[string]*schema.Schema{
18+
"environments": {
19+
Type: schema.TypeList,
20+
Computed: true,
21+
Elem: &schema.Resource{
22+
Schema: map[string]*schema.Schema{
23+
"id": {
24+
Type: schema.TypeInt,
25+
Computed: true,
26+
},
27+
"name": {
28+
Type: schema.TypeString,
29+
Computed: true,
30+
Description: "The environment unique name.",
31+
},
32+
"order": {
33+
Type: schema.TypeInt,
34+
Computed: true,
35+
Description: "The environment sorting order.",
36+
},
37+
},
38+
},
39+
},
40+
},
41+
}
42+
}
43+
44+
func dataSourceEnvironmentRead(_ context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
45+
c := m.(api.Client)
46+
47+
// Warning or errors can be collected in a slice type
48+
var diags diag.Diagnostics
49+
50+
environmentList, err := c.ListEnvironment()
51+
if err != nil {
52+
return diag.FromErr(err)
53+
}
54+
55+
environments := make([]map[string]interface{})
56+
for _, environment := range environmentList {
57+
env := make(map[string]interface{})
58+
env["id"] = environment.ID
59+
env["name"] = environment.Name
60+
env["order"] = environment.Order
61+
62+
environments = append(environments, env)
63+
}
64+
65+
if err := d.Set("environments", environments); err != nil {
66+
return diag.FromErr(err)
67+
}
68+
69+
// always refresh
70+
d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
71+
72+
return diags
73+
}

provider/data_source_instance.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"strconv"
6+
"time"
7+
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10+
11+
"github.com/bytebase/terraform-provider-bytebase/api"
12+
)
13+
14+
func dataSourceInstanceList() *schema.Resource {
15+
return &schema.Resource{
16+
ReadContext: dataSourceInstanceRead,
17+
Schema: map[string]*schema.Schema{
18+
"instances": {
19+
Type: schema.TypeList,
20+
Computed: true,
21+
Elem: &schema.Resource{
22+
Schema: map[string]*schema.Schema{
23+
"id": {
24+
Type: schema.TypeInt,
25+
Computed: true,
26+
},
27+
"name": {
28+
Type: schema.TypeString,
29+
Computed: true,
30+
},
31+
"engine": {
32+
Type: schema.TypeString,
33+
Computed: true,
34+
},
35+
"engine_version": {
36+
Type: schema.TypeString,
37+
Computed: true,
38+
},
39+
"external_link": {
40+
Type: schema.TypeString,
41+
Computed: true,
42+
},
43+
"host": {
44+
Type: schema.TypeString,
45+
Computed: true,
46+
},
47+
"port": {
48+
Type: schema.TypeString,
49+
Computed: true,
50+
},
51+
"username": {
52+
Type: schema.TypeString,
53+
Computed: true,
54+
},
55+
"environment": {
56+
Type: schema.TypeString,
57+
Computed: true,
58+
},
59+
},
60+
},
61+
},
62+
},
63+
}
64+
}
65+
66+
func dataSourceInstanceRead(_ context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
67+
c := m.(api.Client)
68+
69+
// Warning or errors can be collected in a slice type
70+
var diags diag.Diagnostics
71+
72+
instanceList, err := c.ListInstance()
73+
if err != nil {
74+
return diag.FromErr(err)
75+
}
76+
77+
instances := make([]map[string]interface{}, 0)
78+
for _, instance := range instanceList {
79+
ins := make(map[string]interface{})
80+
ins["id"] = instance.ID
81+
ins["name"] = instance.Name
82+
ins["engine"] = instance.Engine
83+
ins["engine_version"] = instance.EngineVersion
84+
ins["external_link"] = instance.ExternalLink
85+
ins["host"] = instance.Host
86+
ins["port"] = instance.Port
87+
ins["username"] = instance.Username
88+
ins["environment"] = instance.Environment
89+
90+
instances = append(instances, ins)
91+
}
92+
93+
if err := d.Set("instances", instances); err != nil {
94+
return diag.FromErr(err)
95+
}
96+
97+
// always refresh
98+
d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
99+
100+
return diags
101+
}

provider/internal/mock_client.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,13 @@ func (c *mockClient) GetEnvironment(environmentID int) (*api.Environment, error)
6464
}
6565

6666
// ListEnvironment finds all environments.
67-
func (*mockClient) ListEnvironment() ([]*api.Environment, error) {
68-
return nil, errors.Errorf("ListEnvironment is not implemented yet")
67+
func (c *mockClient) ListEnvironment() ([]*api.Environment, error) {
68+
environments := make([]*api.Environment, 0)
69+
for _, env := range c.environmentMap {
70+
environments = append(environments, env)
71+
}
72+
73+
return environments, nil
6974
}
7075

7176
// UpdateEnvironment updates the environment.
@@ -107,8 +112,13 @@ func (c *mockClient) findEnvironmentByName(envName string) *api.Environment {
107112
}
108113

109114
// ListInstance will return all instances.
110-
func (*mockClient) ListInstance() ([]*api.Instance, error) {
111-
return nil, errors.Errorf("ListInstance is not implemented yet")
115+
func (c *mockClient) ListInstance() ([]*api.Instance, error) {
116+
instances := make([]*api.Instance, 0)
117+
for _, instance := range c.instanceMap {
118+
instances = append(instances, instance)
119+
}
120+
121+
return instances, nil
112122
}
113123

114124
// CreateInstance creates the instance.

provider/internal/test_utils.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package internal
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
8+
"github.com/pkg/errors"
9+
)
10+
11+
// TestGetTestStepForDataSource returns the test step for data source test.
12+
func TestGetTestStepForDataSource(
13+
dataSourceKey, identifier, fieldKey string,
14+
expectCount int,
15+
) resource.TestStep {
16+
resourceKey := fmt.Sprintf("data.%s.%s", dataSourceKey, identifier)
17+
outputKey := fmt.Sprintf("%s_%s_output_count", dataSourceKey, identifier)
18+
19+
return resource.TestStep{
20+
Config: getDataSourceListConfig(dataSourceKey, identifier, fieldKey, outputKey),
21+
Check: resource.ComposeTestCheckFunc(
22+
TestCheckResourceExists(resourceKey),
23+
resource.TestCheckOutput(outputKey, fmt.Sprintf("%d", expectCount)),
24+
),
25+
}
26+
}
27+
28+
func getDataSourceListConfig(dataSourceKey, identifier, fieldKey, outputKey string) string {
29+
return fmt.Sprintf(`
30+
data "%s" "%s" {}
31+
32+
output "%s" {
33+
value = length(data.%s.%s.%s)
34+
}
35+
`, dataSourceKey, identifier, outputKey, dataSourceKey, identifier, fieldKey)
36+
}
37+
38+
// TestCheckResourceExists will check if the resource exists in the state.
39+
func TestCheckResourceExists(resourceKey string) resource.TestCheckFunc {
40+
return func(s *terraform.State) error {
41+
rs, ok := s.RootModule().Resources[resourceKey]
42+
43+
if !ok {
44+
return errors.Errorf("Not found: %s", resourceKey)
45+
}
46+
47+
if rs.Primary.ID == "" {
48+
return errors.Errorf("No resource set")
49+
}
50+
51+
return nil
52+
}
53+
}

provider/provider.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,46 @@ package provider
33

44
import (
55
"context"
6+
"fmt"
67

78
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
89
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
910

1011
"github.com/bytebase/terraform-provider-bytebase/client"
1112
)
1213

14+
const (
15+
envKeyForBytebaseURL = "BYTEBASE_URL"
16+
envKeyForyUserEmail = "BYTEBASE_USER_EMAIL"
17+
envKeyForyUserPassword = "BYTEBASE_USER_PASSWORD"
18+
)
19+
1320
// NewProvider is the implement for Bytebase Terraform provider.
1421
func NewProvider() *schema.Provider {
1522
return &schema.Provider{
1623
Schema: map[string]*schema.Schema{
1724
"bytebase_url": {
1825
Type: schema.TypeString,
1926
Required: true,
20-
DefaultFunc: schema.EnvDefaultFunc("BYTEBASE_URL", nil),
27+
DefaultFunc: schema.EnvDefaultFunc(envKeyForBytebaseURL, nil),
2128
},
2229
"email": {
2330
Type: schema.TypeString,
2431
Required: true,
25-
DefaultFunc: schema.EnvDefaultFunc("BYTEBASE_USER_EMAIL", nil),
32+
DefaultFunc: schema.EnvDefaultFunc(envKeyForyUserEmail, nil),
2633
},
2734
"password": {
2835
Type: schema.TypeString,
2936
Required: true,
3037
Sensitive: true,
31-
DefaultFunc: schema.EnvDefaultFunc("BYTEBASE_USER_PASSWORD", nil),
38+
DefaultFunc: schema.EnvDefaultFunc(envKeyForyUserPassword, nil),
3239
},
3340
},
3441
ConfigureContextFunc: providerConfigure,
35-
DataSourcesMap: map[string]*schema.Resource{},
42+
DataSourcesMap: map[string]*schema.Resource{
43+
"bytebase_instances": dataSourceInstanceList(),
44+
"bytebase_environments": dataSourceEnvironmentList(),
45+
},
3646
ResourcesMap: map[string]*schema.Resource{
3747
"bytebase_environment": resourceEnvironment(),
3848
"bytebase_instance": resourceInstance(),
@@ -52,7 +62,7 @@ func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{},
5262
diags = append(diags, diag.Diagnostic{
5363
Severity: diag.Error,
5464
Summary: "Unable to create HashiCups client",
55-
Detail: "BYTEBASE_USER_EMAIL or BYTEBASE_USER_PASSWORD cannot be empty",
65+
Detail: fmt.Sprintf("%s or %s cannot be empty", envKeyForyUserEmail, envKeyForyUserPassword),
5666
})
5767

5868
return nil, diags
@@ -62,7 +72,7 @@ func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{},
6272
diags = append(diags, diag.Diagnostic{
6373
Severity: diag.Error,
6474
Summary: "Unable to create HashiCups client",
65-
Detail: "BYTEBASE_URL cannot be empty",
75+
Detail: fmt.Sprintf("%s cannot be empty", envKeyForBytebaseURL),
6676
})
6777

6878
return nil, diags

0 commit comments

Comments
 (0)