Skip to content
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

Cherry-pick #4451 #4457 #4462 #4463 #4464 #4465 #4466 #4469 #4482 #4485

2 changes: 1 addition & 1 deletion manifests/pipecd/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ spec:
{{- end }}
containers:
- name: mysql
image: mysql:{{ .Values.mysql.imageTag }}
image: {{ .Values.mysql.image }}:{{ .Values.mysql.imageTag }}
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_ROOT_PASSWORD
Expand Down
4 changes: 2 additions & 2 deletions manifests/pipecd/templates/managed-certificate.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{- if .Values.managedCertificate.enabled -}}
apiVersion: networking.gke.io/v1beta2
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: {{ include "pipecd.fullname" . }}
Expand All @@ -12,7 +12,7 @@ spec:

{{ if .Values.monitoring.managedCertificate.enabled -}}
---
apiVersion: networking.gke.io/v1beta2
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: {{ include "pipecd.fullname" . }}-grafana
Expand Down
3 changes: 2 additions & 1 deletion manifests/pipecd/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ cloudSQLProxy:
resources: {}

mysql:
imageTag: "8.0.23"
image: mysql
imageTag: 8.0.33
resources: {}
port: 3306

Expand Down
2 changes: 1 addition & 1 deletion manifests/site/templates/managed-certificate.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{- if .Values.managedCertificate.enabled -}}
apiVersion: networking.gke.io/v1beta2
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: {{ include "site.fullname" . }}
Expand Down
21 changes: 18 additions & 3 deletions pkg/app/pipectl/cmd/quickstart/quickstart.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ func (c *command) installControlPlane(ctx context.Context, helm string, input cl
"--create-namespace",
"--values",
fmt.Sprintf(helmQuickstartValueRemotePath, c.version),
"--set",
fmt.Sprintf("mysql.image=%s", selectMySQLImage()),
}

var stderr, stdout bytes.Buffer
Expand Down Expand Up @@ -312,6 +314,19 @@ func openbrowser(url string) error {
return err
}

func selectMySQLImage() string {
var mysqlImage string
switch runtime.GOARCH {
case "amd64":
mysqlImage = "mysql"
case "arm64":
mysqlImage = "arm64v8/mysql"
default:
mysqlImage = "mysql"
}
return mysqlImage
}

func (c *command) uninstallAll(ctx context.Context, helm string, input cli.Input) error {
input.Logger.Info("Uninstalling PipeCD components...")

Expand Down Expand Up @@ -380,9 +395,9 @@ func (c *command) getKubectl() (string, error) {
}

// getHelm finds and returns helm executable binary in the following priority:
// 1. pre-installed in command specified toolsDir (default is $HOME/.pipectl/tools)
// 2. $PATH
// 3. install new helm to command specified toolsDir
// 1. pre-installed in command specified toolsDir (default is $HOME/.pipectl/tools)
// 2. $PATH
// 3. install new helm to command specified toolsDir
func (c *command) getHelm(ctx context.Context) (string, error) {
binName := "helm"

Expand Down
8 changes: 4 additions & 4 deletions pkg/app/piped/executor/ecs/ecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,14 +409,14 @@ func routing(ctx context.Context, in *executor.Input, platformProviderName strin
in.Logger.Error("Failed to store traffic routing config to metadata store", zap.Error(err))
}

currListenerArn, err := client.GetListener(ctx, primaryTargetGroup)
currListenerArns, err := client.GetListenerArns(ctx, primaryTargetGroup)
if err != nil {
in.LogPersister.Errorf("Failed to get current active listener: %v", err)
in.LogPersister.Errorf("Failed to get current active listeners: %v", err)
return false
}

if err := client.ModifyListener(ctx, currListenerArn, routingTrafficCfg); err != nil {
in.LogPersister.Errorf("Failed to routing traffic to CANARY variant: %v", err)
if err := client.ModifyListeners(ctx, currListenerArns, routingTrafficCfg); err != nil {
in.LogPersister.Errorf("Failed to routing traffic to PRIMARY/CANARY variants: %v", err)
return false
}

Expand Down
37 changes: 33 additions & 4 deletions pkg/app/piped/executor/ecs/rollback.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,19 @@ func (e *rollbackExecutor) ensureRollback(ctx context.Context) model.StageStatus
return model.StageStatus_STAGE_FAILURE
}

primary, _, ok := loadTargetGroups(&e.Input, appCfg, runningDS)
primary, canary, ok := loadTargetGroups(&e.Input, appCfg, runningDS)
if !ok {
return model.StageStatus_STAGE_FAILURE
}

if !rollback(ctx, &e.Input, platformProviderName, platformProviderCfg, taskDefinition, serviceDefinition, primary) {
if !rollback(ctx, &e.Input, platformProviderName, platformProviderCfg, taskDefinition, serviceDefinition, primary, canary) {
return model.StageStatus_STAGE_FAILURE
}

return model.StageStatus_STAGE_SUCCESS
}

func rollback(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool {
func rollback(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, primaryTargetGroup *types.LoadBalancer, canaryTargetGroup *types.LoadBalancer) bool {
in.LogPersister.Infof("Start rollback the ECS service and task family: %s and %s to original stage", *serviceDefinition.ServiceName, *taskDefinition.Family)
client, err := provider.DefaultRegistry().Client(platformProviderName, platformProviderCfg, in.Logger)
if err != nil {
Expand Down Expand Up @@ -127,7 +127,7 @@ func rollback(ctx context.Context, in *executor.Input, platformProviderName stri
}

// On rolling back, the scale of desired tasks will be set to 100 (same as the original state).
taskSet, err := client.CreateTaskSet(ctx, *service, *td, targetGroup, 100)
taskSet, err := client.CreateTaskSet(ctx, *service, *td, primaryTargetGroup, 100)
if err != nil {
in.LogPersister.Errorf("Failed to create ECS task set %s: %v", *serviceDefinition.ServiceName, err)
return false
Expand All @@ -147,6 +147,35 @@ func rollback(ctx context.Context, in *executor.Input, platformProviderName stri
}
}

// Reset routing
routingTrafficCfg := provider.RoutingTrafficConfig{
{
TargetGroupArn: *primaryTargetGroup.TargetGroupArn,
Weight: 100,
},
{
TargetGroupArn: *canaryTargetGroup.TargetGroupArn,
Weight: 0,
},
}

currListenerArns, err := client.GetListenerArns(ctx, *primaryTargetGroup)
if err != nil {
in.LogPersister.Errorf("Failed to get current active listeners: %v", err)
return false
}

if err := client.ModifyListeners(ctx, currListenerArns, routingTrafficCfg); err != nil {
in.LogPersister.Errorf("Failed to routing traffic to PRIMARY variant: %v", err)
return false
}

// Delete Canary taskSet
if !clean(ctx, in, platformProviderName, platformProviderCfg) {
in.LogPersister.Error("Failed to delete CANARY TaskSet")
return false
}

in.LogPersister.Infof("Rolled back the ECS service %s and task definition %s configuration to original stage", *serviceDefinition.ServiceName, *taskDefinition.Family)
return true
}
64 changes: 37 additions & 27 deletions pkg/app/piped/platformprovider/ecs/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,29 +277,29 @@ func (c *client) ServiceExists(ctx context.Context, clusterName string, serviceN
return false, nil
}

func (c *client) GetListener(ctx context.Context, targetGroup types.LoadBalancer) (string, error) {
func (c *client) GetListenerArns(ctx context.Context, targetGroup types.LoadBalancer) ([]string, error) {
loadBalancerArn, err := c.getLoadBalancerArn(ctx, *targetGroup.TargetGroupArn)
if err != nil {
return "", err
return nil, err
}

input := &elasticloadbalancingv2.DescribeListenersInput{
LoadBalancerArn: aws.String(loadBalancerArn),
}
output, err := c.elbClient.DescribeListeners(ctx, input)
if err != nil {
return "", err
return nil, err
}
if len(output.Listeners) == 0 {
return "", platformprovider.ErrNotFound
return nil, platformprovider.ErrNotFound
}
// Note: Suppose the load balancer only have one listener.
// TODO: Support multi listeners pattern.
if len(output.Listeners) > 1 {
return "", fmt.Errorf("invalid listener configuration pointed to %s target group", *targetGroup.TargetGroupArn)

arns := make([]string, len(output.Listeners))
for i := range output.Listeners {
arns[i] = *output.Listeners[i].ListenerArn
}

return *output.Listeners[0].ListenerArn, nil
return arns, nil
}

func (c *client) getLoadBalancerArn(ctx context.Context, targetGroupArn string) (string, error) {
Expand All @@ -317,32 +317,42 @@ func (c *client) getLoadBalancerArn(ctx context.Context, targetGroupArn string)
return output.TargetGroups[0].LoadBalancerArns[0], nil
}

func (c *client) ModifyListener(ctx context.Context, listenerArn string, routingTrafficCfg RoutingTrafficConfig) error {
func (c *client) ModifyListeners(ctx context.Context, listenerArns []string, routingTrafficCfg RoutingTrafficConfig) error {
if len(routingTrafficCfg) != 2 {
return fmt.Errorf("invalid listener configuration: requires 2 target groups")
}
input := &elasticloadbalancingv2.ModifyListenerInput{
ListenerArn: aws.String(listenerArn),
DefaultActions: []elbtypes.Action{
{
Type: elbtypes.ActionTypeEnumForward,
ForwardConfig: &elbtypes.ForwardActionConfig{
TargetGroups: []elbtypes.TargetGroupTuple{
{
TargetGroupArn: aws.String(routingTrafficCfg[0].TargetGroupArn),
Weight: aws.Int32(int32(routingTrafficCfg[0].Weight)),
},
{
TargetGroupArn: aws.String(routingTrafficCfg[1].TargetGroupArn),
Weight: aws.Int32(int32(routingTrafficCfg[1].Weight)),

modifyListener := func(ctx context.Context, listenerArn string) error {
input := &elasticloadbalancingv2.ModifyListenerInput{
ListenerArn: aws.String(listenerArn),
DefaultActions: []elbtypes.Action{
{
Type: elbtypes.ActionTypeEnumForward,
ForwardConfig: &elbtypes.ForwardActionConfig{
TargetGroups: []elbtypes.TargetGroupTuple{
{
TargetGroupArn: aws.String(routingTrafficCfg[0].TargetGroupArn),
Weight: aws.Int32(int32(routingTrafficCfg[0].Weight)),
},
{
TargetGroupArn: aws.String(routingTrafficCfg[1].TargetGroupArn),
Weight: aws.Int32(int32(routingTrafficCfg[1].Weight)),
},
},
},
},
},
},
}
_, err := c.elbClient.ModifyListener(ctx, input)
return err
}

for _, listener := range listenerArns {
if err := modifyListener(ctx, listener); err != nil {
return err
}
}
_, err := c.elbClient.ModifyListener(ctx, input)
return err
return nil
}

func (c *client) TagResource(ctx context.Context, resourceArn string, tags []types.Tag) error {
Expand Down
4 changes: 2 additions & 2 deletions pkg/app/piped/platformprovider/ecs/ecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ type ECS interface {
}

type ELB interface {
GetListener(ctx context.Context, targetGroup types.LoadBalancer) (string, error)
ModifyListener(ctx context.Context, listenerArn string, routingTrafficCfg RoutingTrafficConfig) error
GetListenerArns(ctx context.Context, targetGroup types.LoadBalancer) ([]string, error)
ModifyListeners(ctx context.Context, listenerArns []string, routingTrafficCfg RoutingTrafficConfig) error
}

// Registry holds a pool of aws client wrappers.
Expand Down
12 changes: 6 additions & 6 deletions pkg/app/piped/platformprovider/kubernetes/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package kubernetes

import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
Expand Down Expand Up @@ -90,13 +91,12 @@ func (l *loader) LoadManifests(ctx context.Context) (manifests []Manifest, err e
sortManifests(manifests)
}()
l.initOnce.Do(func() {
var initErrorHelm, initErrorKustomize error
l.templatingMethod = determineTemplatingMethod(l.input, l.appDir)
switch l.templatingMethod {
case TemplatingMethodHelm:
l.helm, l.initErr = l.findHelm(ctx, l.input.HelmVersion)

case TemplatingMethodKustomize:
l.kustomize, l.initErr = l.findKustomize(ctx, l.input.KustomizeVersion)
if l.templatingMethod != TemplatingMethodNone {
l.helm, initErrorHelm = l.findHelm(ctx, l.input.HelmVersion)
l.kustomize, initErrorKustomize = l.findKustomize(ctx, l.input.KustomizeVersion)
l.initErr = errors.Join(initErrorHelm, initErrorKustomize)
}
})
if l.initErr != nil {
Expand Down
9 changes: 9 additions & 0 deletions pkg/cli/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package cli

import (
"errors"
"fmt"

"github.com/spf13/cobra"
Expand All @@ -29,15 +30,23 @@ type App struct {
telemetryFlags TelemetryFlags
}

var FlagParseErr = errors.New("FlagParseErr")

func NewApp(name, desc string) *App {
a := &App{
rootCmd: &cobra.Command{
Use: name,
Short: desc,
SilenceErrors: true,
SilenceUsage: true,
},
telemetryFlags: defaultTelemetryFlags,
}
a.rootCmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
cmd.Println(err)
cmd.Println(cmd.UsageString())
return FlagParseErr
})
versionCmd := &cobra.Command{
Use: "version",
Short: "Print the information of current binary.",
Expand Down
2 changes: 1 addition & 1 deletion web/custom-jsdom.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const Environment = require("jest-environment-jsdom");

module.exports = class CustomTestEnvironment extends Environment {
module.exports = class CustomTestEnvironment extends Environment.default {
async setup() {
await super.setup();
if (typeof this.global.TextEncoder === "undefined") {
Expand Down
4 changes: 3 additions & 1 deletion web/file-transformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const path = require("path");

module.exports = {
process(src, filename, config, options) {
return "module.exports = " + JSON.stringify(path.basename(filename)) + ";";
return {
code: `module.exports = ${JSON.stringify(path.basename(filename))};`,
};
},
};
12 changes: 6 additions & 6 deletions web/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
module.exports = {
roots: ["<rootDir>/src"],
transform: {
"^.+\\.tsx?$": "ts-jest",
"^.+\\.tsx?$": [
"ts-jest",
{
isolatedModules: true,
},
],
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|ico)$":
"<rootDir>/file-transformer.js",
},
Expand All @@ -23,9 +28,4 @@ module.exports = {
setupFilesAfterEnv: ["./jest.after-env.ts"],
coverageReporters: ["lcovonly", "text-summary"],
maxWorkers: 1,
globals: {
"ts-jest": {
isolatedModules: true,
},
},
};
Loading