Skip to content

Commit 58138bd

Browse files
authored
cmd: add platform constructor resync command and --all flag (#131)
Implements a new platform constructor resync command which resynchronises a given constructor or all.
1 parent 6372103 commit 58138bd

File tree

5 files changed

+352
-0
lines changed

5 files changed

+352
-0
lines changed

cmd/platform/constructor/resync.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package cmdconstructor
19+
20+
import (
21+
"fmt"
22+
23+
"github.com/spf13/cobra"
24+
25+
cmdutil "github.com/elastic/ecctl/cmd/util"
26+
"github.com/elastic/ecctl/pkg/ecctl"
27+
"github.com/elastic/ecctl/pkg/platform/constructor"
28+
)
29+
30+
var resyncConstructorCmd = &cobra.Command{
31+
Use: "resync {<constructor id> | --all}",
32+
Short: "Resynchronizes the search index and cache for the selected constructor or all",
33+
PreRunE: cmdutil.CheckInputHas1ArgsOr0ArgAndAll,
34+
RunE: func(cmd *cobra.Command, args []string) error {
35+
all, _ := cmd.Flags().GetBool("all")
36+
37+
if all {
38+
fmt.Println("Resynchronizing all constructors")
39+
res, err := constructor.ResyncAll(constructor.Params{
40+
API: ecctl.Get().API,
41+
})
42+
if err != nil {
43+
return err
44+
}
45+
46+
return ecctl.Get().Formatter.Format("", res)
47+
}
48+
49+
fmt.Printf("Resynchronizing constructor: %s\n", args[0])
50+
return constructor.Resync(constructor.ResyncParams{
51+
API: ecctl.Get().API,
52+
ID: args[0],
53+
})
54+
},
55+
}
56+
57+
func init() {
58+
Command.AddCommand(resyncConstructorCmd)
59+
resyncConstructorCmd.Flags().Bool("all", false, "Resynchronizes the search index for all constructors")
60+
}

docs/ecctl_platform_constructor.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,6 @@ ecctl platform constructor [flags]
4141
* [ecctl platform](ecctl_platform.md) - Manages the platform
4242
* [ecctl platform constructor list](ecctl_platform_constructor_list.md) - Returns all of the constructors in the platform
4343
* [ecctl platform constructor maintenance](ecctl_platform_constructor_maintenance.md) - Sets/un-sets a constructor's maintenance mode
44+
* [ecctl platform constructor resync](ecctl_platform_constructor_resync.md) - Resynchronizes the search index and cache for the selected constructor or all
4445
* [ecctl platform constructor show](ecctl_platform_constructor_show.md) - Returns information about the constructor with given ID
4546

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
## ecctl platform constructor resync
2+
3+
Resynchronizes the search index and cache for the selected constructor or all
4+
5+
### Synopsis
6+
7+
Resynchronizes the search index and cache for the selected constructor or all
8+
9+
```
10+
ecctl platform constructor resync {<constructor id> | --all} [flags]
11+
```
12+
13+
### Options
14+
15+
```
16+
--all Resynchronizes the search index for all constructors
17+
-h, --help help for resync
18+
```
19+
20+
### Options inherited from parent commands
21+
22+
```
23+
--apikey string API key to use to authenticate (If empty will look for EC_APIKEY environment variable)
24+
--config string Config name, used to have multiple configs in $HOME/.ecctl/<env> (default "config")
25+
--force Do not ask for confirmation
26+
--format string Formats the output using a Go template
27+
--host string Base URL to use
28+
--insecure Skips all TLS validation
29+
--message string A message to set on cluster operation
30+
--output string Output format [text|json] (default "text")
31+
--pass string Password to use to authenticate (If empty will look for EC_PASS environment variable)
32+
--pprof Enables pprofing and saves the profile to pprof-20060102150405
33+
-q, --quiet Suppresses the configuration file used for the run, if any
34+
--timeout duration Timeout to use on all HTTP calls (default 30s)
35+
--trace Enables tracing saves the trace to trace-20060102150405
36+
--user string Username to use to authenticate (If empty will look for EC_USER environment variable)
37+
--verbose Enable verbose mode
38+
```
39+
40+
### SEE ALSO
41+
42+
* [ecctl platform constructor](ecctl_platform_constructor.md) - Manages constructors
43+

pkg/platform/constructor/resync.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package constructor
19+
20+
import (
21+
"github.com/elastic/cloud-sdk-go/pkg/api"
22+
"github.com/elastic/cloud-sdk-go/pkg/client/platform_infrastructure"
23+
"github.com/elastic/cloud-sdk-go/pkg/models"
24+
"github.com/hashicorp/go-multierror"
25+
26+
"github.com/elastic/ecctl/pkg/util"
27+
)
28+
29+
// ResyncParams is consumed by Resync
30+
type ResyncParams struct {
31+
*api.API
32+
ID string
33+
}
34+
35+
// Validate ensures the parameters are usable by the consuming function.
36+
func (params ResyncParams) Validate() error {
37+
var merr = new(multierror.Error)
38+
39+
if params.API == nil {
40+
merr = multierror.Append(merr, errAPICannotBeNil)
41+
}
42+
43+
if params.ID == "" {
44+
merr = multierror.Append(merr, errIDCannotBeEmpty)
45+
}
46+
47+
return merr.ErrorOrNil()
48+
}
49+
50+
// Resync forces indexer to immediately resynchronize the search index
51+
// and cache for a given constructor.
52+
func Resync(params ResyncParams) error {
53+
if err := params.Validate(); err != nil {
54+
return err
55+
}
56+
57+
return util.ReturnErrOnly(
58+
params.API.V1API.PlatformInfrastructure.ResyncConstructor(
59+
platform_infrastructure.NewResyncConstructorParams().
60+
WithConstructorID(params.ID),
61+
params.API.AuthWriter,
62+
),
63+
)
64+
}
65+
66+
// ResyncAll asynchronously resynchronizes the search index for all constructors.
67+
func ResyncAll(params Params) (*models.ModelVersionIndexSynchronizationResults, error) {
68+
if err := params.Validate(); err != nil {
69+
return nil, err
70+
}
71+
72+
res, err := params.API.V1API.PlatformInfrastructure.ResyncConstructors(
73+
platform_infrastructure.NewResyncConstructorsParams(),
74+
params.API.AuthWriter,
75+
)
76+
if err != nil {
77+
return nil, api.UnwrapError(err)
78+
}
79+
80+
return res.Payload, nil
81+
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package constructor
19+
20+
import (
21+
"errors"
22+
"net/http"
23+
"net/url"
24+
"reflect"
25+
"testing"
26+
27+
"github.com/elastic/cloud-sdk-go/pkg/api"
28+
"github.com/elastic/cloud-sdk-go/pkg/api/mock"
29+
"github.com/elastic/cloud-sdk-go/pkg/models"
30+
multierror "github.com/hashicorp/go-multierror"
31+
)
32+
33+
func TestResync(t *testing.T) {
34+
type args struct {
35+
params ResyncParams
36+
}
37+
tests := []struct {
38+
name string
39+
args args
40+
wantErr error
41+
}{
42+
{
43+
name: "Fails due to parameter validation (Cluster ID)",
44+
args: args{},
45+
wantErr: &multierror.Error{Errors: []error{
46+
errors.New("api field cannot be nil"),
47+
errors.New("id field cannot be empty"),
48+
}},
49+
},
50+
{
51+
name: "Fails due to parameter validation (API)",
52+
args: args{params: ResyncParams{
53+
ID: "d324608c97154bdba2dff97511d40368",
54+
}},
55+
wantErr: &multierror.Error{Errors: []error{
56+
errors.New("api field cannot be nil"),
57+
}},
58+
},
59+
{
60+
name: "Fails due to unknown API response",
61+
args: args{params: ResyncParams{
62+
ID: "2c221bd86b7f48959a59ee3128d5c5e8",
63+
API: api.NewMock(mock.Response{Response: http.Response{
64+
StatusCode: http.StatusForbidden,
65+
Body: mock.NewStringBody(`{"error": "some forbidden error"}`),
66+
}}),
67+
}},
68+
wantErr: errors.New(`{"error": "some forbidden error"}`),
69+
},
70+
{
71+
name: "Fails due to API error",
72+
args: args{params: ResyncParams{
73+
ID: "2c221bd86b7f48959a59ee3128d5c5e8",
74+
API: api.NewMock(mock.Response{
75+
Error: errors.New("error with API"),
76+
}),
77+
}},
78+
wantErr: &url.Error{
79+
Op: "Post",
80+
URL: "https://mock-host/mock-path/platform/infrastructure/constructors/2c221bd86b7f48959a59ee3128d5c5e8/_resync",
81+
Err: errors.New("error with API"),
82+
},
83+
},
84+
{
85+
name: "Succeeds to resynchronize Kibana instance without errors",
86+
args: args{params: ResyncParams{
87+
ID: "d324608c97154bdba2dff97511d40368",
88+
API: api.NewMock(mock.Response{Response: http.Response{
89+
StatusCode: http.StatusOK,
90+
Body: mock.NewStringBody(`{}`),
91+
}}),
92+
}},
93+
},
94+
}
95+
96+
for _, tt := range tests {
97+
t.Run(tt.name, func(t *testing.T) {
98+
if err := Resync(tt.args.params); !reflect.DeepEqual(err, tt.wantErr) {
99+
t.Errorf("Resync() error = %v, wantErr %v", err, tt.wantErr)
100+
}
101+
})
102+
}
103+
}
104+
105+
func TestResyncAll(t *testing.T) {
106+
type args struct {
107+
params Params
108+
}
109+
tests := []struct {
110+
name string
111+
args args
112+
wantErr error
113+
want *models.ModelVersionIndexSynchronizationResults
114+
}{
115+
{
116+
name: "Fails due to parameter validation (API)",
117+
args: args{params: Params{}},
118+
wantErr: errors.New("api field cannot be nil"),
119+
},
120+
{
121+
name: "Fails due to unknown API response",
122+
args: args{params: Params{
123+
API: api.NewMock(mock.Response{Response: http.Response{
124+
StatusCode: http.StatusForbidden,
125+
Body: mock.NewStringBody(`{"error": "some forbidden error"}`),
126+
}}),
127+
}},
128+
wantErr: errors.New(`{"error": "some forbidden error"}`),
129+
},
130+
{
131+
name: "Fails due to API error",
132+
args: args{params: Params{
133+
API: api.NewMock(mock.Response{
134+
Error: errors.New("error with API"),
135+
}),
136+
}},
137+
wantErr: &url.Error{
138+
Op: "Post",
139+
URL: "https://mock-host/mock-path/platform/infrastructure/constructors/_resync?skip_matching_version=true",
140+
Err: errors.New("error with API"),
141+
},
142+
},
143+
{
144+
name: "Succeeds to re-synchronize all Kibana instances without errors",
145+
args: args{params: Params{
146+
API: api.NewMock(mock.Response{Response: http.Response{
147+
StatusCode: http.StatusAccepted,
148+
Body: mock.NewStringBody(`{}`),
149+
}}),
150+
}},
151+
want: &models.ModelVersionIndexSynchronizationResults{},
152+
},
153+
}
154+
155+
for _, tt := range tests {
156+
t.Run(tt.name, func(t *testing.T) {
157+
got, err := ResyncAll(tt.args.params)
158+
if !reflect.DeepEqual(tt.wantErr, err) {
159+
t.Errorf("ResyncAll() error = %v, wantErr %v", err, tt.wantErr)
160+
return
161+
}
162+
if !reflect.DeepEqual(got, tt.want) {
163+
t.Errorf("ResyncAll() = %v, want %v", got, tt.want)
164+
}
165+
})
166+
}
167+
}

0 commit comments

Comments
 (0)