Skip to content

Commit 1177fd5

Browse files
committed
update tekton namespace handling
1 parent e677216 commit 1177fd5

File tree

11 files changed

+145
-97
lines changed

11 files changed

+145
-97
lines changed

cmd/deploy_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ func TestDeploy_GitArgsUsed(t *testing.T) {
571571

572572
// A Pipelines Provider which will validate the expected values were received
573573
pipeliner := mock.NewPipelinesProvider()
574-
pipeliner.RunFn = func(f fn.Function) (string, string, error) {
574+
pipeliner.RunFn = func(f fn.Function) (string, fn.Function, error) {
575575
if f.Build.Git.URL != url {
576576
t.Errorf("Pipeline Provider expected git URL '%v' got '%v'", url, f.Build.Git.URL)
577577
}
@@ -581,7 +581,7 @@ func TestDeploy_GitArgsUsed(t *testing.T) {
581581
if f.Build.Git.ContextDir != dir {
582582
t.Errorf("Pipeline Provider expected git dir '%v' got '%v'", url, f.Build.Git.ContextDir)
583583
}
584-
return url, "", nil
584+
return url, f, nil
585585
}
586586

587587
// Deploy the Function specifying all of the git-related flags and --remote

pkg/functions/client.go

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ type DNSProvider interface {
198198

199199
// PipelinesProvider manages lifecyle of CI/CD pipelines used by a function
200200
type PipelinesProvider interface {
201-
Run(context.Context, Function) (string, string, error)
201+
Run(context.Context, Function) (string, Function, error)
202202
Remove(context.Context, Function) error
203203
ConfigurePAC(context.Context, Function, any) error
204204
RemovePAC(context.Context, Function, any) error
@@ -814,31 +814,13 @@ func (c *Client) Deploy(ctx context.Context, f Function, oo ...DeployOption) (Fu
814814
// Returned function contains applicable registry and deployed image name.
815815
// String is the default route.
816816
func (c *Client) RunPipeline(ctx context.Context, f Function) (string, Function, error) {
817-
var err error
818-
var url string
819817
// Default function registry to the client's global registry
820818
if f.Registry == "" {
821819
f.Registry = c.registry
822820
}
823821

824-
// If no image name has been specified by user (--image), calculate.
825-
// Image name is stored on the function for later use by deploy.
826-
if f.Image != "" {
827-
// if user specified an image, use it
828-
f.Deploy.Image = f.Image
829-
} else if f.Deploy.Image == "" {
830-
f.Deploy.Image, err = f.ImageName()
831-
if err != nil {
832-
return "", f, err
833-
}
834-
}
835-
836822
// Build and deploy function using Pipeline
837-
url, f.Deploy.Namespace, err = c.pipelinesProvider.Run(ctx, f)
838-
if err != nil {
839-
return url, f, fmt.Errorf("failed to run pipeline: %w", err)
840-
}
841-
return url, f, nil
823+
return c.pipelinesProvider.Run(ctx, f)
842824
}
843825

844826
// ConfigurePAC generates Pipeline resources on the local filesystem,
@@ -1369,8 +1351,8 @@ func (n *noopDescriber) Describe(context.Context, string, string) (Instance, err
13691351
// PipelinesProvider
13701352
type noopPipelinesProvider struct{}
13711353

1372-
func (n *noopPipelinesProvider) Run(ctx context.Context, _ Function) (string, string, error) {
1373-
return "", "", nil
1354+
func (n *noopPipelinesProvider) Run(ctx context.Context, f Function) (string, Function, error) {
1355+
return "", f, nil
13741356
}
13751357
func (n *noopPipelinesProvider) Remove(ctx context.Context, _ Function) error { return nil }
13761358
func (n *noopPipelinesProvider) ConfigurePAC(ctx context.Context, _ Function, _ any) error {

pkg/functions/client_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,9 +1437,10 @@ func TestClient_Pipelines_Deploy_Namespace(t *testing.T) {
14371437
defer rm()
14381438

14391439
pprovider := mock.NewPipelinesProvider()
1440-
pprovider.RunFn = func(f fn.Function) (string, string, error) {
1441-
// simulate function getting deployed here and return namespace
1442-
return "", f.Namespace, nil
1440+
pprovider.RunFn = func(f fn.Function) (string, fn.Function, error) {
1441+
// simulate function being deployed
1442+
f.Deploy.Namespace = f.Namespace
1443+
return "", f, nil
14431444
}
14441445

14451446
client := fn.New(

pkg/knative/deployer.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,22 @@ func (d *Deployer) isImageInPrivateRegistry(ctx context.Context, client clientse
111111
return false
112112
}
113113

114+
func onClusterFix(f fn.Function) fn.Function {
115+
// This only exists because of a bootstapping problem with On-Cluster
116+
// builds: It appears that, when sending a function to be built on-cluster
117+
// the target namespace is not being transmitted in the pipeline
118+
// configuration. We should figure out how to transmit this information
119+
// to the pipeline run for initial builds. This is a new problem because
120+
// earlier versions of this logic relied entirely on the current
121+
// kubernetes context.
122+
if f.Namespace == "" && f.Deploy.Namespace == "" {
123+
f.Namespace, _ = k8s.GetDefaultNamespace()
124+
}
125+
return f
126+
}
127+
114128
func (d *Deployer) Deploy(ctx context.Context, f fn.Function) (fn.DeploymentResult, error) {
129+
f = onClusterFix(f)
115130
// Choosing f.Namespace vs f.Deploy.Namespace:
116131
// This is minimal logic currently required of all deployer impls.
117132
// If f.Namespace is defined, this is the (possibly new) target

pkg/mock/pipelines_provider.go

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99

1010
type PipelinesProvider struct {
1111
RunInvoked bool
12-
RunFn func(fn.Function) (string, string, error)
12+
RunFn func(fn.Function) (string, fn.Function, error)
1313
RemoveInvoked bool
1414
RemoveFn func(fn.Function) error
1515
ConfigurePACInvoked bool
@@ -20,25 +20,41 @@ type PipelinesProvider struct {
2020

2121
func NewPipelinesProvider() *PipelinesProvider {
2222
return &PipelinesProvider{
23-
RunFn: func(f fn.Function) (x string, namespace string, err error) {
23+
RunFn: func(f fn.Function) (string, fn.Function, error) {
2424
// the minimum necessary logic for a deployer, which should be
25-
// confirmed by tests in the respective implementations.
25+
// confirmed by tests in the respective implementations, is to
26+
// return the function with f.Deploy.* values set reflecting the
27+
// now deployed state of the function.
28+
if f.Namespace == "" && f.Deploy.Namespace == "" {
29+
return "", f, errors.New("namespace required for initial deployment")
30+
}
31+
32+
// fabricate that we deployed it to the newly requested namespace
2633
if f.Namespace != "" {
27-
namespace = f.Namespace
28-
} else if f.Deploy.Namespace != "" {
29-
namespace = f.Deploy.Namespace
34+
f.Deploy.Namespace = f.Namespace
35+
}
36+
37+
// fabricate that we deployed the requested image or generated
38+
// it as needed
39+
var err error
40+
if f.Image != "" {
41+
f.Deploy.Image = f.Image
3042
} else {
31-
err = errors.New("namespace required for initial deployment")
43+
if f.Deploy.Image, err = f.ImageName(); err != nil {
44+
return "", f, err
45+
}
3246
}
33-
return
47+
48+
return "", f, nil
49+
3450
},
3551
RemoveFn: func(fn.Function) error { return nil },
3652
ConfigurePACFn: func(fn.Function) error { return nil },
3753
RemovePACFn: func(fn.Function) error { return nil },
3854
}
3955
}
4056

41-
func (p *PipelinesProvider) Run(ctx context.Context, f fn.Function) (string, string, error) {
57+
func (p *PipelinesProvider) Run(ctx context.Context, f fn.Function) (string, fn.Function, error) {
4258
p.RunInvoked = true
4359
return p.RunFn(f)
4460
}

pkg/pipelines/tekton/client.go

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,19 @@ const (
1313
DefaultWaitingTimeout = 120 * time.Second
1414
)
1515

16-
// NewTektonClientAndResolvedNamespace returns TektonV1beta1Client,namespace,error
17-
func NewTektonClientAndResolvedNamespace(namespace string) (*v1beta1.TektonV1beta1Client, string, error) {
18-
var err error
19-
if namespace == "" {
20-
namespace, err = k8s.GetDefaultNamespace()
21-
if err != nil {
22-
return nil, "", err
23-
}
24-
}
25-
16+
// NewTektonClient returns TektonV1beta1Client for namespace
17+
func NewTektonClient(namespace string) (*v1beta1.TektonV1beta1Client, error) {
2618
restConfig, err := k8s.GetClientConfig().ClientConfig()
2719
if err != nil {
28-
return nil, "", fmt.Errorf("failed to create new tekton client: %w", err)
20+
return nil, fmt.Errorf("failed to create new tekton client: %w", err)
2921
}
3022

3123
client, err := v1beta1.NewForConfig(restConfig)
3224
if err != nil {
33-
return nil, "", fmt.Errorf("failed to create new tekton client: %v", err)
25+
return nil, fmt.Errorf("failed to create new tekton client: %v", err)
3426
}
3527

36-
return client, namespace, nil
28+
return client, nil
3729
}
3830

3931
func NewTektonClients() (*cli.Clients, error) {

pkg/pipelines/tekton/pipelines_provider.go

Lines changed: 59 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -100,41 +100,61 @@ func NewPipelinesProvider(opts ...Opt) *PipelinesProvider {
100100
return pp
101101
}
102102

103-
// Run creates a Tekton Pipeline and all necessary resources (PVCs, Secrets, SAs,...) for the input Function.
104-
// It ensures that all needed resources are present on the cluster so the PipelineRun can be initialized.
105-
// After the PipelineRun is being initialized, the progress of the PipelineRun is being watched and printed to the output.
106-
func (pp *PipelinesProvider) Run(ctx context.Context, f fn.Function) (string, string, error) {
107-
fmt.Fprintf(os.Stderr, "Creating Pipeline resources\n")
103+
// Run a remote build by creating all necessary resources (PVCs, secrets,
104+
// SAs, etc) specified by the given Function before generating a pipeline
105+
// definition, sending it to the cluster to be run via Tekton.
106+
// Progress is by default piped to stdtout.
107+
// Returned is the final url, and the input Function with the final results of the run populated
108+
// (f.Deploy.Image and f.Deploy.Namespace) or an error.
109+
func (pp *PipelinesProvider) Run(ctx context.Context, f fn.Function) (string, fn.Function, error) {
108110
var err error
111+
112+
// Checks builder and registry:
109113
if err = validatePipeline(f); err != nil {
110-
return "", "", err
114+
return "", f, err
111115
}
112116

117+
// Namespace is either a new namespace, specified as f.Namespace, or
118+
// the currently deployed namespace, recorded on f.Deploy.Namespace.
119+
// If neither exist, an error is returned (namespace is required)
113120
namespace := f.Namespace
114121
if namespace == "" {
115122
namespace = f.Deploy.Namespace
116123
}
124+
if namespace == "" {
125+
return "", f, fn.ErrNamespaceRequired
126+
}
127+
f.Deploy.Namespace = namespace
117128

118-
client, ns2, err := NewTektonClientAndResolvedNamespace(namespace)
119-
if err != nil {
120-
return "", "", err
129+
// Image is either an explicit image indicated with f.Image, or
130+
// generated from a name+registry
131+
image := f.Image
132+
if image == "" {
133+
image, err = f.ImageName()
134+
if err != nil {
135+
return "", f, err
136+
}
121137
}
122-
if ns2 != namespace {
123-
panic("fixme")
138+
f.Deploy.Image = image
139+
140+
// Client for the given namespace
141+
client, err := NewTektonClient(namespace)
142+
if err != nil {
143+
return "", f, err
124144
}
125145

126146
// let's specify labels that will be applied to every resource that is created for a Pipeline
127147
labels, err := f.LabelsMap()
128148
if err != nil {
129-
return "", "", err
149+
return "", f, err
130150
}
131151
if pp.decorator != nil {
132152
labels = pp.decorator.UpdateLabels(f, labels)
133153
}
134154

135155
err = createPipelinePersistentVolumeClaim(ctx, f, namespace, labels)
136156
if err != nil {
137-
return "", "", err
157+
return "", f, err
138158
}
139159

140160
if f.Build.Git.URL == "" {
@@ -143,84 +163,88 @@ func (pp *PipelinesProvider) Run(ctx context.Context, f fn.Function) (string, st
143163
defer content.Close()
144164
err = k8s.UploadToVolume(ctx, content, getPipelinePvcName(f), namespace)
145165
if err != nil {
146-
return "", "", fmt.Errorf("cannot upload sources to the PVC: %w", err)
166+
return "", f, fmt.Errorf("cannot upload sources to the PVC: %w", err)
147167
}
148168
}
149169

150170
err = createAndApplyPipelineTemplate(f, namespace, labels)
151171
if err != nil {
152172
if !k8serrors.IsAlreadyExists(err) {
153173
if k8serrors.IsNotFound(err) {
154-
return "", "", fmt.Errorf("problem creating pipeline, missing tekton?: %v", err)
174+
return "", f, fmt.Errorf("problem creating pipeline, missing tekton?: %v", err)
155175
}
156-
return "", "", fmt.Errorf("problem creating pipeline: %v", err)
176+
return "", f, fmt.Errorf("problem creating pipeline: %v", err)
157177
}
158178
}
159179

160-
registry, err := docker.GetRegistry(f.Deploy.Image)
180+
registry, err := docker.GetRegistry(image)
161181
if err != nil {
162-
return "", "", fmt.Errorf("problem in resolving image registry name: %v", err)
182+
return "", f, fmt.Errorf("problem in resolving image registry name: %v", err)
163183
}
164184

165-
creds, err := pp.credentialsProvider(ctx, f.Deploy.Image)
185+
creds, err := pp.credentialsProvider(ctx, image)
166186
if err != nil {
167-
return "", "", err
187+
return "", f, err
168188
}
169189

190+
// TODO(lkingland): This registry defaulting logic
191+
// is either incorrect or in the wrong place. At this stage of the
192+
// process registry should already be defined/defaulted, and this
193+
// function should be creating resources and deploying. Missing
194+
// data (like registry) should have failed early in the process
170195
if registry == name.DefaultRegistry {
171196
registry = authn.DefaultAuthKey
172197
}
198+
if f.Registry == "" {
199+
f.Registry = registry
200+
}
173201

174202
err = k8s.EnsureDockerRegistrySecretExist(ctx, getPipelineSecretName(f), namespace, labels, f.Deploy.Annotations, creds.Username, creds.Password, registry)
175203
if err != nil {
176-
return "", "", fmt.Errorf("problem in creating secret: %v", err)
177-
}
178-
179-
if f.Registry == "" {
180-
f.Registry = registry
204+
return "", f, fmt.Errorf("problem in creating secret: %v", err)
181205
}
182206

183207
err = createAndApplyPipelineRunTemplate(f, namespace, labels)
184208
if err != nil {
185-
return "", "", fmt.Errorf("problem in creating pipeline run: %v", err)
209+
return "", f, fmt.Errorf("problem in creating pipeline run: %v", err)
186210
}
187211

188212
// we need to give k8s time to actually create the Pipeline Run
189213
time.Sleep(1 * time.Second)
190214

191215
newestPipelineRun, err := findNewestPipelineRunWithRetry(ctx, f, namespace, client)
192216
if err != nil {
193-
return "", "", fmt.Errorf("problem in listing pipeline runs: %v", err)
217+
return "", f, fmt.Errorf("problem in listing pipeline runs: %v", err)
194218
}
195219

196220
err = pp.watchPipelineRunProgress(ctx, newestPipelineRun, namespace)
197221
if err != nil {
198222
if !errors.Is(err, context.Canceled) {
199-
return "", "", fmt.Errorf("problem in watching started pipeline run: %v", err)
223+
return "", f, fmt.Errorf("problem in watching started pipeline run: %v", err)
200224
}
201225
// TODO replace deletion with pipeline-run cancellation
202226
_ = client.PipelineRuns(namespace).Delete(context.TODO(), newestPipelineRun.Name, metav1.DeleteOptions{})
203-
return "", "", fmt.Errorf("pipeline run cancelled: %w", context.Canceled)
227+
return "", f, fmt.Errorf("pipeline run cancelled: %w", context.Canceled)
204228
}
205229

206230
newestPipelineRun, err = client.PipelineRuns(namespace).Get(ctx, newestPipelineRun.Name, metav1.GetOptions{})
207231
if err != nil {
208-
return "", "", fmt.Errorf("problem in retriving pipeline run status: %v", err)
232+
return "", f, fmt.Errorf("problem in retriving pipeline run status: %v", err)
209233
}
210234

211235
if newestPipelineRun.Status.GetCondition(apis.ConditionSucceeded).Status == corev1.ConditionFalse {
212236
message := getFailedPipelineRunLog(ctx, client, newestPipelineRun, namespace)
213-
return "", "", fmt.Errorf("function pipeline run has failed with message: \n\n%s", message)
237+
return "", f, fmt.Errorf("function pipeline run has failed with message: \n\n%s", message)
214238
}
215239

216240
kClient, err := knative.NewServingClient(namespace)
217241
if err != nil {
218-
return "", "", fmt.Errorf("problem in retrieving status of deployed function: %v", err)
242+
return "", f, fmt.Errorf("problem in retrieving status of deployed function: %v", err)
219243
}
220244

221245
ksvc, err := kClient.GetService(ctx, f.Name)
222246
if err != nil {
223-
return "", "", fmt.Errorf("problem in retrieving status of deployed function: %v", err)
247+
return "", f, fmt.Errorf("problem in retrieving status of deployed function: %v", err)
224248
}
225249

226250
if ksvc.Generation == 1 {
@@ -230,10 +254,10 @@ func (pp *PipelinesProvider) Run(ctx context.Context, f fn.Function) (string, st
230254
}
231255

232256
if ksvc.Namespace != namespace {
233-
panic("fixme 2")
257+
fmt.Fprintf(os.Stderr, "Warning: Final ksvc namespace %q does not match expected %q", ksvc.Namespace, namespace)
234258
}
235259

236-
return ksvc.Status.URL.String(), ksvc.Namespace, nil
260+
return ksvc.Status.URL.String(), f, nil
237261
}
238262

239263
// Creates tar stream with the function sources as they were in "./source" directory.

0 commit comments

Comments
 (0)