Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for multiple git resolver configurations #8263

Merged
merged 1 commit into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 111 additions & 4 deletions docs/git-resolver.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,6 @@ Note that not all `go-scm` implementations have been tested with the `git` resol
* BitBucket Server
* BitBucket Cloud

Fetching from multiple Git providers with different configuration is not
supported. You can use the [http resolver](./http-resolver.md) to fetch URL
from another provider with different credentials.

#### Task Resolution

```yaml
Expand Down Expand Up @@ -195,6 +191,117 @@ spec:
value: Ranni
```

### Specifying Configuration for Multiple Git Providers
afrittoli marked this conversation as resolved.
Show resolved Hide resolved

It is possible to specify configurations for multiple providers and even multiple configurations for same provider to use in
different tekton resources. You need to first add details in configmap with your unique identifier key prefix.
To use them in tekton resources, pass the unique key mentioned in configmap as an extra param to resolver with key
`configKey` and value your unique key. If no `configKey` param is passed, `default` will be used. You can specify
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: you can...

default configuration in configmap either by mentioning no unique identifier or by using default identifier `default`

**Note**: `configKey` should not contain `.` while specifying configurations in configmap

### Example Configmap

You can add multiple configuration to `git-resolver-configmap` like this. All keys mentioned above are supported.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: "you can..."


```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: git-resolver-config
namespace: tekton-pipelines-resolvers
labels:
app.kubernetes.io/component: resolvers
app.kubernetes.io/instance: default
app.kubernetes.io/part-of: tekton-pipelines
data:
# configuration 1, default one to use if no configKey provided or provided with value default
fetch-timeout: "1m"
default-url: "https://github.com/tektoncd/catalog.git"
default-revision: "main"
scm-type: "github"
server-url: ""
api-token-secret-name: ""
api-token-secret-key: ""
api-token-secret-namespace: "default"
default-org: ""

# configuration 2, will be used if configKey param passed with value test1
test1.fetch-timeout: "5m"
test1.default-url: ""
test1.default-revision: "stable"
test1.scm-type: "github"
test1.server-url: "api.internal-github.com"
test1.api-token-secret-name: "test1-secret"
test1.api-token-secret-key: "token"
test1.api-token-secret-namespace: "test1"
test1.default-org: "tektoncd"

# configuration 3, will be used if configKey param passed with value test2
test2.fetch-timeout: "10m"
test2.default-url: ""
test2.default-revision: "stable"
test2.scm-type: "gitlab"
test2.server-url: "api.internal-gitlab.com"
test2.api-token-secret-name: "test2-secret"
test2.api-token-secret-key: "pat"
test2.api-token-secret-namespace: "test2"
test2.default-org: "tektoncd-infra"
```

#### Task Resolution

A specific configurations from the configMap can be selected by passing the parameter `configKey` with the value
matching one of the configuration keys used in the configMaps.

```yaml
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
name: git-api-demo-tr
spec:
taskRef:
resolver: git
params:
- name: org
value: tektoncd
- name: repo
value: catalog
- name: revision
value: main
- name: pathInRepo
value: task/git-clone/0.6/git-clone.yaml
- name: configKey
value: test1
```

#### Pipeline resolution

```yaml
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: git-api-demo-pr
spec:
pipelineRef:
resolver: git
params:
- name: org
value: tektoncd
- name: repo
value: catalog
- name: revision
value: main
- name: pathInRepo
value: pipeline/simple/0.1/simple.yaml
- name: configKey
value: test2
params:
- name: name
value: Ranni
```

## `ResolutionRequest` Status
`ResolutionRequest.Status.RefSource` field captures the source where the remote resource came from. It includes the 3 subfields: `url`, `digest` and `entrypoint`.
- `url`
Expand Down
4 changes: 2 additions & 2 deletions pkg/remoteresolution/resolver/framework/fakeresolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,6 @@ func (r *FakeResolver) Resolve(_ context.Context, req *v1beta1.ResolutionRequest
var _ framework.TimedResolution = &FakeResolver{}

// GetResolutionTimeout returns the configured timeout for the reconciler, or the default time.Duration if not configured.
func (r *FakeResolver) GetResolutionTimeout(ctx context.Context, defaultTimeout time.Duration) time.Duration {
return framework.GetResolutionTimeout(r.Timeout, defaultTimeout)
func (r *FakeResolver) GetResolutionTimeout(ctx context.Context, defaultTimeout time.Duration, params map[string]string) (time.Duration, error) {
return framework.GetResolutionTimeout(r.Timeout, defaultTimeout), nil
}
11 changes: 10 additions & 1 deletion pkg/remoteresolution/resolver/framework/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,18 @@ func (r *Reconciler) resolve(ctx context.Context, key string, rr *v1beta1.Resolu
errChan := make(chan error)
resourceChan := make(chan framework.ResolvedResource)

paramsMap := make(map[string]string)
for _, p := range rr.Spec.Params {
paramsMap[p.Name] = p.Value.StringVal
}

timeoutDuration := defaultMaximumResolutionDuration
if timed, ok := r.resolver.(framework.TimedResolution); ok {
timeoutDuration = timed.GetResolutionTimeout(ctx, defaultMaximumResolutionDuration)
var err error
timeoutDuration, err = timed.GetResolutionTimeout(ctx, defaultMaximumResolutionDuration, paramsMap)
if err != nil {
return err
}
}

// A new context is created for resolution so that timeouts can
Expand Down
16 changes: 10 additions & 6 deletions pkg/remoteresolution/resolver/git/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,17 @@ var _ resolutionframework.TimedResolution = &Resolver{}
// GetResolutionTimeout returns a time.Duration for the amount of time a
// single git fetch may take. This can be configured with the
// fetch-timeout field in the git-resolver-config configmap.
func (r *Resolver) GetResolutionTimeout(ctx context.Context, defaultTimeout time.Duration) time.Duration {
conf := resolutionframework.GetResolverConfigFromContext(ctx)
if timeoutString, ok := conf[git.DefaultTimeoutKey]; ok {
func (r *Resolver) GetResolutionTimeout(ctx context.Context, defaultTimeout time.Duration, params map[string]string) (time.Duration, error) {
conf, err := git.GetScmConfigForParamConfigKey(ctx, params)
if err != nil {
return time.Duration(0), err
}
if timeoutString := conf.Timeout; timeoutString != "" {
timeout, err := time.ParseDuration(timeoutString)
if err == nil {
return timeout
if err != nil {
return time.Duration(0), err
}
return timeout, nil
}
return defaultTimeout
return defaultTimeout, nil
}
88 changes: 81 additions & 7 deletions pkg/remoteresolution/resolver/git/resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,10 @@ func TestValidateParams_Failure(t *testing.T) {
func TestGetResolutionTimeoutDefault(t *testing.T) {
resolver := Resolver{}
defaultTimeout := 30 * time.Minute
timeout := resolver.GetResolutionTimeout(context.Background(), defaultTimeout)
timeout, err := resolver.GetResolutionTimeout(context.Background(), defaultTimeout, map[string]string{})
if err != nil {
t.Fatalf("couldn't get default-timeout: %v", err)
}
if timeout != defaultTimeout {
t.Fatalf("expected default timeout to be returned")
}
Expand All @@ -233,12 +236,34 @@ func TestGetResolutionTimeoutCustom(t *testing.T) {
gitresolution.DefaultTimeoutKey: configTimeout.String(),
}
ctx := resolutionframework.InjectResolverConfigToContext(context.Background(), config)
timeout := resolver.GetResolutionTimeout(ctx, defaultTimeout)
timeout, err := resolver.GetResolutionTimeout(ctx, defaultTimeout, map[string]string{})
if err != nil {
t.Fatalf("couldn't get default-timeout: %v", err)
}
if timeout != configTimeout {
t.Fatalf("expected timeout from config to be returned")
}
}

func TestGetResolutionTimeoutCustomIdentifier(t *testing.T) {
resolver := Resolver{}
defaultTimeout := 30 * time.Minute
configTimeout := 5 * time.Second
identifierConfigTImeout := 10 * time.Second
config := map[string]string{
gitresolution.DefaultTimeoutKey: configTimeout.String(),
"foo." + gitresolution.DefaultTimeoutKey: identifierConfigTImeout.String(),
}
ctx := resolutionframework.InjectResolverConfigToContext(context.Background(), config)
timeout, err := resolver.GetResolutionTimeout(ctx, defaultTimeout, map[string]string{"configKey": "foo"})
if err != nil {
t.Fatalf("couldn't get default-timeout: %v", err)
}
if timeout != identifierConfigTImeout {
t.Fatalf("expected timeout from config to be returned")
}
}

func TestResolveNotEnabled(t *testing.T) {
resolver := Resolver{}

Expand Down Expand Up @@ -268,6 +293,7 @@ type params struct {
namespace string
serverURL string
scmType string
configKey string
}

func TestResolve(t *testing.T) {
Expand Down Expand Up @@ -344,6 +370,7 @@ func TestResolve(t *testing.T) {
expectedCommitSHA string
expectedStatus *v1beta1.ResolutionRequestStatus
expectedErr error
configIdentifer string
}{{
name: "clone: default revision main",
args: &params{
Expand Down Expand Up @@ -439,6 +466,46 @@ func TestResolve(t *testing.T) {
apiToken: "some-token",
expectedCommitSHA: commitSHAsInSCMRepo[0],
expectedStatus: resolution.CreateResolutionRequestStatusWithData(mainTaskYAML),
}, {
name: "api: successful task from params api information with identifier",
args: &params{
revision: "main",
pathInRepo: "tasks/example-task.yaml",
org: testOrg,
repo: testRepo,
token: "token-secret",
tokenKey: "token",
namespace: "foo",
configKey: "test",
},
config: map[string]string{
"test." + gitresolution.ServerURLKey: "fake",
"test." + gitresolution.SCMTypeKey: "fake",
},
configIdentifer: "test.",
apiToken: "some-token",
expectedCommitSHA: commitSHAsInSCMRepo[0],
expectedStatus: resolution.CreateResolutionRequestStatusWithData(mainTaskYAML),
}, {
name: "api: successful task with identifier",
args: &params{
revision: "main",
pathInRepo: "tasks/example-task.yaml",
org: testOrg,
repo: testRepo,
configKey: "test",
},
config: map[string]string{
"test." + gitresolution.ServerURLKey: "fake",
"test." + gitresolution.SCMTypeKey: "fake",
"test." + gitresolution.APISecretNameKey: "token-secret",
"test." + gitresolution.APISecretKeyKey: "token",
"test." + gitresolution.APISecretNamespaceKey: system.Namespace(),
},
configIdentifer: "test.",
apiToken: "some-token",
expectedCommitSHA: commitSHAsInSCMRepo[0],
expectedStatus: resolution.CreateResolutionRequestStatusWithData(mainTaskYAML),
}, {
name: "api: successful pipeline",
args: &params{
Expand Down Expand Up @@ -591,9 +658,9 @@ func TestResolve(t *testing.T) {
if cfg == nil {
cfg = make(map[string]string)
}
cfg[gitresolution.DefaultTimeoutKey] = "1m"
if cfg[gitresolution.DefaultRevisionKey] == "" {
cfg[gitresolution.DefaultRevisionKey] = plumbing.Master.Short()
cfg[tc.configIdentifer+gitresolution.DefaultTimeoutKey] = "1m"
if cfg[tc.configIdentifer+gitresolution.DefaultRevisionKey] == "" {
cfg[tc.configIdentifer+gitresolution.DefaultRevisionKey] = plumbing.Master.Short()
}

request := createRequest(tc.args)
Expand Down Expand Up @@ -654,8 +721,8 @@ func TestResolve(t *testing.T) {

frtesting.RunResolverReconcileTest(ctx, t, d, resolver, request, expectedStatus, tc.expectedErr, func(resolver framework.Resolver, testAssets test.Assets) {
var secretName, secretNameKey, secretNamespace string
if tc.config[gitresolution.APISecretNameKey] != "" && tc.config[gitresolution.APISecretNamespaceKey] != "" && tc.config[gitresolution.APISecretKeyKey] != "" && tc.apiToken != "" {
secretName, secretNameKey, secretNamespace = tc.config[gitresolution.APISecretNameKey], tc.config[gitresolution.APISecretKeyKey], tc.config[gitresolution.APISecretNamespaceKey]
if tc.config[tc.configIdentifer+gitresolution.APISecretNameKey] != "" && tc.config[tc.configIdentifer+gitresolution.APISecretNamespaceKey] != "" && tc.config[tc.configIdentifer+gitresolution.APISecretKeyKey] != "" && tc.apiToken != "" {
secretName, secretNameKey, secretNamespace = tc.config[tc.configIdentifer+gitresolution.APISecretNameKey], tc.config[tc.configIdentifer+gitresolution.APISecretKeyKey], tc.config[tc.configIdentifer+gitresolution.APISecretNamespaceKey]
}
if tc.args.token != "" && tc.args.namespace != "" && tc.args.tokenKey != "" {
secretName, secretNameKey, secretNamespace = tc.args.token, tc.args.tokenKey, tc.args.namespace
Expand Down Expand Up @@ -879,6 +946,13 @@ func createRequest(args *params) *v1beta1.ResolutionRequest {
}
}

if args.configKey != "" {
rr.Spec.Params = append(rr.Spec.Params, pipelinev1.Param{
Name: gitresolution.ConfigKeyParam,
Value: *pipelinev1.NewStructuredValues(args.configKey),
})
}

return rr
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/resolution/resolver/framework/fakeresolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ func Resolve(params []pipelinev1.Param, forParam map[string]*FakeResolvedResourc
var _ TimedResolution = &FakeResolver{}

// GetResolutionTimeout returns the configured timeout for the reconciler, or the default time.Duration if not configured.
func (r *FakeResolver) GetResolutionTimeout(ctx context.Context, defaultTimeout time.Duration) time.Duration {
return GetResolutionTimeout(r.Timeout, defaultTimeout)
func (r *FakeResolver) GetResolutionTimeout(ctx context.Context, defaultTimeout time.Duration, params map[string]string) (time.Duration, error) {
return GetResolutionTimeout(r.Timeout, defaultTimeout), nil
}

// GetResolutionTimeout returns the input timeout if set to something greater than 0 or the default time.Duration if not configured.
Expand Down
2 changes: 1 addition & 1 deletion pkg/resolution/resolver/framework/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ type TimedResolution interface {
// object, which includes any request-scoped data like
// resolver config and the request's originating namespace,
// along with a default.
GetResolutionTimeout(ctx context.Context, timeout time.Duration) time.Duration
GetResolutionTimeout(ctx context.Context, timeout time.Duration, params map[string]string) (time.Duration, error)
}

// ResolvedResource returns the data and annotations of a successful
Expand Down
11 changes: 10 additions & 1 deletion pkg/resolution/resolver/framework/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,18 @@ func (r *Reconciler) resolve(ctx context.Context, key string, rr *v1beta1.Resolu
errChan := make(chan error)
resourceChan := make(chan ResolvedResource)

paramsMap := make(map[string]string)
for _, p := range rr.Spec.Params {
paramsMap[p.Name] = p.Value.StringVal
}

timeoutDuration := defaultMaximumResolutionDuration
if timed, ok := r.resolver.(TimedResolution); ok {
timeoutDuration = timed.GetResolutionTimeout(ctx, defaultMaximumResolutionDuration)
var err error
timeoutDuration, err = timed.GetResolutionTimeout(ctx, defaultMaximumResolutionDuration, paramsMap)
if err != nil {
return err
}
}

// A new context is created for resolution so that timeouts can
Expand Down
Loading