Skip to content

Commit 42ce7af

Browse files
authored
docs: Update to reflect the current state of the project (#347)
Updates the developer docs with current information about the state of the project. Some of the information removed from the STYLEGUIDE.md will be moved to cloud-sdk-go where it is relevant.
1 parent 500e04a commit 42ce7af

File tree

2 files changed

+45
-122
lines changed

2 files changed

+45
-122
lines changed

developer_docs/NEW_COMMAND.md

Lines changed: 33 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -10,67 +10,53 @@ into the `app` package for business logic and returns results.
1010

1111
[`cmd/root.go`](../cmd/root.go) contains the root command (`ecctl`) and global flags.
1212
[`cmd/commands.go`](../cmd/commands.go) attaches the top level commands (`deployment`, `platform`, etc) to the root command.
13-
The subdirectories define the subcommand structure (e.g. `platform` -> `proxy`). The lowest level `command.go`
14-
(e.g. [`cmd/platform/proxy/command.go`](../cmd/platform/proxy/command.go)) contains the definition of the actual command
15-
(e.g. `platform proxy list`).
13+
The subdirectories define the subcommand structure (e.g. `deployment` -> `list`). The lowest level
14+
(e.g. [`cmd/deployment/list.go`](../cmd/deployment/list.go)) contains the definition of the actual command
15+
(e.g. `deployment list`).
1616

17-
### Example: adding a new command to list proxies
17+
### Example: adding a new command to list deployments
1818

19-
Create a new subdirectory `cmd/platform/proxy`, and add a `command.go` file to it. (Some of these files/directories
20-
might already exist, e.g. if you are adding a new operation for an existing entity.) You will define your new command here.
19+
Create a new subdirectory `cmd/deployment`, and add a `list.go` file to it. Some of these files/directories
20+
might already exist (i.e. if you are adding a new operation for an existing entity). You will define your new command here.
2121

22-
First, you need to define the `proxy` command:
22+
Define the `list` subcommand in `cmd/deployment/list.go`:
2323

2424
```go
25-
var Command = &cobra.Command{
26-
Use: "proxy",
27-
// ...
28-
}
29-
```
30-
31-
Next, define the `list` subcommand:
32-
33-
```go
34-
var listProxiesCmd = &cobra.Command{
25+
var listCmd = &cobra.Command{
3526
Use: "list",
3627
// ...
3728
}
3829
```
3930

4031
There are a few things to note about the `list` command:
4132

42-
- The `list` command will contain a call to the `proxy.List()` method from the `pkg/platform/proxy` package which you have
43-
not defined yet. You will define this method later. You will also need to define the parameters struct used to pass
44-
arguments to this method - e.g. `proxy.ListParams`.
45-
- Ideally, you would implement a custom text formatter to present the proxy list in a user-friendly way. We currently
33+
- The `list` command will contain a call to the `deploymentapi.List()` function from the [`/pkg/api/deploymentapi`](https://github.com/elastic/cloud-sdk-go/tree/master/pkg/api/deploymentapi)
34+
package found in the cloud-sdk-go. You will define this function later. You will also need to define the parameters
35+
struct used to pass arguments to this function - e.g. `deploymentapi.ListParams`.
36+
- Ideally, you would implement a custom text formatter to present the deployment list in a user-friendly way. We currently
4637
support two types of output: json and text. Absent a custom formatter, the output defaults to *json*. If you choose to add
4738
a formatter to support *text* output, you'll need to create a template such as
48-
[`pkg/formatter/templates/text/proxy/list.gotmpl`](../pkg/formatter/templates/text/proxy/list.gotmpl). To ensure this template
49-
gets used, invoke a `ecctl.Get().Formatter.Format()` from the [`listProxiesCmd`](https://github.com/elastic/ecctl/blob/a90daa0c4411905c8d5c3fa06f5b6250395c4730/cmd/platform/proxy/command.go#L60).
39+
[`pkg/formatter/templates/text/deployment/list.gotmpl`](../pkg/formatter/templates/text/deployment/list.gotmpl). To ensure this template
40+
gets used, invoke a `ecctl.Get().Formatter.Format()` from the [`listCmd`](https://github.com/elastic/ecctl/blob/master/cmd/deployment/list.go#L39).
5041

51-
Next, the `init()` function attaches the `list` subcommand to the `proxy` command, and adds any custom
42+
Next, the `init()` function attaches the `list` subcommand to the `deployment` command, and adds any custom
5243
command line parameters.
5344

5445
```go
5546
func init() {
56-
Command.AddCommand(listProxiesCmd)
47+
Command.AddCommand(listCmd)
5748
// ...
5849
}
5950

6051
```
6152

62-
Finally, you need to attach the `proxy` subcommand to the `platform` command, by adding a reference to it
63-
in [`cmd/platform/platform.go`](../cmd/platform/platform.go).
53+
Finally, you need to attach the `deployment` subcommand to the `ecctl` command, by adding a reference to it
54+
in [`cmd/commands.go`](../cmd/commands.go).
6455

6556
## The `pkg` package
6657

6758
The [`pkg`](../pkg) package is used by the `cmd` package to create commands and is library code that's ok to use by external applications.
6859

69-
### [`deployment`](../pkg/deployment)
70-
71-
Business logic behind `deployment` commands. Typically, methods in this package are called from the `cmd` package,
72-
they accept parameters and call into the ES Cloud API, process and return results to the commands.
73-
7460
### [`ecctl`](../pkg/ecctl)
7561

7662
Contains the business logic and configuration for the ecctl app.
@@ -79,39 +65,32 @@ Contains the business logic and configuration for the ecctl app.
7965

8066
Functions and templates to format command output from json into user friendly text.
8167

82-
### [`platform`](../pkg/platform)
83-
84-
Business logic behind `platform` commands. Typically, methods in this package are called from the `cmd` package,
85-
they accept parameters and call into the ES Cloud API, process and return results to the commands.
86-
8768
### [`util`](../pkg/util)
8869

8970
Common resources, such as utility functions, constants, parameters, that are shared among different packages.
9071

91-
### Example: adding a new command to list proxies (application logic)
72+
### Example: adding a new command to list deployments (application logic)
9273

93-
Next, you need to add the business logic to your `platform proxy list` command. That's the `List()`
94-
method mentioned earlier. It should go into the `pkg/platform/proxy/proxy.go` file (that you'll need create if it does
95-
not already exist):
74+
Next, you need to add the business logic to your `ecctl deployment list` command. That's the `List()`
75+
function mentioned earlier. These APIs can be found in [cloud-sdk-go](https://github.com/elastic/cloud-sdk-go/tree/master/pkg/api).
76+
The API for our list command should go in the [`cloud-sdk-go/pkg/api/deploymentapi/list.go`](https://github.com/elastic/cloud-sdk-go/tree/master/pkg/api/deploymentapi/list.go) file
77+
which you'll need create if it does not already exist:
9678

9779
```go
98-
func List(params ListParams) (*models.ProxyOverview, error) {
80+
func List(params ListParams) (*models.DeploymentsListResponse, error) {
9981
// ...
10082
}
10183
```
10284

10385
Things to note about the `List` function:
10486

105-
- This is where the main business logic happens: it should include a call to the cloud API to retrieve all the
106-
proxies and return them to the calling function.
107-
- Make sure to properly catch and handle all possible errors. Consider using the `multierror`
108-
library if that makes sense. In this simple case, where we only have one API call, and not much else in terms of processing,
109-
it's probably fine to use regular `errors`.
110-
- For a general set of guidelines on how to write good Go code, see our [Style Guide](https://github.com/elastic/ecctl/blob/master/developer_docs/STYLEGUIDE.md).
87+
- This is where the main business logic happens: it should include a call to the Elastic Cloud API to retrieve all the
88+
deployments and return them to the calling function.
89+
- Make sure to properly catch and handle all possible validation errors. Use the [`multierror`](https://github.com/elastic/cloud-sdk-go/blob/master/pkg/api/deploymentapi/get.go#L46-L57)
90+
package even if you're returning a single error. This provides consistency and good UX since the errors will be be properly prefixed.
91+
- For a general set of guidelines on the project's code style, see our [Style Guide](https://github.com/elastic/ecctl/blob/master/developer_docs/STYLEGUIDE.md).
11192

112-
You'll also need to define the `ListParams` struct, as we normally use structs to pass several arguments to functions. If you only have one or two
113-
functions and corresponding parameter structs, it's ok to define the parameters structs in the same file. If it starts growing beyond that,
114-
a good practice is to define parameter structs in their own file - in this case it would be `pkg/platform/proxy/proxy_params.go`.
93+
You'll also need to define the `ListParams` struct, as we normally use structs to pass several arguments to API functions.
11594

11695
```go
11796
type ListParams struct {
@@ -128,14 +107,13 @@ func (params ListParams) Validate() error {
128107
```
129108

130109
Additionally, please create unit tests for your changes. Both the `List()` function and the `Validate()`
131-
method for `ListParams` need to be tested. These tests should go to `pkg/platform/proxy/proxy_test.go`
132-
(and `pkg/platform/proxy/proxy_params_test.go`, if you separated the params in their own file).
110+
method for `ListParams` need to be tested. These tests should go to `cloud-sdk-go/pkg/api/deploymentapi/list_test.go`
133111
There are many examples of this in the code base, so feel free to browse and use them as inspiration.
134112

135113
That concludes all the steps necessary for creating a new command. You can easily manually test your new command by
136-
making use of our [helper scripts](../CONTRIBUTING.md#helpers):
114+
importing your local cloud-sdk-go changes running `make fake-sdk`, and making use of our [helper scripts](../CONTRIBUTING.md#helpers):
137115

138-
`dev-cli --config ~/path/to/your/ecctl/config.yaml platform proxy list`
116+
`dev-cli --config config deployment list`
139117

140118
If your command behaves as expected, all that's left is to make sure you followed all the
141119
[code contribution guidelines](../CONTRIBUTING.md#code-contribution-guidelines) before submitting your PR.

developer_docs/STYLEGUIDE.md

Lines changed: 12 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
- [API Errors](#api-errors)
77
- [Multiple errors](#multiple-errors)
88
- [Packages](#packages)
9-
- [Structure](#structure)
9+
- [Command Structure](#command-structure)
1010
- [Util packages](#util-packages)
1111
- [Testing](#testing)
1212
- [Unit tests](#unit-tests)
@@ -37,19 +37,15 @@ All errors must be handled and returned, `_` variables must not be used to disca
3737

3838
#### Exceptions
3939

40-
Parsing command line arguments/flags is done like this
40+
Parsing command line arguments/flags is done like this:
4141

4242
```go
4343
threshold, _ := cmd.Flags().GetUint32(thresholdArg)
4444
```
4545

46-
The above line would return an error only if the flag is not defined, or the datatype does not match the flag declaration data type.
46+
The above line would return an error only if the flag is not defined, or the data type does not match the flag declaration data type.
4747
Adding many `if err` checks will make the code a little bit noisy so we can ignore the errors in these cases.
4848

49-
### API Errors
50-
51-
API errors should always be encapsulated with `apierr.Unwrap()`, this function tries to break down and inspect the encapsulated and multi-layer wraps that the API errors contain.
52-
5349
### Multiple errors
5450

5551
When multiple errors can be returned, it is preferable to use the `mutlierror.Prefixed` type to return all the possible errors with a prefixed string to include some context.
@@ -89,15 +85,13 @@ func (params StopParams) Validate() error {
8985

9086
## Packages
9187

92-
### Structure
93-
94-
A package per context, a context applies to any of the high level containers like `platform`, `deployment`, etc. When a context becomes too large to be contained in a package we can start breaking it down into sub-packages.
88+
### Command Structure
9589

96-
An example would be [pkg/deployment/elasticsearch/plan](../pkg/deployment/elasticsearch/plan).
90+
Commands are defined following the structure of the Elastic Cloud API. If a request to the API is `GET /api/v1/deployments/{deployment_id}`, the corresponding command will be `ecctl deployment show <deployment_id>`.
9791

9892
### Util packages
9993

100-
When a function can be made generic it should go in one of the utils packages (e.g. [pkg/util](../pkg/util), [pkg/util](../pkg/util)) to remove complexity and give the ability to be reused.
94+
When a function can be made generic it should go in one of the utils packages (e.g. [cmd/util](../cmd/util), [pkg/util](../pkg/util)) to remove complexity and give the ability to be reused.
10195

10296
If the function is not specific to `ecctl`, it should be part of [cloud-sdk-go](https://github.com/elastic/cloud-sdk-go) or a standalone repository if the functionality is big enough.
10397

@@ -107,40 +101,11 @@ If the function is not specific to `ecctl`, it should be part of [cloud-sdk-go](
107101

108102
All files containing functions or methods must have a corresponding unit test file, and we aim to have 100% coverage.
109103

110-
#### API Mocks
104+
#### Testing commands
111105

112-
When unit testing functions which will call the external API, please use the provided `api.NewMock` in conjunction with `mock.Response`.
106+
When writing unit tests for commands, we use the `testutils.RunCmdAssertion()` function which tests a `*cobra.Command` and uses the testing.T struct to return any unmatched assertions..
113107

114-
yes! :smile:
115-
116-
``` go
117-
import (
118-
"net/http"
119-
120-
"github.com/elastic/cloud-sdk-go/pkg/api"
121-
"github.com/elastic/cloud-sdk-go/pkg/api/mock"
122-
)
123-
124-
//Test case
125-
{
126-
name: "succeeds",
127-
args: args{params{
128-
API: api.NewMock(mock.Response{
129-
Response: http.Response{
130-
Body: mock.NewStringBody(`{}`),
131-
StatusCode: 200,
132-
},
133-
}),
134-
}},
135-
},
136-
// More tests ...
137-
```
138-
139-
### Testing commands
140-
141-
When writing unit tests for commands, we only look to assert that the command is constructing the correct API call. API responses are mocked and tested only in the `pkg/` directory.
142-
143-
See [TestRunShowKibanaClusterCmd()](./cmd/kibana/show_test.go) as a good example to base your tests on.
108+
See [Test_listCmd()](../cmd/deployment/template/list_test.go) as a good example to base your tests on.
144109

145110
## General Style
146111

@@ -179,33 +144,13 @@ Names should be descriptive and we should avoid redundancy.
179144
yes! :smile:
180145

181146
```go
182-
kibana.Create()
183-
```
184-
185-
preferably not :confused:
186-
187-
```go
188-
kibana.CreateKibanaDeployment()
189-
```
190-
191-
When using method chaining make sure to put each method in it's own line to improve readability.
192-
193-
yes! :smile:
194-
195-
```go
196-
res, err := a.API.V1API.ClustersApm.GetApmClusterPlanActivity(
197-
clusters_apm.NewGetApmClusterPlanActivityParams().
198-
WithClusterID(params.id).
199-
WithShowPlanDefaults(params.defaults),
200-
)
147+
ecctl.Get()
201148
```
202149

203150
preferably not :confused:
204151

205152
```go
206-
res, err := a.API.V1API.ClustersApm.GetApmClusterPlanActivity(
207-
clusters_apm.NewGetApmClusterPlanActivityParams().WithClusterID(params.id).WithShowPlanDefaults(params.defaults),
208-
)
153+
ecctl.GetEcctlInstance()
209154
```
210155

211156
When possible we try to avoid `else` and nested `if`s. This makes our code more readable and removes complexity.
@@ -241,7 +186,7 @@ if params.Hide {
241186

242187
We use `make docs` to automatically generate documentation for our commands which live in the `cmd` folder.
243188

244-
It is important when writing the descriptions to our commands or flags, that we use simple language and are as clear as possible to provide good UX. If you need to explain more about the command or give examples, please do so using the `Example` field, a good example is the [deployment elasticsearch list](cmd/deployment/elasticsearch/list.go) command.
189+
It is important when writing the descriptions to our commands or flags, that we use simple language and are as clear as possible to provide good UX. If you need to explain more about the command or give examples, please do so using the `Example` field, a good example is the [deployment list](../cmd/deployment/create.go) command.
245190

246191
The package wide description and documentation is provided in a godoc `doc.go` file. Aside form packages with a very small context, all packages should have this file.
247192

0 commit comments

Comments
 (0)