Browse Source

support waiting for update-image call (#4141)

d-g-town 2 years ago
parent
commit
f3df2092d4

+ 28 - 0
api/server/handlers/porter_app/get_app_revision_status.go

@@ -35,9 +35,23 @@ func NewGetAppRevisionStatusHandler(
 	}
 }
 
+// HighLevelStatus is a high level status that can be used to determine whether the revisions is progressing, successful or failed
+type HighLevelStatus string
+
+const (
+	// HighLevelStatus_Progressing indicates that the revision is still in progress
+	HighLevelStatus_Progressing HighLevelStatus = "progressing"
+	// HighLevelStatus_Successful indicates that the revision has completed successfully
+	HighLevelStatus_Successful HighLevelStatus = "successful"
+	// HighLevelStatus_Failed indicates that the revision has failed
+	HighLevelStatus_Failed HighLevelStatus = "failed"
+)
+
 // GetAppRevisionStatusResponse represents the response from the /apps/{porter_app_name}/revisions/{app_revision_id}/status endpoint
 type GetAppRevisionStatusResponse struct {
 	AppRevisionStatus porter_app.RevisionProgress `json:"app_revision_status"`
+	// HighLevelStatus is a high level status that can be used to determine whether the revisions is progressing, successful or failed
+	HighLevelStatus HighLevelStatus `json:"status"`
 }
 
 // GetAppRevisionStatusHandler returns the status of an app revision
@@ -84,8 +98,22 @@ func (c *GetAppRevisionStatusHandler) ServeHTTP(w http.ResponseWriter, r *http.R
 		IsInTerminalStatus:   ccpResp.Msg.IsInTerminalStatus,
 	}
 
+	statusTransform := map[porterv1.EnumAppRevisionStatus]HighLevelStatus{
+		porterv1.EnumAppRevisionStatus_ENUM_APP_REVISION_STATUS_PROGRESSING: HighLevelStatus_Progressing,
+		porterv1.EnumAppRevisionStatus_ENUM_APP_REVISION_STATUS_SUCCESSFUL:  HighLevelStatus_Successful,
+		porterv1.EnumAppRevisionStatus_ENUM_APP_REVISION_STATUS_FAILED:      HighLevelStatus_Failed,
+	}
+
+	status, ok := statusTransform[ccpResp.Msg.Status]
+	if !ok {
+		err = telemetry.Error(ctx, span, nil, "unsupported revision status status")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+		return
+	}
+
 	res := &GetAppRevisionStatusResponse{
 		AppRevisionStatus: revisionStatus,
+		HighLevelStatus:   status,
 	}
 
 	c.WriteResult(w, r, res)

+ 2 - 0
api/server/handlers/porter_app/update_image.go

@@ -46,6 +46,7 @@ type UpdateImageRequest struct {
 type UpdateImageResponse struct {
 	Repository string `json:"repository"`
 	Tag        string `json:"tag"`
+	RevisionID string `json:"revision_id"`
 }
 
 func (c *UpdateImageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -102,6 +103,7 @@ func (c *UpdateImageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	res := &UpdateImageResponse{
 		Repository: ccpResp.Msg.RepositoryUrl,
 		Tag:        ccpResp.Msg.Tag,
+		RevisionID: ccpResp.Msg.RevisionId,
 	}
 
 	c.WriteResult(w, r, res)

+ 28 - 12
cli/cmd/commands/app.go

@@ -35,16 +35,17 @@ import (
 )
 
 var (
-	appNamespace     string
-	appVerbose       bool
-	appExistingPod   bool
-	appInteractive   bool
-	appContainerName string
-	appTag           string
-	deploymentTarget string
-	appCpuMilli      int
-	appMemoryMi      int
-	jobName          string
+	appNamespace            string
+	appVerbose              bool
+	appExistingPod          bool
+	appInteractive          bool
+	appContainerName        string
+	appTag                  string
+	deploymentTarget        string
+	appCpuMilli             int
+	appMemoryMi             int
+	jobName                 string
+	waitForSuccessfulUpdate bool
 )
 
 const (
@@ -110,6 +111,14 @@ func registerCommand_App(cliConf config.CLIConfig) *cobra.Command {
 		},
 	}
 
+	appUpdateTagCmd.PersistentFlags().BoolVarP(
+		&waitForSuccessfulUpdate,
+		"wait",
+		"w",
+		false,
+		"set this to wait and be notified when an update is successful, otherwise time out",
+	)
+
 	appUpdateTagCmd.PersistentFlags().StringVarP(
 		&appTag,
 		"tag",
@@ -1249,11 +1258,18 @@ func appUpdateTag(ctx context.Context, user *types.GetAuthenticatedUserResponse,
 	}
 
 	if project.ValidateApplyV2 {
-		tag, err := v2.UpdateImage(ctx, appTag, client, cliConf.Project, cliConf.Cluster, args[0], deploymentTarget)
+		err := v2.UpdateImage(ctx, v2.UpdateImageInput{
+			ProjectID:               cliConf.Project,
+			ClusterID:               cliConf.Cluster,
+			AppName:                 args[0],
+			DeploymentTargetName:    deploymentTarget,
+			Tag:                     appTag,
+			WaitForSuccessfulUpdate: waitForSuccessfulUpdate,
+			Client:                  client,
+		})
 		if err != nil {
 			return fmt.Errorf("error updating tag: %w", err)
 		}
-		_, _ = color.New(color.FgGreen).Printf("Successfully updated application %s to use tag \"%s\"\n", args[0], tag)
 		return nil
 	} else {
 		namespace := fmt.Sprintf("porter-stack-%s", args[0])

+ 78 - 6
cli/cmd/v2/update_image.go

@@ -4,24 +4,96 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"time"
+
+	"github.com/porter-dev/porter/api/server/handlers/porter_app"
+
+	"github.com/fatih/color"
 
 	api "github.com/porter-dev/porter/api/client"
 )
 
+// UpdateImageInput is the input for the UpdateImage function
+type UpdateImageInput struct {
+	ProjectID               uint
+	ClusterID               uint
+	AppName                 string
+	DeploymentTargetName    string
+	Tag                     string
+	WaitForSuccessfulUpdate bool
+	Client                  api.Client
+}
+
+const (
+	// DefaultWaitTimeoutMinutes is the default timeout for waiting for an update-image to complete
+	DefaultWaitTimeoutMinutes = 10
+	// DefaultRetryFrequencySeconds is the default frequency for checking the status of an update-image
+	DefaultRetryFrequencySeconds = 10
+)
+
 // UpdateImage updates the image of an application
-func UpdateImage(ctx context.Context, tag string, client api.Client, projectId, clusterId uint, appName string, deploymentTargetName string) (string, error) {
-	if deploymentTargetName == "" {
-		return "", errors.New("please provide a deployment target")
+func UpdateImage(ctx context.Context, input UpdateImageInput) error {
+	if input.DeploymentTargetName == "" {
+		return errors.New("please provide a deployment target")
 	}
 
+	tag := input.Tag
 	if tag == "" {
 		tag = "latest"
 	}
 
-	resp, err := client.UpdateImage(ctx, projectId, clusterId, appName, deploymentTargetName, tag)
+	resp, err := input.Client.UpdateImage(ctx, input.ProjectID, input.ClusterID, input.AppName, input.DeploymentTargetName, tag)
 	if err != nil {
-		return "", fmt.Errorf("unable to update image: %w", err)
+		return fmt.Errorf("unable to update image: %w", err)
+	}
+
+	triggeredBackgroundColor := color.FgGreen
+	if input.WaitForSuccessfulUpdate {
+		triggeredBackgroundColor = color.FgBlue
+	}
+
+	_, _ = color.New(triggeredBackgroundColor).Printf("Updated application %s to use tag \"%s\"\n", input.AppName, tag)
+
+	if !input.WaitForSuccessfulUpdate {
+		return nil
 	}
 
-	return resp.Tag, nil
+	timeoutMinutes := DefaultWaitTimeoutMinutes
+	timeout := time.Duration(timeoutMinutes) * time.Minute
+	deadline := time.Now().Add(timeout)
+
+	color.New(color.FgBlue).Printf("Waiting %d minutes for update to complete\n", timeoutMinutes) // nolint:errcheck,gosec
+
+	var status porter_app.HighLevelStatus
+
+	for time.Now().Before(deadline) {
+		statusResp, err := input.Client.GetRevisionStatus(ctx, input.ProjectID, input.ClusterID, input.AppName, resp.RevisionID)
+		if err != nil {
+			return fmt.Errorf("error getting app revision status: %w", err)
+		}
+
+		if statusResp == nil {
+			return errors.New("unable to determine status of app revision")
+		}
+
+		status = statusResp.HighLevelStatus
+
+		if status != porter_app.HighLevelStatus_Progressing {
+			break
+		}
+
+		time.Sleep(DefaultRetryFrequencySeconds * time.Second)
+	}
+
+	switch status {
+	case porter_app.HighLevelStatus_Progressing:
+		return fmt.Errorf("timeout exceeded")
+	case porter_app.HighLevelStatus_Successful:
+		_, _ = color.New(color.FgGreen).Printf("Update completed successfully\n") // nolint:errcheck,gosec
+		return nil
+	case porter_app.HighLevelStatus_Failed:
+		return fmt.Errorf("update failed: check dashboard for details")
+	default:
+		return fmt.Errorf("received unknown status: %s", status)
+	}
 }

+ 5 - 5
go.mod

@@ -83,7 +83,7 @@ require (
 	github.com/matryer/is v1.4.0
 	github.com/nats-io/nats.go v1.24.0
 	github.com/open-policy-agent/opa v0.44.0
-	github.com/porter-dev/api-contracts v0.2.84
+	github.com/porter-dev/api-contracts v0.2.85
 	github.com/riandyrn/otelchi v0.5.1
 	github.com/santhosh-tekuri/jsonschema/v5 v5.0.1
 	github.com/stefanmcshane/helm v0.0.0-20221213002717-88a4a2c6e77d
@@ -226,7 +226,7 @@ require (
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 	github.com/docker/go-metrics v0.0.1 // indirect
 	github.com/docker/go-units v0.4.0 // indirect
-	github.com/dustin/go-humanize v1.0.0 // indirect
+	github.com/dustin/go-humanize v1.0.1 // indirect
 	github.com/emirpasic/gods v1.18.1 // indirect
 	github.com/evanphx/json-patch v5.6.0+incompatible // indirect
 	github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
@@ -291,8 +291,8 @@ require (
 	github.com/magiconair/properties v1.8.5 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-colorable v0.1.12 // indirect
-	github.com/mattn/go-isatty v0.0.16 // indirect
-	github.com/mattn/go-runewidth v0.0.13 // indirect
+	github.com/mattn/go-isatty v0.0.18 // indirect
+	github.com/mattn/go-runewidth v0.0.15 // indirect
 	github.com/mattn/go-sqlite3 v1.14.16 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
@@ -346,7 +346,7 @@ require (
 	go.starlark.net v0.0.0-20220328144851-d1966c6b9fcd // indirect
 	golang.org/x/mod v0.8.0 // indirect
 	golang.org/x/sync v0.2.0
-	golang.org/x/sys v0.11.0 // indirect
+	golang.org/x/sys v0.12.0 // indirect
 	golang.org/x/term v0.11.0 // indirect
 	golang.org/x/text v0.12.0 // indirect
 	golang.org/x/time v0.3.0 // indirect

+ 11 - 7
go.sum

@@ -558,8 +558,9 @@ github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNE
 github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
 github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
 github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
 github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f h1:8GDPb0tCY8LQ+OJ3dbHb5sA6YZWXFORQYZx5sdsTlMs=
@@ -1279,16 +1280,18 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME
 github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
+github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
+github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@@ -1520,8 +1523,8 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw=
-github.com/porter-dev/api-contracts v0.2.84 h1:HaqhaNgCR8ll86ExczPr6+eTIIYfNUujwVV2GXha9rQ=
-github.com/porter-dev/api-contracts v0.2.84/go.mod h1:fX6JmP5QuzxDLvqP3evFOTXjI4dHxsG0+VKNTjImZU8=
+github.com/porter-dev/api-contracts v0.2.85 h1:9GakgkflrPiuH9R+/zG5NFlIGL7GjzX1OS4FfhO5qqQ=
+github.com/porter-dev/api-contracts v0.2.85/go.mod h1:fX6JmP5QuzxDLvqP3evFOTXjI4dHxsG0+VKNTjImZU8=
 github.com/porter-dev/switchboard v0.0.3 h1:dBuYkiVLa5Ce7059d6qTe9a1C2XEORFEanhbtV92R+M=
 github.com/porter-dev/switchboard v0.0.3/go.mod h1:xSPzqSFMQ6OSbp42fhCi4AbGbQbsm6nRvOkrblFeXU4=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
@@ -2269,9 +2272,10 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
-golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
+golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

+ 7 - 1
go.work.sum

@@ -418,6 +418,8 @@ github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb
 github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
 github.com/ashanbrown/forbidigo v1.2.0 h1:RMlEFupPCxQ1IogYOQUnIQwGEUGK8g5vAPMRyJoSxbc=
 github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde h1:YOsoVXsZQPA9aOTy1g0lAJv5VzZUvwQuZqug8XPeqfM=
+github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
+github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
 github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo=
 github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
 github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible h1:Ppm0npCCsmuR9oQaBtRuZcmILVE74aXE+AmrJj8L2ns=
@@ -444,6 +446,8 @@ github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMr
 github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
 github.com/charithe/durationcheck v0.0.9 h1:mPP4ucLrf/rKZiIG/a9IPXHGlh8p4CzgpyTy6EEutYk=
 github.com/charmbracelet/glamour v0.3.0 h1:3H+ZrKlSg8s+WU6V7eF2eRVYt8lCueffbi7r2+ffGkc=
+github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
+github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
 github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af h1:spmv8nSH9h5oCQf40jt/ufBCt9j0/58u4G+rkeMqXGI=
 github.com/checkpoint-restore/go-criu/v4 v4.1.0 h1:WW2B2uxx9KWF6bGlHqhm8Okiafwwx7Y2kcpn8lCpjgo=
 github.com/checkpoint-restore/go-criu/v5 v5.3.0 h1:wpFFOoomK3389ue2lAb0Boag6XPht5QYpipxmSNL4d8=
@@ -874,6 +878,8 @@ github.com/ryanuber/columnize v2.1.0+incompatible h1:j1Wcmh8OrK4Q7GXY+V7SVSY8nUW
 github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U=
 github.com/sagikazarmark/crypt v0.3.0 h1:TV5DVog+pihN4Rr0rN1IClv4ePpkzdg9sPrw7WDofZ8=
 github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
+github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y=
+github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
 github.com/sanposhiho/wastedassign/v2 v2.0.6 h1:+6/hQIHKNJAUixEj6EmOngGIisyeI+T3335lYTyxRoA=
 github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=
@@ -1024,8 +1030,8 @@ golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4
 golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=