diff options
Diffstat (limited to 'pkg/domain/infra')
-rw-r--r-- | pkg/domain/infra/abi/manifest.go | 3 | ||||
-rw-r--r-- | pkg/domain/infra/abi/play.go | 79 | ||||
-rw-r--r-- | pkg/domain/infra/abi/play_utils.go | 16 | ||||
-rw-r--r-- | pkg/domain/infra/abi/play_utils_test.go | 38 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/manifest.go | 2 |
5 files changed, 132 insertions, 6 deletions
diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index 4b10d9b18..e0c11267e 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -321,6 +321,7 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin pushOptions.SignBySigstorePrivateKeyFile = opts.SignBySigstorePrivateKeyFile pushOptions.SignSigstorePrivateKeyPassphrase = opts.SignSigstorePrivateKeyPassphrase pushOptions.InsecureSkipTLSVerify = opts.SkipTLSVerify + pushOptions.Writer = opts.Writer compressionFormat := opts.CompressionFormat if compressionFormat == "" { @@ -341,7 +342,7 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination strin if opts.All { pushOptions.ImageListSelection = cp.CopyAllImages } - if !opts.Quiet { + if !opts.Quiet && pushOptions.Writer == nil { pushOptions.Writer = os.Stderr } diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 3f2fd5f92..faa89cc26 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -27,13 +27,19 @@ import ( "github.com/containers/podman/v4/pkg/specgen/generate" "github.com/containers/podman/v4/pkg/specgen/generate/kube" "github.com/containers/podman/v4/pkg/specgenutil" + "github.com/containers/podman/v4/pkg/systemd/notifyproxy" "github.com/containers/podman/v4/pkg/util" + "github.com/coreos/go-systemd/v22/daemon" "github.com/ghodss/yaml" "github.com/opencontainers/go-digest" "github.com/sirupsen/logrus" yamlv3 "gopkg.in/yaml.v3" ) +// sdNotifyAnnotation allows for configuring service-global and +// container-specific sd-notify modes. +const sdNotifyAnnotation = "io.containers.sdnotify" + // createServiceContainer creates a container that can later on // be associated with the pods of a K8s yaml. It will be started along with // the first pod. @@ -73,7 +79,12 @@ func (ic *ContainerEngine) createServiceContainer(ctx context.Context, name stri return nil, fmt.Errorf("creating runtime spec for service container: %w", err) } opts = append(opts, libpod.WithIsService()) - opts = append(opts, libpod.WithSdNotifyMode(define.SdNotifyModeConmon)) + + // Set the sd-notify mode to "ignore". Podman is responsible for + // sending the notify messages when all containers are ready. + // The mode for individual containers or entire pods can be configured + // via the `sdNotifyAnnotation` annotation in the K8s YAML. + opts = append(opts, libpod.WithSdNotifyMode(define.SdNotifyModeIgnore)) // Create a new libpod container based on the spec. ctr, err := ic.Libpod.NewContainer(ctx, runtimeSpec, spec, false, opts...) @@ -96,6 +107,10 @@ func k8sName(content []byte, suffix string) string { } func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options entities.PlayKubeOptions) (_ *entities.PlayKubeReport, finalErr error) { + if options.ServiceContainer && options.Start == types.OptionalBoolFalse { // Sanity check to be future proof + return nil, fmt.Errorf("running a service container requires starting the pod(s)") + } + report := &entities.PlayKubeReport{} validKinds := 0 @@ -121,6 +136,8 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options var configMaps []v1.ConfigMap + ranContainers := false + var serviceContainer *libpod.Container // create pod on each document if it is a pod or deployment // any other kube kind will be skipped for _, document := range documentList { @@ -130,8 +147,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options } // TODO: create constants for the various "kinds" of yaml files. - var serviceContainer *libpod.Container - if options.ServiceContainer && (kind == "Pod" || kind == "Deployment") { + if options.ServiceContainer && serviceContainer == nil && (kind == "Pod" || kind == "Deployment") { ctr, err := ic.createServiceContainer(ctx, k8sName(content, "service"), options) if err != nil { return nil, err @@ -178,6 +194,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options report.Pods = append(report.Pods, r.Pods...) validKinds++ + ranContainers = true case "Deployment": var deploymentYAML v1apps.Deployment @@ -192,6 +209,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options report.Pods = append(report.Pods, r.Pods...) validKinds++ + ranContainers = true case "PersistentVolumeClaim": var pvcYAML v1.PersistentVolumeClaim @@ -239,6 +257,20 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options return nil, fmt.Errorf("YAML document does not contain any supported kube kind") } + if options.ServiceContainer && ranContainers { + // We can consider the service to be up and running now. + // Send the sd-notify messages pointing systemd to the + // service container. + data, err := serviceContainer.Inspect(false) + if err != nil { + return nil, err + } + message := fmt.Sprintf("MAINPID=%d\n%s", data.State.ConmonPid, daemon.SdNotifyReady) + if err := notifyproxy.SendMessage("", message); err != nil { + return nil, err + } + } + return report, nil } @@ -280,6 +312,11 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY report entities.PlayKubeReport ) + mainSdNotifyMode, err := getSdNotifyMode(annotations, "") + if err != nil { + return nil, err + } + // Create the secret manager before hand secretsManager, err := ic.Libpod.SecretsManager() if err != nil { @@ -562,6 +599,9 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY initContainers = append(initContainers, ctr) } + + var sdNotifyProxies []*notifyproxy.NotifyProxy // containers' sd-notify proxies + for _, container := range podYAML.Spec.Containers { // Error out if the same name is used for more than one container if _, ok := ctrNames[container.Name]; ok { @@ -606,7 +646,31 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY if err != nil { return nil, err } - opts = append(opts, libpod.WithSdNotifyMode(define.SdNotifyModeIgnore)) + + sdNotifyMode := mainSdNotifyMode + ctrNotifyMode, err := getSdNotifyMode(annotations, container.Name) + if err != nil { + return nil, err + } + if ctrNotifyMode != "" { + sdNotifyMode = ctrNotifyMode + } + if sdNotifyMode == "" { // Default to "ignore" + sdNotifyMode = define.SdNotifyModeIgnore + } + + opts = append(opts, libpod.WithSdNotifyMode(sdNotifyMode)) + + // Create a notify proxy for the container. + if sdNotifyMode != "" && sdNotifyMode != define.SdNotifyModeIgnore { + proxy, err := notifyproxy.New("") + if err != nil { + return nil, err + } + sdNotifyProxies = append(sdNotifyProxies, proxy) + opts = append(opts, libpod.WithSdNotifySocket(proxy.SocketPath())) + } + ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, opts...) if err != nil { return nil, err @@ -624,6 +688,13 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY playKubePod.ContainerErrors = append(playKubePod.ContainerErrors, fmt.Errorf("error starting container %s: %w", id, err).Error()) fmt.Println(playKubePod.ContainerErrors) } + + // Wait for each proxy to receive a READY message. + for _, proxy := range sdNotifyProxies { + if err := proxy.WaitAndClose(); err != nil { + return nil, err + } + } } playKubePod.ID = pod.ID() diff --git a/pkg/domain/infra/abi/play_utils.go b/pkg/domain/infra/abi/play_utils.go new file mode 100644 index 000000000..482a158e6 --- /dev/null +++ b/pkg/domain/infra/abi/play_utils.go @@ -0,0 +1,16 @@ +package abi + +import "github.com/containers/podman/v4/libpod/define" + +// getSdNotifyMode returns the `sdNotifyAnnotation/$name` for the specified +// name. If name is empty, it'll only look for `sdNotifyAnnotation`. +func getSdNotifyMode(annotations map[string]string, name string) (string, error) { + var mode string + switch len(name) { + case 0: + mode = annotations[sdNotifyAnnotation] + default: + mode = annotations[sdNotifyAnnotation+"/"+name] + } + return mode, define.ValidateSdNotifyMode(mode) +} diff --git a/pkg/domain/infra/abi/play_utils_test.go b/pkg/domain/infra/abi/play_utils_test.go new file mode 100644 index 000000000..80a9fe543 --- /dev/null +++ b/pkg/domain/infra/abi/play_utils_test.go @@ -0,0 +1,38 @@ +package abi + +import ( + "testing" + + "github.com/containers/podman/v4/libpod/define" + "github.com/stretchr/testify/require" +) + +func TestGetSdNotifyMode(t *testing.T) { + tests := []struct { + key, value, name, result string + mustError bool + }{ + {sdNotifyAnnotation, define.SdNotifyModeConmon, "", define.SdNotifyModeConmon, false}, + {sdNotifyAnnotation + "/container-a", define.SdNotifyModeContainer, "container-a", define.SdNotifyModeContainer, false}, + {sdNotifyAnnotation + "/container-b", define.SdNotifyModeIgnore, "container-b", define.SdNotifyModeIgnore, false}, + {sdNotifyAnnotation + "/container-c", "", "container-c", "", false}, + {sdNotifyAnnotation + "-/wrong-key", "xxx", "wrong-key", "", false}, + {sdNotifyAnnotation + "/container-error", "invalid", "container-error", "", true}, + } + + annotations := make(map[string]string) + // Populate the annotations + for _, test := range tests { + annotations[test.key] = test.value + } + // Run the tests + for _, test := range tests { + result, err := getSdNotifyMode(annotations, test.name) + if test.mustError { + require.Error(t, err, "%v", test) + continue + } + require.NoError(t, err, "%v", test) + require.Equal(t, test.result, result, "%v", test) + } +} diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go index 00ecb3b59..2a514861d 100644 --- a/pkg/domain/infra/tunnel/manifest.go +++ b/pkg/domain/infra/tunnel/manifest.go @@ -99,7 +99,7 @@ func (ir *ImageEngine) ManifestRm(ctx context.Context, names []string) (*entitie // ManifestPush pushes a manifest list or image index to the destination func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ImagePushOptions) (string, error) { options := new(images.PushOptions) - options.WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile).WithRemoveSignatures(opts.RemoveSignatures).WithAll(opts.All).WithFormat(opts.Format).WithCompressionFormat(opts.CompressionFormat) + options.WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile).WithRemoveSignatures(opts.RemoveSignatures).WithAll(opts.All).WithFormat(opts.Format).WithCompressionFormat(opts.CompressionFormat).WithQuiet(opts.Quiet).WithProgressWriter(opts.Writer) if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { if s == types.OptionalBoolTrue { |