Skip to content

Commit 8d3ed7f

Browse files
feat(RAIN-70441): add CLI command to support migration (#1369)
* feat: add cli support for gcpv2 migration
1 parent b480a52 commit 8d3ed7f

File tree

7 files changed

+135
-0
lines changed

7 files changed

+135
-0
lines changed

api/api.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ const (
127127
apiSuppressions = "v2/suppressions/%s/allExceptions"
128128

129129
apiRecommendations = "v2/recommendations/%s"
130+
131+
apiV2MigrateGcpAtSes = "v2/migrateGcpAtSes"
130132
)
131133

132134
// WithApiV2 configures the client to use the API version 2 (/api/v2)

api/cloud_accounts.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package api
2020

2121
import (
2222
"fmt"
23+
"time"
2324

2425
"github.com/pkg/errors"
2526

@@ -172,6 +173,30 @@ func (svc *CloudAccountsService) Delete(guid string) error {
172173
)
173174
}
174175

176+
// Migrate marks a Cloud Account integration that matches the provided guid for migration
177+
func (svc *CloudAccountsService) Migrate(guid string) error {
178+
if guid == "" {
179+
return errors.New("specify an intgGuid")
180+
}
181+
182+
data := MigrateRequestData{
183+
MigrateData{
184+
IntgGuid: guid,
185+
Props: Props{
186+
Migrate: true,
187+
MigrationTimestamp: time.Now(),
188+
},
189+
},
190+
}
191+
192+
return svc.client.RequestEncoderDecoder(
193+
"PATCH",
194+
apiV2MigrateGcpAtSes,
195+
data,
196+
nil,
197+
)
198+
}
199+
175200
// Get returns a raw response of the Cloud Account with the matching integration guid.
176201
//
177202
// To return a more specific Go struct of a Cloud Account integration, use the proper

api/cloud_accounts_gcp_at.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
package api
2020

21+
import "time"
22+
2123
// GetGcpAtSes gets a single GcpAtSes integration matching the provided integration guid
2224
func (svc *CloudAccountsService) GetGcpAtSes(guid string) (
2325
response GcpAtSesIntegrationResponse,
@@ -59,3 +61,17 @@ type GcpAtSesCredentials struct {
5961
PrivateKeyID string `json:"privateKeyId,omitempty"`
6062
PrivateKey string `json:"privateKey,omitempty"`
6163
}
64+
65+
type Props struct {
66+
Migrate bool `json:"migrate"`
67+
MigrationTimestamp time.Time `json:"migrationTimestamp"`
68+
}
69+
70+
type MigrateData struct {
71+
IntgGuid string `json:"intgGuid"`
72+
Props Props `json:"props"`
73+
}
74+
75+
type MigrateRequestData struct {
76+
Data MigrateData `json:"data"`
77+
}

api/cloud_accounts_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,56 @@ func TestCloudAccountsListByType(t *testing.T) {
263263
}
264264
}
265265

266+
func TestCloudAccountMigrate(t *testing.T) {
267+
var (
268+
intgGUID = intgguid.New()
269+
apiPath = "migrateGcpAtSes"
270+
fakeServer = lacework.MockServer()
271+
)
272+
fakeServer.MockToken("TOKEN")
273+
defer fakeServer.Close()
274+
275+
fakeServer.MockAPI(apiPath, func(w http.ResponseWriter, r *http.Request) {
276+
assert.Equal(t, "PATCH", r.Method, "cloudAccountMigrate() should be a PATCH method")
277+
278+
if assert.NotNil(t, r.Body) {
279+
body := httpBodySniffer(r)
280+
assert.Contains(t, body, intgGUID, "INTG_GUID missing")
281+
assert.Contains(t, body, "props", "migration props are missing")
282+
assert.Contains(t, body, "migrate\":true",
283+
"migrate field is missing or it is set to false")
284+
assert.Contains(t, body, "migrationTimestamp", "migration timestamp is missing")
285+
}
286+
fmt.Fprintf(w, generateCloudAccountResponse(singleGcpAtCloudAccount(intgGUID)))
287+
})
288+
289+
c, err := api.NewClient("test",
290+
api.WithToken("TOKEN"),
291+
api.WithURL(fakeServer.URL()),
292+
)
293+
assert.Nil(t, err)
294+
295+
cloudAccount := api.NewCloudAccount("integration_name",
296+
api.GcpAtSesCloudAccount,
297+
api.GcpAtSesData{
298+
Credentials: api.GcpAtSesCredentials{
299+
ClientID: "123456789",
300+
ClientEmail: "[email protected]",
301+
PrivateKeyID: "",
302+
PrivateKey: "",
303+
},
304+
},
305+
)
306+
assert.Equal(t, "integration_name", cloudAccount.Name, "GcpAtSes cloud account name mismatch")
307+
assert.Equal(t, "GcpAtSes", cloudAccount.Type,
308+
"a new GcpAtSes cloud account should match its type")
309+
assert.Equal(t, 1, cloudAccount.Enabled, "a new GcpAtSes cloud account should be enabled")
310+
cloudAccount.IntgGuid = intgGUID
311+
312+
err = c.V2.CloudAccounts.Migrate(intgGUID)
313+
assert.Nil(t, err)
314+
}
315+
266316
func generateCloudAccounts(guids []string, iType string) string {
267317
cloudAccounts := make([]string, len(guids))
268318
for i, guid := range guids {

cli/cmd/cloud_account.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ var (
6262
Args: cobra.ExactArgs(1),
6363
RunE: cloudAccountDelete,
6464
}
65+
66+
// cloudAccountMigrateCmd represents the migrate sub-command inside the cloud accounts command
67+
cloudAccountMigrateCmd = &cobra.Command{
68+
Use: "migrate",
69+
Short: "Mark a cloud account integration for migration",
70+
Args: cobra.ExactArgs(1),
71+
RunE: cloudAccountMigrate,
72+
}
6573
)
6674

6775
func init() {
@@ -71,6 +79,7 @@ func init() {
7179
cloudAccountCommand.AddCommand(cloudAccountShowCmd)
7280
cloudAccountCommand.AddCommand(cloudAccountDeleteCmd)
7381
cloudAccountCommand.AddCommand(cloudAccountCreateCmd)
82+
cloudAccountCommand.AddCommand(cloudAccountMigrateCmd)
7483

7584
// add type flag to cloud accounts list command
7685
cloudAccountListCmd.Flags().StringVarP(&cloudAccountType,
@@ -140,6 +149,17 @@ func cloudAccountDelete(_ *cobra.Command, args []string) error {
140149
return nil
141150
}
142151

152+
func cloudAccountMigrate(_ *cobra.Command, args []string) error {
153+
cli.StartProgress(" Initiating migration for cloud account...")
154+
err := cli.LwApi.V2.CloudAccounts.Migrate(args[0])
155+
cli.StopProgress()
156+
if err != nil {
157+
return errors.Wrap(err, "unable to initiate migration for cloud-account.")
158+
}
159+
cli.OutputHuman("The cloud account %s was marked for migration.\n", args[0])
160+
return nil
161+
}
162+
143163
func cloudAccountShow(_ *cobra.Command, args []string) error {
144164
var (
145165
cloudAccount api.CloudAccountResponse

integration/test_resources/help/cloud-account

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Available Commands:
1010
create Create a new cloud account integration
1111
delete Delete a cloud account integration
1212
list List all available cloud account integrations
13+
migrate Mark a cloud account integration for migration
1314
show Show a single cloud account integration
1415

1516
Flags:
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Mark a cloud account integration for migration
2+
3+
Usage:
4+
lacework cloud-account migrate [flags]
5+
6+
Flags:
7+
-h, --help help for migrate
8+
9+
Global Flags:
10+
-a, --account string account subdomain of URL (i.e. <ACCOUNT>.lacework.net)
11+
-k, --api_key string access key id
12+
-s, --api_secret string secret access key
13+
--api_token string access token (replaces the use of api_key and api_secret)
14+
--debug turn on debug logging
15+
--json switch commands output from human-readable to json format
16+
--nocache turn off caching
17+
--nocolor turn off colors
18+
--noninteractive turn off interactive mode (disable spinners, prompts, etc.)
19+
--organization access organization level data sets (org admins only)
20+
-p, --profile string switch between profiles configured at ~/.lacework.toml
21+
--subaccount string sub-account name inside your organization (org admins only)

0 commit comments

Comments
 (0)