Skip to content

Commit

Permalink
simplify docker build, use testcontainers and build once, do not run …
Browse files Browse the repository at this point in the history
…Promtail in CI (#1401)

* simplify docker build, use testcontainers and build once, do not run promtail in CI

* changeset
  • Loading branch information
skudasov authored Nov 27, 2024
1 parent 24b9149 commit d2d8cf2
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 129 deletions.
3 changes: 3 additions & 0 deletions framework/.changeset/v0.2.13.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- Simplify local Chainlink build
- Remove dependency on "local-registry"
- Skip Promtail to accelerate CI 🚀
45 changes: 1 addition & 44 deletions framework/cmd/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import (
"github.com/smartcontractkit/chainlink-testing-framework/framework"
"os"
"os/exec"
"strings"
)

func rmTestContainers() error {
func removeTestContainers() error {
framework.L.Info().Str("label", "framework=ctf").Msg("Cleaning up docker containers")
// Bash command for removing Docker containers and networks with "framework=ctf" label
cmd := exec.Command("bash", "-c", `
Expand Down Expand Up @@ -45,48 +44,6 @@ func cleanUpDockerResources() error {
return nil
}

// BuildDocker runs Docker commands to set up a local registry, build an image, and push it.
func BuildDocker(dockerfile string, buildContext string, imageName string) error {
registryRunning := isContainerRunning("local-registry")
if registryRunning {
fmt.Println("Local registry container is already running.")
} else {
framework.L.Info().Msg("Starting local registry container...")
err := runCommand("docker", "run", "-d", "-p", "5050:5000", "--name", "local-registry", "registry:2")
if err != nil {
return fmt.Errorf("failed to start local registry: %w", err)
}
framework.L.Info().Msg("Local registry started")
}

img := fmt.Sprintf("localhost:5050/%s:latest", imageName)
framework.L.Info().Str("DockerFile", dockerfile).Str("Context", buildContext).Msg("Building Docker image")
err := runCommand("docker", "build", "-t", fmt.Sprintf("localhost:5050/%s:latest", imageName), "-f", dockerfile, buildContext)
if err != nil {
return fmt.Errorf("failed to build Docker image: %w", err)
}
framework.L.Info().Msg("Docker image built successfully")

framework.L.Info().Str("Image", img).Msg("Pushing Docker image to local registry")
fmt.Println("Pushing Docker image to local registry...")
err = runCommand("docker", "push", img)
if err != nil {
return fmt.Errorf("failed to push Docker image: %w", err)
}
framework.L.Info().Msg("Docker image pushed successfully")
return nil
}

// isContainerRunning checks if a Docker container with the given name is running.
func isContainerRunning(containerName string) bool {
cmd := exec.Command("docker", "ps", "--filter", fmt.Sprintf("name=%s", containerName), "--format", "{{.Names}}")
output, err := cmd.Output()
if err != nil {
return false
}
return strings.Contains(string(output), containerName)
}

// runCommand executes a command and prints the output.
func runCommand(name string, args ...string) error {
cmd := exec.Command(name, args...)
Expand Down
2 changes: 1 addition & 1 deletion framework/cmd/interactive.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func createComponentsFromForm(form *nodeSetForm) error {
func cleanup(form *nodeSetForm) error {
var err error
f := func() {
err = rmTestContainers()
err = removeTestContainers()
}
err = spinner.New().
Title("Removing docker resources..").
Expand Down
2 changes: 1 addition & 1 deletion framework/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func main() {
Aliases: []string{"rm"},
Usage: "Remove Docker containers and networks with 'framework=ctf' label",
Action: func(c *cli.Context) error {
err := rmTestContainers()
err := removeTestContainers()
if err != nil {
return fmt.Errorf("failed to clean Docker resources: %w", err)
}
Expand Down
5 changes: 3 additions & 2 deletions framework/components/clnode/clnode.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
const (
DefaultHTTPPort = "6688"
DefaultP2PPort = "6690"
TmpImageName = "chainlink-tmp:latest"
)

var (
Expand Down Expand Up @@ -257,8 +258,8 @@ func newNode(in *Input, pgOut *postgres.Output) (*NodeOut, error) {
return nil, errors.New("you provided both 'image' and one of 'docker_file', 'docker_ctx' fields. Please provide either 'image' or params to build a local one")
}
if req.Image == "" {
req.Image, err = framework.RebuildDockerImage(once, in.Node.DockerFilePath, in.Node.DockerContext, in.Node.DockerImageName)
if err != nil {
req.Image = TmpImageName
if err := framework.BuildImageOnce(ctx, once, in.Node.DockerContext, in.Node.DockerFilePath, req.Image); err != nil {
return nil, err
}
req.KeepImage = false
Expand Down
6 changes: 4 additions & 2 deletions framework/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,10 @@ func Load[X any](t *testing.T) (*X, error) {
//}
err = DefaultNetwork(once)
require.NoError(t, err)
err = NewPromtail()
require.NoError(t, err)
if os.Getenv(EnvVarCI) != "true" {
err = NewPromtail()
require.NoError(t, err)
}
return input, nil
}

Expand Down
106 changes: 27 additions & 79 deletions framework/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"golang.org/x/sync/errgroup"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
Expand Down Expand Up @@ -70,84 +69,6 @@ func DefaultTCName(name string) string {
return fmt.Sprintf("%s-%s", name, uuid.NewString()[0:5])
}

// BuildAndPublishLocalDockerImage runs Docker commands to set up a local registry, build an image, and push it.
func BuildAndPublishLocalDockerImage(once *sync.Once, dockerfile string, buildContext string, imageName string) error {
var retErr error
once.Do(func() {
L.Info().
Str("Dockerfile", dockerfile).
Str("Ctx", buildContext).
Str("ImageName", imageName).
Msg("Building local docker file")
registryRunning := isContainerRunning("local-registry")
if registryRunning {
fmt.Println("Local registry container is already running.")
} else {
L.Info().Msg("Removing local registry")
_ = runCommand("docker", "stop", "local-registry")
_ = runCommand("docker", "rm", "local-registry")
L.Info().Msg("Starting local registry container...")
err := runCommand("docker", "run", "-d", "-p", "5050:5000", "--name", "local-registry", "registry:2")
if err != nil {
retErr = fmt.Errorf("failed to start local registry: %w", err)
}
L.Info().Msg("Local registry started")
}

img := fmt.Sprintf("localhost:5050/%s:latest", imageName)
err := runCommand("docker", "build", "-t", fmt.Sprintf("localhost:5050/%s:latest", imageName), "-f", dockerfile, buildContext)
if err != nil {
retErr = fmt.Errorf("failed to build Docker image: %w", err)
}
L.Info().Msg("Docker image built successfully")

L.Info().Str("Image", img).Msg("Pushing Docker image to local registry")
fmt.Println("Pushing Docker image to local registry...")
err = runCommand("docker", "push", img)
if err != nil {
retErr = fmt.Errorf("failed to push Docker image: %w", err)
}
L.Info().Msg("Docker image pushed successfully")
})
return retErr
}

// isContainerRunning checks if a Docker container with the given name is running.
func isContainerRunning(containerName string) bool {
cmd := exec.Command("docker", "ps", "--filter", fmt.Sprintf("name=%s", containerName), "--format", "{{.Names}}")
output, err := cmd.Output()
if err != nil {
return false
}
return strings.Contains(string(output), containerName)
}

// runCommand executes a command and prints the output.
func runCommand(name string, args ...string) error {
cmd := exec.Command(name, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

// TODO: use tc.NewDockerProvider().BuildImage() to skip managing the registry container
// RebuildDockerImage rebuilds docker image if necessary
func RebuildDockerImage(once *sync.Once, dockerfile string, buildContext string, imageName string) (string, error) {
if dockerfile == "" {
return "", errors.New("docker_file path must be provided")
}
if buildContext == "" {
return "", errors.New("docker_ctx path must be provided")
}
if imageName == "" {
imageName = "ctftmp"
}
if err := BuildAndPublishLocalDockerImage(once, dockerfile, buildContext, imageName); err != nil {
return "", err
}
return fmt.Sprintf("localhost:5050/%s:latest", imageName), nil
}

// DockerClient wraps a Docker API client and provides convenience methods
type DockerClient struct {
cli *client.Client
Expand Down Expand Up @@ -320,3 +241,30 @@ func WriteAllContainersLogs() error {
}
return eg.Wait()
}

func BuildImageOnce(ctx context.Context, once *sync.Once, dctx, dfile, nameAndTag string) error {
var (
p *tc.DockerProvider
dockerCtx string
err error
)
once.Do(func() {
nt := strings.Split(nameAndTag, ":")
if len(nt) != 2 {
err = errors.New("BuildImageOnce, tag must be in 'repo:tag' format")
return
}
p, err = tc.NewDockerProvider()
dockerCtx, err = filepath.Abs(dctx)
_, err = p.BuildImage(ctx, &tc.ContainerRequest{
FromDockerfile: tc.FromDockerfile{
Repo: nt[0],
Tag: nt[1],
Context: dockerCtx,
Dockerfile: dfile,
PrintBuildLog: true,
},
})
})
return err
}

0 comments on commit d2d8cf2

Please sign in to comment.