package executables import ( "context" "fmt" "os" "strings" "github.com/aws/eks-anywhere/pkg/filewriter" "github.com/aws/eks-anywhere/pkg/logger" "github.com/aws/eks-anywhere/pkg/manifests" "github.com/aws/eks-anywhere/pkg/providers/cloudstack/decoder" ) const defaultEksaImage = "public.ecr.aws/l0g8r8j6/eks-anywhere-cli-tools:v0.7.2-eks-a-v0.0.0-dev-build.1864" type ExecutableBuilder interface { Init(ctx context.Context) (Closer, error) Build(binaryPath string) Executable } type ExecutablesBuilder struct { executableBuilder ExecutableBuilder } func NewExecutablesBuilder(executableBuilder ExecutableBuilder) *ExecutablesBuilder { return &ExecutablesBuilder{ executableBuilder: executableBuilder, } } func (b *ExecutablesBuilder) BuildKindExecutable(writer filewriter.FileWriter) *Kind { return NewKind(b.executableBuilder.Build(kindPath), writer) } func (b *ExecutablesBuilder) BuildClusterAwsAdmExecutable() *Clusterawsadm { return NewClusterawsadm(b.executableBuilder.Build(clusterAwsAdminPath)) } // BuildClusterCtlExecutable builds a new Clusterctl executable. func (b *ExecutablesBuilder) BuildClusterCtlExecutable(writer filewriter.FileWriter, reader manifests.FileReader) *Clusterctl { return NewClusterctl(b.executableBuilder.Build(clusterCtlPath), writer, reader) } func (b *ExecutablesBuilder) BuildKubectlExecutable() *Kubectl { return NewKubectl(b.executableBuilder.Build(kubectlPath)) } func (b *ExecutablesBuilder) BuildGovcExecutable(writer filewriter.FileWriter, opts ...GovcOpt) *Govc { return NewGovc(b.executableBuilder.Build(govcPath), writer, opts...) } // BuildCmkExecutable initializes a Cmk object and returns it. func (b *ExecutablesBuilder) BuildCmkExecutable(writer filewriter.FileWriter, config *decoder.CloudStackExecConfig) (*Cmk, error) { return NewCmk(b.executableBuilder.Build(cmkPath), writer, config) } func (b *ExecutablesBuilder) BuildAwsCli() *AwsCli { return NewAwsCli(b.executableBuilder.Build(awsCliPath)) } func (b *ExecutablesBuilder) BuildFluxExecutable() *Flux { return NewFlux(b.executableBuilder.Build(fluxPath)) } func (b *ExecutablesBuilder) BuildTroubleshootExecutable() *Troubleshoot { return NewTroubleshoot(b.executableBuilder.Build(troubleshootPath)) } func (b *ExecutablesBuilder) BuildHelmExecutable(opts ...HelmOpt) *Helm { return NewHelm(b.executableBuilder.Build(helmPath), opts...) } // BuildDockerExecutable initializes a docker executable and returns it. func (b *ExecutablesBuilder) BuildDockerExecutable() *Docker { return NewDocker(b.executableBuilder.Build(dockerPath)) } // BuildSSHExecutable initializes a SSH executable and returns it. func (b *ExecutablesBuilder) BuildSSHExecutable() *SSH { return NewSSH(b.executableBuilder.Build(sshPath)) } // Init initializes the executable builder and returns a Closer // that needs to be called once the executables are not in used anymore // The closer will cleanup and free all internal resources. func (b *ExecutablesBuilder) Init(ctx context.Context) (Closer, error) { return b.executableBuilder.Init(ctx) } func BuildSonobuoyExecutable() *Sonobuoy { return NewSonobuoy(&executable{ cli: sonobuoyPath, }) } func BuildDockerExecutable() *Docker { return NewDocker(&executable{ cli: dockerPath, }) } // RunExecutablesInDocker determines if binary executables should be ran // from a docker container or native binaries from the host path // It reads MR_TOOLS_DISABLE variable. func ExecutablesInDocker() bool { if env, ok := os.LookupEnv("MR_TOOLS_DISABLE"); ok && strings.EqualFold(env, "true") { logger.Info("Warning: eks-a tools image disabled, using client's executables") return false } return true } // InitInDockerExecutablesBuilder builds and inits a default ExecutablesBuilder to run executables in a docker container // that will make use of a long running docker container. func InitInDockerExecutablesBuilder(ctx context.Context, image string, mountDirs ...string) (*ExecutablesBuilder, Closer, error) { b, err := NewInDockerExecutablesBuilder(BuildDockerExecutable(), image, mountDirs...) if err != nil { return nil, nil, err } closer, err := b.Init(ctx) if err != nil { return nil, nil, err } return b, closer, nil } // NewInDockerExecutablesBuilder builds an executables builder for docker. func NewInDockerExecutablesBuilder(dockerClient DockerClient, image string, mountDirs ...string) (*ExecutablesBuilder, error) { currentDir, err := os.Getwd() if err != nil { return nil, fmt.Errorf("getting current directory: %v", err) } mountDirs = append(mountDirs, currentDir) dockerContainer := newDockerContainer(image, currentDir, mountDirs, dockerClient) dockerExecutableBuilder := NewDockerExecutableBuilder(dockerContainer) return NewExecutablesBuilder(dockerExecutableBuilder), nil } func NewLocalExecutablesBuilder() *ExecutablesBuilder { return NewExecutablesBuilder(newLocalExecutableBuilder()) } func DefaultEksaImage() string { return defaultEksaImage } type Closer func(ctx context.Context) error // Close implements interface types.Closer. func (c Closer) Close(ctx context.Context) error { return c(ctx) } // CheckErr just calls the closer and logs an error if present // It's mostly a helper for defering the close in a oneliner without ignoring the error. func (c Closer) CheckErr(ctx context.Context) { if err := c(ctx); err != nil { logger.Error(err, "Failed closing container for executables") } }