Skip to content

Commit

Permalink
Create pipectl init for ECS (#4741)
Browse files Browse the repository at this point in the history
* Add pipectl init command but not implemented

Signed-off-by: t-kikuc <[email protected]>

* Modify init command

Signed-off-by: t-kikuc <[email protected]>

* Rename init command to initialize

Signed-off-by: t-kikuc <[email protected]>

* Create pipectl int for ECS

Signed-off-by: t-kikuc <[email protected]>

* Add init for ECS

Signed-off-by: t-kikuc <[email protected]>

* Convert json.RawMessage to struct

Signed-off-by: t-kikuc <[email protected]>

* Use original structs of ECS for pipectl init

Signed-off-by: t-kikuc <[email protected]>

* Init k8s app for pipectl init, but not implemented

Signed-off-by: t-kikuc <[email protected]>

* Fix init input(wip)

Signed-off-by: t-kikuc <[email protected]>

* Add mock prompt reader

Signed-off-by: t-kikuc <[email protected]>

* Reractor prompt reader

Signed-off-by: t-kikuc <[email protected]>

* Modify mock reader to public

Signed-off-by: t-kikuc <[email protected]>

* Refactored prompt reader

Signed-off-by: t-kikuc <[email protected]>

* Add yaml snapshot test

Signed-off-by: t-kikuc <[email protected]>

* Revert "Init k8s app for pipectl init, but not implemented"

This reverts commit 4097fb7.

Signed-off-by: t-kikuc <[email protected]>

* Refactor pipectl init

Signed-off-by: t-kikuc <[email protected]>

* Remove go-yaml from go.mod

Signed-off-by: t-kikuc <[email protected]>

* Modify tests to Parallel

Signed-off-by: t-kikuc <[email protected]>

* Fix reference of loop variable in tests

Signed-off-by: t-kikuc <[email protected]>

* Enable interruption signal for pipectl init

Signed-off-by: t-kikuc <[email protected]>

* Add LoadBalancerName to ECS TargetGroup

Signed-off-by: t-kikuc <[email protected]>

* Use Reader in tests

Signed-off-by: t-kikuc <[email protected]>

* Remove stdinReader

Signed-off-by: t-kikuc <[email protected]>

* Remove mockReader

Signed-off-by: t-kikuc <[email protected]>

* Add Prompt and PromptInput types

Signed-off-by: t-kikuc <[email protected]>

* Remove promptReader

Signed-off-by: t-kikuc <[email protected]>

* Refactored init,exporter

Signed-off-by: t-kikuc <[email protected]>

* Update Copyright

Signed-off-by: t-kikuc <[email protected]>

* Refactored nits

Signed-off-by: t-kikuc <[email protected]>

* Add reference to the blog that shows how to install control plane on ECS (#4746)

Signed-off-by: Yoshiki Fujikane <[email protected]>
Signed-off-by: t-kikuc <[email protected]>

* Bump follow-redirects from 1.15.2 to 1.15.4 in /web (#4747)

Signed-off-by: t-kikuc <[email protected]>

* Add RC Release Procedure (#4749)

Signed-off-by: t-kikuc <[email protected]>

* Fix nits

Signed-off-by: t-kikuc <[email protected]>

* Fix error variable name

Signed-off-by: t-kikuc <[email protected]>

---------

Signed-off-by: t-kikuc <[email protected]>
Signed-off-by: Yoshiki Fujikane <[email protected]>
Co-authored-by: Yoshiki Fujikane <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 19, 2024
1 parent 522a13b commit 0189b18
Show file tree
Hide file tree
Showing 10 changed files with 879 additions and 17 deletions.
2 changes: 2 additions & 0 deletions cmd/pipectl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/pipe-cd/pipecd/pkg/app/pipectl/cmd/deployment"
"github.com/pipe-cd/pipecd/pkg/app/pipectl/cmd/encrypt"
"github.com/pipe-cd/pipecd/pkg/app/pipectl/cmd/event"
"github.com/pipe-cd/pipecd/pkg/app/pipectl/cmd/initialize"
"github.com/pipe-cd/pipecd/pkg/app/pipectl/cmd/piped"
"github.com/pipe-cd/pipecd/pkg/app/pipectl/cmd/planpreview"
"github.com/pipe-cd/pipecd/pkg/app/pipectl/cmd/quickstart"
Expand All @@ -41,6 +42,7 @@ func main() {
piped.NewCommand(),
encrypt.NewCommand(),
quickstart.NewCommand(),
initialize.NewCommand(),
)

if err := app.Run(); err != nil {
Expand Down
99 changes: 99 additions & 0 deletions pkg/app/pipectl/cmd/initialize/ecs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2024 The PipeCD Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package initialize

import (
"github.com/pipe-cd/pipecd/pkg/app/pipectl/cmd/initialize/prompt"
"github.com/pipe-cd/pipecd/pkg/config"
)

// Use genericConfigs in order to simplify using the GenericApplicationSpec and keep the order as we want.
type genericECSApplicationSpec struct {
Name string `json:"name"`
Input config.ECSDeploymentInput `json:"input"`
Description string `json:"description,omitempty"`
}

func generateECSConfig(p prompt.Prompt) (*genericConfig, error) {
// inputs
var (
appName string
serviceDefFile string
taskDefFile string
targetGroupArn string
containerName string
containerPort int
)
inputs := []prompt.Input{
{
Message: "Name of the application",
TargetPointer: &appName,
Required: true,
},
{
Message: "Name of the service definition file (e.g. serviceDef.yaml)",
TargetPointer: &serviceDefFile,
Required: true,
},
{
Message: "Name of the task definition file (e.g. taskDef.yaml)",
TargetPointer: &taskDefFile,
Required: true,
},
// target group inputs
{
Message: "ARN of the target group to the service",
TargetPointer: &targetGroupArn,
Required: false,
},
{
Message: "Name of the container of the target group",
TargetPointer: &containerName,
Required: false,
},
{
Message: "Port number of the container of the target group",
TargetPointer: &containerPort,
Required: false,
},
}

err := p.RunSlice(inputs)
if err != nil {
return nil, err
}

spec := &genericECSApplicationSpec{
Name: appName,
Input: config.ECSDeploymentInput{
ServiceDefinitionFile: serviceDefFile,
TaskDefinitionFile: taskDefFile,
TargetGroups: config.ECSTargetGroups{
Primary: &config.ECSTargetGroup{
TargetGroupArn: targetGroupArn,
ContainerName: containerName,
ContainerPort: containerPort,
},
},
},
Description: "Generated by `pipectl init`. See https://pipecd.dev/docs/user-guide/configuration-reference/ for more.",
}

return &genericConfig{
Kind: config.KindECSApp,
APIVersion: config.VersionV1Beta1,
ApplicationSpec: spec,
}, nil
}
85 changes: 85 additions & 0 deletions pkg/app/pipectl/cmd/initialize/ecs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2024 The PipeCD Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package initialize

import (
"os"
"strings"
"testing"

"github.com/goccy/go-yaml"
"github.com/stretchr/testify/assert"

"github.com/pipe-cd/pipecd/pkg/app/pipectl/cmd/initialize/prompt"
"github.com/pipe-cd/pipecd/pkg/config"
)

func TestGenerateECSConfig(t *testing.T) {
t.Parallel()

testcases := []struct {
name string
inputs string // mock for user's input
expectedFile string
expectedErr bool
}{
{
name: "valid inputs",
inputs: `myApp
serviceDef.yaml
taskDef.yaml
arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/xxx/xxx
web
80
`,
expectedFile: "testdata/ecs-app.yaml",
expectedErr: false,
},
{
name: "missing required",
inputs: `myApp
serviceDef.yaml
`,
expectedFile: "",
expectedErr: true,
},
}

for _, tc := range testcases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
reader := strings.NewReader(tc.inputs)
prompt := prompt.NewPrompt(reader)

// Generate the config
cfg, err := generateECSConfig(prompt)
assert.Equal(t, tc.expectedErr, err != nil)

if err == nil {
// Compare the YAML output
yml, err := yaml.Marshal(cfg)
assert.NoError(t, err)
file, err := os.ReadFile(tc.expectedFile)
assert.NoError(t, err)
assert.Equal(t, string(file), string(yml))

// Check if the YAML output is compatible with the original Config model
_, err = config.DecodeYAML(yml)
assert.NoError(t, err)
}
})
}
}
58 changes: 58 additions & 0 deletions pkg/app/pipectl/cmd/initialize/exporter/exporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package exporter

import (
"fmt"
"os"

"github.com/pipe-cd/pipecd/pkg/app/pipectl/cmd/initialize/prompt"
)

// Export the bytes to the path.
// If the path is empty or a directory, return an error.
// If the file already exists, ask if overwrite it.
func Export(bytes []byte, path string) error {
if len(path) == 0 {
return fmt.Errorf("path is not specified. Please specify a file path")
}

// Check if the file/directory already exists
if fInfo, err := os.Stat(path); err == nil {
if fInfo.IsDir() {
// When the target is a directory.
return fmt.Errorf("the path %s is a directory. Please specify a file path", path)
}

// When the file exists, ask if overwrite it.
overwrite, err := askOverwrite()
if err != nil {
return fmt.Errorf("invalid input for overwrite(y/n): %v", err)
}

if !overwrite {
return fmt.Errorf("cancelled exporting")
}
}

fmt.Printf("Start exporting to %s\n", path)
err := os.WriteFile(path, bytes, 0644)
if err != nil {
return fmt.Errorf("failed to export to %s: %v", path, err)
} else {
fmt.Printf("Successfully exported to %s\n", path)
}
return nil
}

func askOverwrite() (overwrite bool, err error) {
overwriteInput := prompt.Input{
Message: "The file already exists. Overwrite it? [y/n]",
TargetPointer: &overwrite,
Required: false,
}
p := prompt.NewPrompt(os.Stdin)
err = p.Run(overwriteInput)
if err != nil {
return false, err
}
return overwrite, nil
}
Loading

0 comments on commit 0189b18

Please sign in to comment.