Skip to content

Commit

Permalink
Refactor functions to not be members of Sink 🚰
Browse files Browse the repository at this point in the history
Just like with reconcilers / controllers, adding functions of as members
of the Sink (or controller) object itself has some downsides: it's hard
to tell what the responsibilities of the functions are and how they are
coupled to the Sink, and it's harder to write more focused unit tests.

I am working on tektoncd#258 in another branch and in that branch I wanted to
make some updates to the unit tests for this logic; it's easier to do if
it's factored out a bit.

There is more factoring we can do here: now that the functions are moved
out of Sink we can make decisions around their interfaces and maybe make
them even more focused and unit testable.

(Though it was a happy surprise to see they were unit tested even though
they were members of Sink!)
  • Loading branch information
bobcatfish committed Dec 12, 2019
1 parent e6881e2 commit 5af787c
Show file tree
Hide file tree
Showing 5 changed files with 478 additions and 381 deletions.
121 changes: 121 additions & 0 deletions pkg/resources/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
Copyright 2019 The Tekton Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package resources

import (
"encoding/json"
"fmt"
"strings"

"k8s.io/client-go/dynamic"

"go.uber.org/zap"

"golang.org/x/xerrors"

triggersv1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1"
"github.com/tidwall/sjson"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
discoveryclient "k8s.io/client-go/discovery"
)

// FindAPIResource returns the APIResource definition using the discovery client c.
func FindAPIResource(apiVersion, kind string, c discoveryclient.ServerResourcesInterface) (*metav1.APIResource, error) {
//resourceList, err := r.DiscoveryClient.ServerResourcesForGroupVersion(apiVersion)
resourceList, err := c.ServerResourcesForGroupVersion(apiVersion)
if err != nil {
return nil, xerrors.Errorf("Error getting kubernetes server resources for apiVersion %s: %s", apiVersion, err)
}
for _, apiResource := range resourceList.APIResources {
if apiResource.Kind != kind {
continue
}
r := &apiResource
// Resolve GroupVersion from parent list to have consistent resource identifiers.
if r.Version == "" || r.Group == "" {
gv, err := schema.ParseGroupVersion(resourceList.GroupVersion)
if err != nil {
return nil, xerrors.Errorf("error parsing parsing GroupVersion: %v", err)
}
r.Group = gv.Group
r.Version = gv.Version
}
return r, nil
}
return nil, xerrors.Errorf("Error could not find resource with apiVersion %s and kind %s", apiVersion, kind)
}

// Create uses the kubeClient to create the resource defined in the
// TriggerResourceTemplate and returns any errors with this process
func Create(logger *zap.SugaredLogger, rt json.RawMessage, triggerName, eventID, elName, elNamespace string, c discoveryclient.ServerResourcesInterface, dc dynamic.Interface) error {
rt, err := AddLabels(rt, map[string]string{
triggersv1.EventListenerLabelKey: elName,
triggersv1.EventIDLabelKey: eventID,
triggersv1.TriggerLabelKey: triggerName,
})
if err != nil {
return err
}

// Assume the TriggerResourceTemplate is valid (it has an apiVersion and Kind)
data := new(unstructured.Unstructured)
if err := data.UnmarshalJSON(rt); err != nil {
return err
}

namespace := data.GetNamespace()
// Default the resource creation to the EventListenerNamespace if not found in the resource template
if namespace == "" {
namespace = elNamespace
}

// Resolve resource kind to the underlying API Resource type.
apiResource, err := FindAPIResource(data.GetAPIVersion(), data.GetKind(), c)
if err != nil {
return err
}

name := data.GetName()
if name == "" {
name = data.GetGenerateName()
}
logger.Infof("Generating resource: kind: %+v, name: %s", apiResource, name)

gvr := schema.GroupVersionResource{
Group: apiResource.Group,
Version: apiResource.Version,
Resource: apiResource.Name,
}

_, err = dc.Resource(gvr).Namespace(namespace).Create(data, metav1.CreateOptions{})
return err
}

// AddLabels adds autogenerated Tekton labels to created resources.
func AddLabels(rt json.RawMessage, labels map[string]string) (json.RawMessage, error) {
var err error
for k, v := range labels {
l := fmt.Sprintf("metadata.labels.%s/%s", triggersv1.LabelEscape, strings.TrimLeft(k, "/"))
rt, err = sjson.SetBytes(rt, l, v)
if err != nil {
return rt, err
}
}
return rt, err
}
Loading

0 comments on commit 5af787c

Please sign in to comment.