#Providing downstream functions with optional arguments?

1 messages · Page 1 of 1 (latest)

forest flax
#

I've tried to look through the Discord channel for an answer but I have not been able to find any. If this has already
been answered, please point me to the right post.

I'm working on a Dagger module that contains a plethora of functions that are used across several Dagger modules. One issue I'm having is
figuring out how to provide optional arguments, on par with the function signature for Container.WithFile:

func (r *Container) WithFile(path string, source *File, opts ...ContainerWithFileOpts) *Container

Consider this function, that sets up a container with git credentials configured with git config credential.helper:

type GitCredentialConfig struct {
    Remote string
    Secret *dagger.Secret
}

func (g *GitTest) SetupCredentials(
    ctx context.Context,
    container *dagger.Container,
    githubCredentialSecret GitCredentialConfig,
    additionalSecrets ...GitCredentialConfig,
) (*dagger.Container, error) {
    var err error
    for _, config := range additionalSecrets {
        if container, err = g.setupCredentialsWithConfig(ctx, container, config); err != nil {
            return nil, err
        }
    }

    return g.setupCredentialsWithConfig(ctx, container, githubCredentialSecret)
}

func (g *GitTest) setupCredentialsWithConfig(
    ctx context.Context,
    container *dagger.Container,
    config GitCredentialConfig,
) (*dagger.Container, error) {
    secret := config.Secret
    remote := config.Remote
    id, err := secret.ID(ctx)
    //...
    abbrevID := id[:7]
    secretFile := filepath.Join("/root", fmt.Sprintf("credentials-%s", abbrevID))
    container = container.
        WithExec([]string{"git", "config", "--global", fmt.Sprintf("credential.%s.helper", remote), fmt.Sprintf("store --file %s", secretFile)}).
        WithMountedSecret(secretFile, secret)
    return container, nil
}

Part 2: continuing in comments...

#

Part 2:

Thinking that SetupCredentials uses variadic arguments as the "optional" argument, similarily to how Container.File
does, downstream code would not require to specify the last arguments.

Example downstream test module consumes the function in a test:

func (t *Tests) TestSetupCredentials(ctx context.Context, gitSecret *dagger.Secret) *dagger.Container {
    container := dag.Container().
        From("debian:bookworm-slim").
        WithExec([]string{"apt-get", "update"}).
        WithExec([]string{"apt-get", "install", "-y", "git"})
    container = dag.GitTest().SetupCredentials(container, dagger.GitTestGitcredentialConfig{
        Remote: "https://github.com",
        Secret: gitSecret,
    })
    return container
}

This does not compile, barking out with the following error:

./main.go:16:56: not enough arguments in call to dag.GitTest().SetupCredentials
        have (*dagger.Container, unknown type)
        want (*dagger.Container, *dagger.GitTestGitCredentialConfig,[]*dagger.GitTestGitCredentialConfig)
./main.go:16:63: undefined: dagger.GitTestGitcredentialConfig (but have GitTestGitCredentialConfig)

In order to pass the config arguments, I must provide a constructor - albeit optional, if I want the receiver to be by
value - and setter methods for GitCredentialConfig. We also must pass an empty array as the last argument to appease
the compiler.

func (t *Tests) TestSetupCredentials(ctx context.Context, gitSecret *dagger.Secret) *dagger.Container {
    ...
    container = dag.GitTest().SetupCredentials(container, dag.GitTest().EmptyConfig().
        WithRemote("https://github.com").
        WithSecret(gitSecret),
        []*dagger.GitTestGitCredentialConfig{},
    )
    return container
}

While this works, it feels overly complicated. Is this caused by some limitations with the codegen or am I (hopefully) "just" using Dagger wrong?

Thanks!

solid lynx
forest flax
# solid lynx <@696046241511112784> you can make an argument optional by adding the `// +opti...

@solid lynx , I did not include this in the post, but I have tried using the // +optional pragma. This only gave partial success:

With the signature of SetupCredentials changed to use the pragma, like so:

func (g *GitTest) SetupCredentials(
    ctx context.Context,
    container *dagger.Container,
    githubCredentialSecret GitCredentialConfig,
    // +optional
    additionalSecrets []GitCredentialConfig,

I can omit the empty slice in the test, as you point out. But, when I try to add an additional argument, like below:

func (t *Tests) TestSetupCredentials(ctx context.Context, gitSecret *dagger.Secret) *dagger.Container {
    // ...
    container = dag.GitTest().SetupCredentials(
      container,
      dagger.GitTestGitcredentialConfig{
              Remote: "https://github.com",
              Secret: gitSecret,
      },
      dagger.GitTestGitcredentialConfig{
              Remote: "https://github.com/myorg/myrepo",
              Secret: gitSecret,
      },
    ),
    return container
}

I get the following error message:

./main.go:33:3: cannot use dag.GitTest().EmptyConfig().WithRemote("https://github.com/nordicsemi/nrf-ble-lib").WithSecret(gitSecret) (value of type *dagger.GitTestGitCredentialConfig) as dagger.GitTestSetupCredentialsOpts value in argument to dag.GitTest().SetupCredentials

Inspecting dagger.gen.go, I see that I must wrap the variadic parameters into a generated structure dagger.GitTestSetupCredentialsOpts in order to make this compile:

        dagger.GitTestSetupCredentialsOpts{
            AdditionalSecrets: []*dagger.GitTestGitCredentialConfig{dag.GitTest().EmptyConfig().WithRemote("https://github.com/nordicsemi/nrf-ble-lib").WithSecret(gitSecret)},
        },

Is this correct? It seems very complicated.

solid lynx