pipecd icon indicating copy to clipboard operation
pipecd copied to clipboard

Use current deployment context in SCRIPT_RUN stage

Open minhquang4334 opened this issue 1 year ago • 6 comments

What would you like to be added: Use current deployment context (like DeploymentTrigger, DeploymentStatus, etc.) in SCRIPT_RUN stage. refs: https://pipecd.dev/docs-v0.46.x/user-guide/managing-application/customizing-deployment/script-run/

Why is this needed: Since we don't know about the current deployment, it is difficult to write a script for customization of the deployment process.

This is similar to GitHubAction, where we can access job and step contexts through a defined property. refs: https://docs.github.com/en/actions/learn-github-actions/contexts#job-context

minhquang4334 avatar Mar 10 '24 07:03 minhquang4334

We look forward to this feature!

In our use case, we want to get the Summary including a docker image tag and deployment ID.

peaceiris avatar Apr 17 '24 07:04 peaceiris

@minhquang4334 @peaceiris Sorry for the late response 🙏

We can realize it by adding the default environment variable for SR_XX (like SR_DEPLOYMENT_ID). How about this?

apiVersion: pipecd.dev/v1beta1
kind: KubernetesApp
spec:
  name: script-run
  labels:
    env: example
    team: product
  pipeline:
    stages:
      - name: K8S_CANARY_ROLLOUT
        with:
          replicas: 10%
      - name: WAIT
        with:
          duration: 10s
      - name: SCRIPT_RUN
        with:
          run: |
            echo $SR_DEPLOYMENT_ID
      - name: K8S_PRIMARY_ROLLOUT
      - name: K8S_CANARY_CLEAN

ffjlabo avatar May 22 '24 04:05 ffjlabo

environment variable for SR_XX (like SR_DEPLOYMENT_ID).

LGTM ʕ◔ϖ◔ʔ

peaceiris avatar May 22 '24 05:05 peaceiris

We can realize it by adding the default environment variable for SR_XX (like SR_DEPLOYMENT_ID).

LGTM 🐳 @ffjlabo

minhquang4334 avatar May 22 '24 06:05 minhquang4334

Thanks, both 👍 I will try it.

Also, I received a comment from @khanhtc1202 about the implementation. We should check whether the env values are shared by multiple deployments.

So at first, I will investigate it :)

ffjlabo avatar May 22 '24 08:05 ffjlabo

After the investigation, I found out the way is possible. 👍 I will implement it later.

Detail As the official document, os/exec package executes the command as a new process.

Package exec runs external commands. It wraps os.StartProcess to make it easier to remap stdin and stdout, connect I/O with pipes, and do other adjustments. https://pkg.go.dev/os/exec

Proceses usually have their own environment variable and separate them.

Also, I tested the behavior using the code below, and it works as I expected.

package main

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"os/exec"

	"golang.org/x/sync/errgroup"
)

func main() {
	// This example shows that each exec.Cmd doesn't share the env value.

	// Scenario:
	// 	When executing "echo $DEPLOYMENT_ID" asynchronously with two exec.Cmd,
	// 	The one is executed after 10s with "sleep 10", the other without any waiting time.
	// Result:
	// 	The output of the first command is "one" and the second is "two". Not shared.
	/*
		% go run main.go
		===== D2 ======
		output: two

		err:
		===== D1 (execute after 5s) ======
		output: one

		err:
	*/
	eg, _ := errgroup.WithContext(context.TODO())
	eg.Go(func() error {
		var outBuf, errBuf bytes.Buffer
		cmd := exec.Command("/bin/sh", "-l", "-c", "sleep 5 && echo $DEPLOYMENT_ID")
		cmd.Env = append(os.Environ(), "DEPLOYMENT_ID=one")
		cmd.Stdout = &outBuf
		cmd.Stderr = &errBuf

		if err := cmd.Run(); err != nil {
			return err
		}

		fmt.Println("===== D1 (execute after 5s) ======")
		fmt.Println("output:", outBuf.String())
		fmt.Println("err:", errBuf.String())

		return nil
	})

	eg.Go(func() error {
		var outBuf, errBuf bytes.Buffer
		cmd := exec.Command("/bin/sh", "-l", "-c", "echo $DEPLOYMENT_ID")
		cmd.Env = append(os.Environ(), "DEPLOYMENT_ID=two")
		cmd.Stdout = &outBuf
		cmd.Stderr = &errBuf

		if err := cmd.Run(); err != nil {
			return err
		}

		fmt.Println("===== D2 ======")
		fmt.Println("output:", outBuf.String())
		fmt.Println("err:", errBuf.String())

		return nil
	})

	if err := eg.Wait(); err != nil {
		fmt.Printf("error :%v\n", err)
	}
}

ffjlabo avatar May 25 '24 09:05 ffjlabo