-
Notifications
You must be signed in to change notification settings - Fork 157
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create pipectl init for ECS #4741
Conversation
016f930
to
7f89bab
Compare
I'll clean unnecessary commits |
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## master #4741 +/- ##
==========================================
+ Coverage 30.87% 31.03% +0.15%
==========================================
Files 222 225 +3
Lines 26037 26257 +220
==========================================
+ Hits 8039 8148 +109
- Misses 17349 17458 +109
- Partials 649 651 +2 ☔ View full report in Codecov by Sentry. |
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
7f89bab
to
d877787
Compare
Done |
/review |
PR AnalysisMain themeEnhancement PR summaryThis PR introduces initialization commands to assist users in generating application configuration files for ECS applications in an interactive manner. Type of PREnhancement PR Feedback:General suggestions
Code feedback
Security concerns:No The changes seem to focus on configuration generation and input handling, which does not introduce security concerns such as SQL injection, XSS, CSRF, etc. |
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your great contribution!
I left a comment at first 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops... I forgot to add review comment.
re: comment🙏
pkg/app/pipectl/prompt/reader.go
Outdated
// Reader is an interface that reads input from user or mock. | ||
type Reader interface { | ||
// ReadString reads a string with showing message. If the input is empty, it returns an empty string. | ||
// If the input is more than one word, it returns an error. | ||
ReadString(message string) (string, error) | ||
// ReadStrings reads a string slice separated by blank space with showing message. If the input is empty, it returns an empty slice. | ||
ReadStrings(message string) ([]string, error) | ||
// ReadInt reads an integer with showing message. If the input is empty, it returns 0. | ||
// If the input is not an integer, it returns an error. | ||
ReadInt(message string) (int, error) | ||
// ReadStringRequired reads a string with showing message. If the input is empty, it asks again. | ||
// If number of retry exceeds maximum_retry, it returns an error. | ||
// If the input is more than one word, it returns an error. | ||
ReadStringRequired(message string) (string, error) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[ask] What's the purpose of the Interface?
I think reading data from stdin is a concrete usecase, so it might not be necessary abstraction 👀
WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The purpose is to use mock in initialize/ecs_test.go
.
In tests, stdin is difficult to use but we need to pass values instead of users.
So in tests I use mockReader
(in prompt/reader_mock.go
), which implements Reader
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thx, I got it!
So you can create prompt.NewReader(input io.Reader)
instead of NewStdinReader
and mock the input
👍
You can create the implementation of io.Reader
from a string using strings.NewReader
https://pkg.go.dev/strings#NewReader
Like this ↓
// default
r := prompt.NewReader(os.Stdin)
// when test
mockStr := `foo
foo bar
`
mockStdin := strings.NewReader(mockStr)
r := prompt.NewReader(mockStdin)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ffjlabo
great, amazing, thank you so much!
It worked well, and I could remove the Reader
interface!
strings.NewReader
is what i wanted.
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the fixes!
Added some more comments 🙏
// Automatically reverts all changes from all stages when one of them failed. | ||
// Default is true. | ||
AutoRollback *bool `json:"autoRollback,omitempty" default:"true"` | ||
// Run standalone task during deployment. | ||
// Default is true. | ||
RunStandaloneTask *bool `json:"runStandaloneTask" default:"true"` | ||
RunStandaloneTask *bool `json:"runStandaloneTask,omitempty" default:"true"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[must] look back whether to check the reference of the field is nil before using it for avoiding nil pointer exception.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I confirmed there's no preblem.
omitempty
affects only encoding to yaml/json, not decoding from yaml/json.
And when encoding,
- If
RunStandaloneTask
is not defined ingenericECSApplicationSpec.Input
,runStandaloneTask
won't appear in thepipectl init
output. - If
RunStandaloneTask
has null value,runStandaloneTask: false
will appear in thepipectl init
output.
@@ -0,0 +1,90 @@ | |||
// Copyright 2023 The PipeCD Authors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[must] plz update the copyright to the one of 2024
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I updated all
for _, tc := range testcases { | ||
t.Run(tc.name, func(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] add tc := tc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I fixed with adding T.Parallel()
switch platform { | ||
case "0": // Kubernetes | ||
// cfg, err = generateKubernetesConfig(in) | ||
panic("not implemented") | ||
case "1": // ECS | ||
cfg, err = generateECSConfig(reader) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[imo] Define identifier for the platform number as the const value for the readability of the code👍
const (
PlatformKubernetes string = "0"
PlatformECS string = "1"
)
switch platform {
case PlatformKubernetes:
case PlatformECS:
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, that's better
pkg/app/pipectl/prompt/reader.go
Outdated
func (r *Reader) ReadStringRequired(message string) (string, error) { | ||
for i := 0; i < maximum_retry; i++ { | ||
s, e := r.ReadString(message) | ||
if e != nil { | ||
return "", e | ||
} | ||
if len(s) > 0 { | ||
return s, nil | ||
} | ||
fmt.Printf("[WARN] This field is required. \n") | ||
} | ||
return "", fmt.Errorf("this field is required. Maximum retry(%d) exceeded", maximum_retry) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[imo] How about adding the argument required bool
to each method like ReadStrings
and ReadString
instead of preparing the method for required input.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I created prompt.Input.Required
spec, e := generateECSSpec(reader) | ||
if e != nil { | ||
return nil, e | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[imo] It would be nice to define necessary values first, and pass them to generateECSSpec
👀 WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I refactored
} | ||
|
||
func generateECSSpec(reader prompt.Reader) (*genericECSApplicationSpec, error) { | ||
appName, e := reader.ReadStringRequired("Name of the application: ") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[must] Use err
as the variable for error instead of e
. Go often uses err
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I fixed all
pkg/app/pipectl/prompt/reader.go
Outdated
// Reader reads input from user or mock. | ||
type Reader struct { | ||
bufReader bufio.Reader | ||
} | ||
|
||
func NewReader(in io.Reader) Reader { | ||
return Reader{ | ||
bufReader: *bufio.NewReader(in), | ||
} | ||
} | ||
|
||
// ReadString reads a string with showing message. If the input is empty, it returns an empty string. | ||
// If the input is more than one word, it returns an error. | ||
func (r *Reader) ReadString(message string) (string, error) { | ||
s, e := r.ReadStrings(message) | ||
if e != nil { | ||
return "", e | ||
} | ||
|
||
if len(s) == 0 { | ||
return "", nil | ||
} | ||
if len(s) > 1 { | ||
return "", fmt.Errorf("too many arguments") | ||
} | ||
|
||
return s[0], nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[imo] How about rename like below? WDYT?
Reader
->Prompt
ReadXX
->InputXX
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I renamed
Reader
->Prompt
ReadXX
-> removed and createdprompt.Input
struct.
{ | ||
name: "valid config for ECSApp", | ||
inputs: `myApp | ||
serviceDef.yaml | ||
taskDef.yaml | ||
arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/xxx/xxx | ||
web | ||
80 | ||
`, | ||
expectedFile: "testdata/ecs-app.yaml", | ||
expectedErr: nil, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[imo] Add testcase for failed case 👀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I added a failure case.
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
@ffjlabo Thank you for great reviewing. I fixed all |
pkg/app/pipectl/prompt/prompt.go
Outdated
type Prompt struct { | ||
bufReader bufio.Reader | ||
} | ||
|
||
type Input struct { | ||
Message string | ||
TargetPointer any | ||
Required bool | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] Add comments for these structs and fields 👍
t.Run(tc.name, func(t *testing.T) { | ||
strReader := strings.NewReader(tc.str) | ||
p := NewPrompt(strReader) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] Add t.Parallel
here :)
t.Run(tc.name, func(t *testing.T) { | ||
strReader := strings.NewReader(tc.str) | ||
p := NewPrompt(strReader) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] Add t.Parallel here :)
Same as above.
t.Run(tc.name, func(t *testing.T) { | ||
strReader := strings.NewReader(tc.str) | ||
p := NewPrompt(strReader) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] Add t.Parallel here :)
Same as above.
t.Run(tc.name, func(t *testing.T) { | ||
strReader := strings.NewReader(tc.str) | ||
p := NewPrompt(strReader) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] Add t.Parallel here :)
Same as above.
pkg/app/pipectl/prompt/prompt.go
Outdated
} | ||
|
||
// RunSlice sequentially asks for inputs and set the values to the target pointers. | ||
func (prompt *Prompt) RunSlice(inputs []Input) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[imo] It should change prompt
to p
to distinguish package name and receiver name 👍
const ( | ||
platformKubernetes string = "0" | ||
platformECS string = "1" | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] Add comments :)
@t-kikuc |
…ECS (#4746) Signed-off-by: Yoshiki Fujikane <[email protected]> Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
…tl-init-ecs Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: t-kikuc <[email protected]>
I got it. |
@ffjlabo |
Signed-off-by: t-kikuc <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! 🚀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's go 🚀
What this PR does / why we need it:
Create
pipectl init
command for ECS's simplest use case.I'll add Kubernetes next.
pipectl-init-ecs-1.mov
(x1.5 pace)
NOTE: This PR depends on #4737
Which issue(s) this PR fixes:
Part of #4677
Does this PR introduce a user-facing change?: no