Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a15edd2

Browse files
committedJul 10, 2020
Add account resource
1 parent 358739a commit a15edd2

File tree

4 files changed

+496
-1
lines changed

4 files changed

+496
-1
lines changed
 

‎client/account.go

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
package client
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strconv"
7+
8+
"github.com/google/martian/log"
9+
"github.com/imdario/mergo"
10+
)
11+
12+
13+
type DockerRegistry struct {
14+
Kind string `json:"kind"`
15+
BehindFirewall bool `json:"behindFirewall"`
16+
Primary bool `json:"primary"`
17+
Default bool `json:"default"`
18+
Internal bool `json:"internal"`
19+
DenyCompositeDomain bool `json:"denyCompositeDomain"`
20+
ID string `json:"_id"`
21+
Name string `json:"name"`
22+
Provider string `json:"provider"`
23+
Username string `json:"username"`
24+
Password string `json:"password"`
25+
Domain string `json:"domain"`
26+
RepositoryPrefix string `json:"repositoryPrefix"`
27+
}
28+
29+
type Suspension struct {
30+
IsSuspended bool `json:"isSuspended"`
31+
}
32+
33+
type Exist struct {
34+
Exist bool `json:"exist"`
35+
}
36+
37+
type Active struct {
38+
Active bool `json:"active"`
39+
}
40+
41+
type Integration struct {
42+
Stash Active `json:"stash,omitempty"`
43+
Github Active `json:"github,omitempty"`
44+
Gitlab Active `json:"gitlab,omitempty"`
45+
Aks Exist `json:"aks,omitempty"`
46+
Gcloud Exist `json:"gcloud,omitempty"`
47+
DigitalOcean Exist `json:"digitalOcean,omitempty"`
48+
Registries []DockerRegistry `json:"registries,omitempty"`
49+
}
50+
51+
type Build struct {
52+
Strategy string `json:"strategy,omitempty"`
53+
Nodes int `json:"nodes,omitempty"`
54+
Parallel int `json:"parallel,omitempty"`
55+
Packs []interface{} `json:"packs,omitempty"`
56+
}
57+
58+
type PaymentPlan struct {
59+
Trial struct {
60+
Trialing bool `json:"trialing"`
61+
TrialWillEndNotified bool `json:"trialWillEndNotified"`
62+
TrialEndedNotified bool `json:"trialEndedNotified"`
63+
Type string `json:"type"`
64+
PreviousSegment string `json:"previousSegment"`
65+
} `json:"trial,omitempty"`
66+
ID string `json:"id,omitempty"`
67+
Provider string `json:"provider,omitempty"`
68+
}
69+
70+
type ImageViewConfig struct {
71+
Version string `json:"version"`
72+
}
73+
74+
type BuildStepConfig struct {
75+
Version string `json:"version"`
76+
DisablePush bool `json:"disablePush"`
77+
AutoPush bool `json:"autoPush"`
78+
}
79+
80+
type CFCRState struct {
81+
Enabled bool `json:"enabled"`
82+
System string `json:"system"`
83+
DisplayGlobalNotice bool `json:"displayGlobalNotice"`
84+
AccountChoice string `json:"accountChoice"`
85+
}
86+
87+
type NotificationEvent struct {
88+
Events []string `json:"events"`
89+
Type string `json:"type"`
90+
}
91+
92+
type Collaborators struct {
93+
Limit int `json:"limit"`
94+
Used int `json:"used,omitempty"`
95+
}
96+
97+
type DataRetention struct {
98+
Weeks int `json:"weeks"`
99+
}
100+
101+
type Limits struct {
102+
Collaborators Collaborators `json:"collaborators,omitempty"`
103+
DataRetention DataRetention `json:"dataRetention,omitempty"`
104+
}
105+
106+
type Account struct {
107+
Suspension *Suspension `json:"suspension,omitempty"`
108+
Integrations *Integration `json:"integrations,omitempty"`
109+
Build *Build `json:"build,omitempty"`
110+
PaymentPlan *PaymentPlan `json:"paymentPlan,omitempty"`
111+
ImageViewConfig *ImageViewConfig `json:"imageViewConfig,omitempty"`
112+
BuildStepConfig *BuildStepConfig `json:"buildStepConfig,omitempty"`
113+
CFCRState *CFCRState `json:"CFCRState,omitempty"`
114+
AllowedDomains []interface{} `json:"allowedDomains,omitempty"`
115+
Admins []string `json:"admins,omitempty"`
116+
Environment int `json:"environment,omitempty"`
117+
DedicatedInfrastructure bool `json:"dedicatedInfrastructure,omitempty"`
118+
CanUsePrivateRepos bool `json:"canUsePrivateRepos,omitempty"`
119+
SupportPlan string `json:"supportPlan,omitempty"`
120+
IncreasedAttention bool `json:"increasedAttention,omitempty"`
121+
LocalUserPasswordIDPEnabled bool `json:"localUserPasswordIDPEnabled,omitempty"`
122+
CodefreshEnv string `json:"codefreshEnv,omitempty"`
123+
ID string `json:"_id,omitempty,omitempty"`
124+
BadgeToken string `json:"badgeToken,omitempty"`
125+
CreatedAt string `json:"createdAt,omitempty"`
126+
UpdatedAt string `json:"updatedAt,omitempty"`
127+
Name string `json:"name,omitempty"`
128+
RuntimeEnvironment string `json:"runtimeEnvironment,omitempty"`
129+
CfcrRepositoryPath string `json:"cfcrRepositoryPath,omitempty"`
130+
Notifications []NotificationEvent `json:"notifications,omitempty"`
131+
RepoPermission string `json:"repoPermission,omitempty"`
132+
__V int `json:"__v,omitempty"`
133+
Limits *Limits `json:"limits,omitempty"`
134+
Features map[string]bool `json:"features,omitempty"`
135+
// Features *Features `json:"features,omitempty"`
136+
// RuntimeEnvironments ToDo
137+
// Remaining ToDo
138+
// ID string `json:"id"`
139+
}
140+
141+
type AccountDetails struct {
142+
AccountDetails Account `json:"accountDetails"`
143+
}
144+
145+
// Decodes a TypeMap of map[string]interface{} into map[string]bool for account features
146+
func (account *Account) SetFeatures(m map[string]interface{}) {
147+
res := make(map[string]bool)
148+
for k, v := range m {
149+
value := v.(string)
150+
b, err := strconv.ParseBool(value)
151+
if err != nil {
152+
log.Errorf("[ERROR] Can't parse '%s = %s' as boolean", k, value)
153+
}
154+
res[k] = b
155+
}
156+
account.Features = res
157+
}
158+
159+
func (account *Account) GetID() string {
160+
return account.ID
161+
}
162+
163+
func (client *Client) GetAccountByID(id string) (*Account, error) {
164+
fullPath := fmt.Sprintf("/admin/accounts/%s", id)
165+
opts := RequestOptions{
166+
Path: fullPath,
167+
Method: "GET",
168+
}
169+
170+
resp, err := client.RequestAPI(&opts)
171+
172+
if err != nil {
173+
return nil, err
174+
}
175+
176+
var account Account
177+
178+
err = DecodeResponseInto(resp, &account)
179+
if err != nil {
180+
return nil, err
181+
}
182+
183+
return &account, nil
184+
}
185+
186+
func (client *Client) CreateAccount(account *Account) (*Account, error) {
187+
188+
body, err := EncodeToJSON(account)
189+
190+
if err != nil {
191+
return nil, err
192+
}
193+
opts := RequestOptions{
194+
Path: "/admin/accounts",
195+
Method: "POST",
196+
Body: body,
197+
}
198+
199+
resp, err := client.RequestAPI(&opts)
200+
201+
if err != nil {
202+
return nil, err
203+
}
204+
205+
var respAccount Account
206+
207+
err = DecodeResponseInto(resp, &respAccount)
208+
if err != nil {
209+
return nil, err
210+
}
211+
212+
return &respAccount, nil
213+
}
214+
215+
func (client *Client) UpdateAccount(account *Account) (*Account, error) {
216+
217+
id := account.GetID()
218+
if id == "" {
219+
return nil, errors.New("[ERROR] Account ID is empty")
220+
}
221+
222+
existingAccount, err := client.GetAccountByID(id)
223+
if err = mergo.Merge(account, existingAccount); err != nil {
224+
return nil, err
225+
}
226+
227+
putAccount := AccountDetails{*account}
228+
229+
body, err := EncodeToJSON(putAccount)
230+
if err != nil {
231+
return nil, err
232+
}
233+
234+
fullPath := fmt.Sprintf("/admin/accounts/%s/update", id)
235+
opts := RequestOptions{
236+
Path: fullPath,
237+
Method: "POST",
238+
Body: body,
239+
}
240+
241+
resp, err := client.RequestAPI(&opts)
242+
if err != nil {
243+
return nil, err
244+
}
245+
246+
var respAccount Account
247+
err = DecodeResponseInto(resp, &respAccount)
248+
if err != nil {
249+
return nil, err
250+
}
251+
252+
return &respAccount, nil
253+
}
254+
255+
func (client *Client) DeleteAccount(id string) error {
256+
257+
fullPath := fmt.Sprintf("/admin/accounts/%s", id)
258+
opts := RequestOptions{
259+
Path: fullPath,
260+
Method: "DELETE",
261+
}
262+
263+
_, err := client.RequestAPI(&opts)
264+
265+
if err != nil {
266+
return err
267+
}
268+
269+
return nil
270+
}

‎codefresh/resource_account.go

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
package codefresh
2+
3+
import (
4+
// "fmt"
5+
cfClient "github.com/codefresh-io/terraform-provider-codefresh/client"
6+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
7+
// "os"
8+
)
9+
10+
func resourceAccount() *schema.Resource {
11+
return &schema.Resource{
12+
Create: resourceAccountCreate,
13+
Read: resourceAccountRead,
14+
Update: resourceAccountUpdate,
15+
Delete: resourceAccountDelete,
16+
Importer: &schema.ResourceImporter{
17+
State: schema.ImportStatePassthrough,
18+
},
19+
Schema: map[string]*schema.Schema{
20+
"name": {
21+
Type: schema.TypeString,
22+
Required: true,
23+
},
24+
// "runtime_environment": {
25+
// Type: schema.TypeString,
26+
// Optional: true,
27+
// },
28+
"admins": {
29+
Type: schema.TypeSet,
30+
Optional: true,
31+
Elem: &schema.Schema{
32+
Type: schema.TypeString,
33+
},
34+
},
35+
"limits": {
36+
Type: schema.TypeList,
37+
Optional: true,
38+
Elem: &schema.Resource{
39+
Schema: map[string]*schema.Schema{
40+
"collaborators": {
41+
Type: schema.TypeInt,
42+
Required: true,
43+
},
44+
"data_retention_weeks": {
45+
Type: schema.TypeInt,
46+
Optional: true,
47+
},
48+
},
49+
},
50+
},
51+
"build": {
52+
Type: schema.TypeList,
53+
Optional: true,
54+
Elem: &schema.Resource{
55+
Schema: map[string]*schema.Schema{
56+
"parallel": {
57+
Type: schema.TypeInt,
58+
Required: true,
59+
},
60+
"nodes": {
61+
Type: schema.TypeInt,
62+
Optional: true,
63+
},
64+
},
65+
},
66+
},
67+
// Features can not be managed by account API
68+
// "features": {
69+
// Type: schema.TypeMap,
70+
// Optional: true,
71+
// Elem: &schema.Schema {
72+
// Type: schema.TypeString,
73+
// },
74+
// },
75+
},
76+
}
77+
}
78+
79+
func resourceAccountCreate(d *schema.ResourceData, meta interface{}) error {
80+
81+
client := meta.(*cfClient.Client)
82+
83+
account := *mapResourceToAccount(d)
84+
85+
resp, err := client.CreateAccount(&account)
86+
if err != nil {
87+
return err
88+
}
89+
90+
d.SetId(resp.ID)
91+
92+
return nil
93+
}
94+
95+
func resourceAccountRead(d *schema.ResourceData, meta interface{}) error {
96+
97+
client := meta.(*cfClient.Client)
98+
99+
accountID := d.Id()
100+
if accountID == "" {
101+
d.SetId("")
102+
return nil
103+
}
104+
105+
team, err := client.GetAccountByID(accountID)
106+
if err != nil {
107+
return err
108+
}
109+
110+
err = mapAccountToResource(team, d)
111+
if err != nil {
112+
return err
113+
}
114+
115+
return nil
116+
}
117+
118+
func resourceAccountUpdate(d *schema.ResourceData, meta interface{}) error {
119+
120+
client := meta.(*cfClient.Client)
121+
122+
account := *mapResourceToAccount(d)
123+
124+
_, err := client.UpdateAccount(&account)
125+
if err != nil {
126+
return err
127+
}
128+
129+
// TODO
130+
// - rename account
131+
// - add/remove admins
132+
133+
return nil
134+
}
135+
136+
func resourceAccountDelete(d *schema.ResourceData, meta interface{}) error {
137+
client := meta.(*cfClient.Client)
138+
139+
err := client.DeleteAccount(d.Id())
140+
if err != nil {
141+
return err
142+
}
143+
144+
return nil
145+
}
146+
147+
func mapAccountToResource(account *cfClient.Account, d *schema.ResourceData) error {
148+
149+
err := d.Set("name", account.Name)
150+
if err != nil {
151+
return err
152+
}
153+
154+
// err = d.Set("runtime_environment", account.RuntimeEnvironment)
155+
// if err != nil {
156+
// return err
157+
// }
158+
159+
err = d.Set("admins", account.Admins)
160+
if err != nil {
161+
return err
162+
}
163+
164+
err = d.Set("limits", []map[string]interface{}{flattenLimits(*account.Limits)})
165+
if err != nil {
166+
return err
167+
}
168+
169+
err = d.Set("build", []map[string]interface{}{flattenBuild(*account.Build)})
170+
if err != nil {
171+
return err
172+
}
173+
174+
return nil
175+
}
176+
177+
func flattenLimits(limits cfClient.Limits) map[string]interface{} {
178+
res := make(map[string]interface{})
179+
res["collaborators"] = limits.Collaborators.Limit
180+
res["data_retention_weeks"] = limits.DataRetention.Weeks
181+
return res
182+
}
183+
184+
func flattenBuild(build cfClient.Build) map[string]interface{} {
185+
res := make(map[string]interface{})
186+
res["parallel"] = build.Parallel
187+
res["nodes"] = build.Nodes
188+
return res
189+
}
190+
func mapResourceToAccount(d *schema.ResourceData) *cfClient.Account {
191+
admins := d.Get("admins").(*schema.Set).List()
192+
193+
account := &cfClient.Account{
194+
ID: d.Id(),
195+
Name: d.Get("name").(string),
196+
Admins: convertStringArr(admins),
197+
// RuntimeEnvironment: d.Get("runtime_environment").(string),
198+
}
199+
200+
if _, ok := d.GetOk("limits"); ok {
201+
account.Limits = &cfClient.Limits{
202+
Collaborators: cfClient.Collaborators{
203+
Limit: d.Get("limits.0.collaborators").(int),
204+
},
205+
DataRetention: cfClient.DataRetention{
206+
Weeks: d.Get("limits.0.data_retention_weeks").(int),
207+
},
208+
}
209+
}
210+
211+
if _, ok := d.GetOk("build"); ok {
212+
account.Build = &cfClient.Build{
213+
Parallel: d.Get("build.0.parallel").(int),
214+
Nodes: d.Get("build.0.nodes").(int),
215+
}
216+
}
217+
218+
// Features can not be managed by account API
219+
// features := d.Get("features").(map[string]interface{})
220+
// account.SetFeatures(features)
221+
222+
return account
223+
}
224+

‎codefresh/utils.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ func convertVariables(vars []cfClient.Variable) map[string]string {
2525
res[v.Key] = v.Value
2626
}
2727
return res
28-
}
28+
}

‎go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/bflad/tfproviderlint v0.14.0
77
github.com/client9/misspell v0.3.4
88
github.com/golangci/golangci-lint v1.27.0
9+
github.com/google/martian v2.1.0+incompatible
910
github.com/hashicorp/terraform v0.12.25
1011
github.com/hashicorp/terraform-plugin-sdk v1.7.0
1112
github.com/imdario/mergo v0.3.9

0 commit comments

Comments
 (0)
Please sign in to comment.