github.com/containerd/nerdctl/v2@v2.0.0-beta.5.0.20240520001846-b5758f54fa28/pkg/cmd/container/run_restart.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package container
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  
    24  	"github.com/containerd/containerd"
    25  	"github.com/containerd/containerd/runtime/restart"
    26  	"github.com/containerd/nerdctl/v2/pkg/strutil"
    27  )
    28  
    29  func checkRestartCapabilities(ctx context.Context, client *containerd.Client, restartFlag string) (bool, error) {
    30  	policySlice := strings.Split(restartFlag, ":")
    31  	switch policySlice[0] {
    32  	case "", "no":
    33  		return true, nil
    34  	}
    35  	res, err := client.IntrospectionService().Plugins(ctx, []string{"id==restart"})
    36  	if err != nil {
    37  		return false, err
    38  	}
    39  	if len(res.Plugins) == 0 {
    40  		return false, fmt.Errorf("no restart plugin found")
    41  	}
    42  	restartPlugin := res.Plugins[0]
    43  	capabilities := restartPlugin.Capabilities
    44  	if len(capabilities) == 0 {
    45  		capabilities = []string{"always"}
    46  	}
    47  	if !strutil.InStringSlice(capabilities, policySlice[0]) {
    48  		return false, fmt.Errorf("unsupported restart policy %q, supported policies are: %q", policySlice[0], capabilities)
    49  	}
    50  	return true, nil
    51  }
    52  
    53  func generateRestartOpts(ctx context.Context, client *containerd.Client, restartFlag, logURI string, inRun bool) ([]containerd.NewContainerOpts, error) {
    54  	if restartFlag == "" || restartFlag == "no" {
    55  		return nil, nil
    56  	}
    57  	if _, err := checkRestartCapabilities(ctx, client, restartFlag); err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	policy, err := restart.NewPolicy(restartFlag)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	desireStatus := containerd.Created
    66  	if inRun {
    67  		desireStatus = containerd.Running
    68  	}
    69  	opts := []containerd.NewContainerOpts{restart.WithPolicy(policy), restart.WithStatus(desireStatus)}
    70  	if logURI != "" {
    71  		opts = append(opts, restart.WithLogURIString(logURI))
    72  	}
    73  	return opts, nil
    74  }
    75  
    76  // UpdateContainerRestartPolicyLabel updates the restart policy label of the container.
    77  func UpdateContainerRestartPolicyLabel(ctx context.Context, client *containerd.Client, container containerd.Container, restartFlag string) error {
    78  	if _, err := checkRestartCapabilities(ctx, client, restartFlag); err != nil {
    79  		return err
    80  	}
    81  	policy, err := restart.NewPolicy(restartFlag)
    82  	if err != nil {
    83  		return err
    84  	}
    85  
    86  	updateOpts := []containerd.UpdateContainerOpts{restart.WithPolicy(policy)}
    87  
    88  	lables, err := container.Labels(ctx)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	_, statusLabelExist := lables[restart.StatusLabel]
    93  	if !statusLabelExist {
    94  		task, err := container.Task(ctx, nil)
    95  		if err != nil {
    96  			return fmt.Errorf("failed to get task:%w", err)
    97  		}
    98  		desireStatus := containerd.Running
    99  		status, err := task.Status(ctx)
   100  		if err == nil {
   101  			switch status.Status {
   102  			case containerd.Stopped:
   103  				desireStatus = containerd.Stopped
   104  			case containerd.Created:
   105  				desireStatus = containerd.Created
   106  			}
   107  		}
   108  		updateOpts = append(updateOpts, restart.WithStatus(desireStatus))
   109  	}
   110  
   111  	return container.Update(ctx, updateOpts...)
   112  }