Skip to content

Commit

Permalink
add auth and generate release
Browse files Browse the repository at this point in the history
  • Loading branch information
itsmurugappan committed May 14, 2021
1 parent 3eeb1c3 commit 13211e3
Show file tree
Hide file tree
Showing 254 changed files with 5,172 additions and 15,062 deletions.
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
# Knative Eventing source for GraphQL Subscription

GraphQL Source is a kubernetes resource specifying the GraphQL endpoint and subscription
queries. It is backed by a controller. On creation of this resource a reciever adapter is
created which subscribes to the graph ql endpoint with queries specified.
GraphQL Source is a kubernetes resource specifying the GraphQL endpoint and subscription queries. It is backed by a controller. On creation of this resource a reciever adapter is created which subscribes to the graph ql endpoint with queries specified.

Ideal for tracking incremental changes of an entity. Checkout the example to see how GQL Source
can be put to work.
Ideal for tracking incremental changes of an entity. Checkout the example to see how GQL Source can be put to work.

![](assets/gql-source.jpg)

Expand Down
4 changes: 2 additions & 2 deletions examples/restaurant-app/pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (
"fmt"
"log"
"net/http"
"strings"
"sync"
"time"
"strings"

"github.com/99designs/gqlgen/handler"
"github.com/allegro/bigcache"
Expand Down Expand Up @@ -44,7 +44,7 @@ func loadInitialMenu(c *bigcache.BigCache) {
"Appetizer-ChickenWings": {ItemType: schema.ItemTypesAppetizer, Name: "Chicken Wings"},
"Entree-ChickenScampi": {ItemType: schema.ItemTypesEntree, Name: "Chicken Scampi"},
"Entree-CheeseRavioli": {ItemType: schema.ItemTypesEntree, Name: "Cheese Ravioli"},
"Dessert-Tiramisu": {ItemType: schema.ItemTypesDessert, Name: "Tiramisu"},
"Dessert-Tiramisu": {ItemType: schema.ItemTypesDessert, Name: "Tiramisu"},
})
info, _ := json.Marshal(&schema.Info{
Address: "1000 Orange Ave Cypress California 90630",
Expand Down
2 changes: 1 addition & 1 deletion examples/restaurant-app/pkg/twitter/twitter.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package twitter

import (
"fmt"
"io/ioutil"
"log"
"os"
"fmt"

"github.com/dghubble/go-twitter/twitter"
"github.com/dghubble/oauth1"
Expand Down
6 changes: 2 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,17 @@ go 1.14

require (
github.com/cloudevents/sdk-go/v2 v2.2.0
github.com/google/licenseclassifier v0.0.0-20200708223521-3d09a0ea2f39
github.com/google/uuid v1.1.1
github.com/itsmurugappan/graphql v0.0.0-00010101000000-000000000000
github.com/matryer/is v1.4.0 // indirect
go.uber.org/zap v1.15.0
k8s.io/api v0.18.7-rc.0
k8s.io/apimachinery v0.18.7-rc.0
k8s.io/client-go v11.0.1-0.20190805182717-6502b5e7b1b5+incompatible
k8s.io/code-generator v0.18.6
k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29
knative.dev/eventing v0.17.1
knative.dev/hack v0.0.0-20210428122153-93ad9129c268
knative.dev/pkg v0.0.0-20200819202314-b5411f2221aa
knative.dev/test-infra v0.0.0-20200819210814-f578ab25945b
knative.dev/test-infra v0.0.0-20200819210814-f578ab25945b // indirect
)

replace (
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1871,6 +1871,8 @@ knative.dev/eventing v0.17.1 h1:E5s1v4T/4S2l66iZyNUU3cA1GwRUqA0C7uObVCwAYNU=
knative.dev/eventing v0.17.1/go.mod h1:9NwCSwLnMCKmgz3YQBNax18mSgBjud8CvfsUUVOZ1sA=
knative.dev/eventing-contrib v0.6.1-0.20190723221543-5ce18048c08b/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
knative.dev/eventing-contrib v0.11.2/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
knative.dev/hack v0.0.0-20210428122153-93ad9129c268 h1:lBIj9Epd9UQ55NEaHzAdY/UZbuaegCdGPKVC2+Z68Q0=
knative.dev/hack v0.0.0-20210428122153-93ad9129c268/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI=
knative.dev/pkg v0.0.0-20191101194912-56c2594e4f11/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
knative.dev/pkg v0.0.0-20191111150521-6d806b998379/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
knative.dev/pkg v0.0.0-20200207155214-fef852970f43/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
Expand Down
3 changes: 3 additions & 0 deletions hack/boilerplate.go.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/*
Copyright 2021 Murugappan Sevugan Chetty
*/
17 changes: 1 addition & 16 deletions hack/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,7 @@

package tools

// This package imports things required by this repository, to force `go mod` to see them as dependencies
import (
_ "k8s.io/code-generator"
_ "knative.dev/test-infra/scripts"

// codegen: hack/generate-knative.sh
_ "knative.dev/hack"
_ "knative.dev/pkg/hack"

_ "k8s.io/code-generator/cmd/client-gen"
_ "k8s.io/code-generator/cmd/deepcopy-gen"
_ "k8s.io/code-generator/cmd/defaulter-gen"
_ "k8s.io/code-generator/cmd/informer-gen"
_ "k8s.io/code-generator/cmd/lister-gen"
_ "k8s.io/kube-openapi/cmd/openapi-gen"
_ "knative.dev/pkg/codegen/cmd/injection-gen"

// Licenseclassifier
_ "github.com/google/licenseclassifier"
)
39 changes: 21 additions & 18 deletions hack/update-codegen.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,38 @@ set -o errexit
set -o nounset
set -o pipefail

export GO111MODULE=on
source $(dirname $0)/../vendor/knative.dev/hack/codegen-library.sh

# If we run with -mod=vendor here, then generate-groups.sh looks for vendor files in the wrong place.
export GOFLAGS=-mod=

if [ -z "${GOPATH:-}" ]; then
export GOPATH=$(go env GOPATH)
fi

REPO_ROOT=$(dirname ${BASH_SOURCE})/..
CODEGEN_PKG=${CODEGEN_PKG:-$(cd ${REPO_ROOT}; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
echo "=== Update Codegen for $MODULE_NAME"

KNATIVE_CODEGEN_PKG=${KNATIVE_CODEGEN_PKG:-$(cd ${REPO_ROOT}; ls -d -1 ./vendor/knative.dev/pkg 2>/dev/null || echo ../pkg)}
group "Kubernetes Codegen"

# generate the code with:
# --output-base because this script should also be able to run inside the vendor dir of
# k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir
# instead of the $GOPATH directly. For normal projects this can be dropped.
chmod +x ${CODEGEN_PKG}/generate-groups.sh
${CODEGEN_PKG}/generate-groups.sh "deepcopy,client,informer,lister" \
github.com/itsmurugappan/gql-source/pkg/client github.com/itsmurugappan/gql-source/pkg/apis \
"sources:v1alpha1" \
--go-header-file ${REPO_ROOT}/hack/boilerplate/boilerplate.go.txt
"github.com/itsmurugappan/gql-source/pkg/client" "github.com/itsmurugappan/gql-source/pkg/apis" \
"sources:v1alpha1" \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate.go.txt

group "Knative Codegen"

# Knative Injection
chmod +x ${KNATIVE_CODEGEN_PKG}/hack/generate-knative.sh
${KNATIVE_CODEGEN_PKG}/hack/generate-knative.sh "injection" \
github.com/itsmurugappan/gql-source/pkg/client github.com/itsmurugappan/gql-source/pkg/apis \
"sources:v1alpha1" \
--go-header-file ${REPO_ROOT}/hack/boilerplate/boilerplate.go.txt
"github.com/itsmurugappan/gql-source/pkg/client" "github.com/itsmurugappan/gql-source/pkg/apis" \
"sources:v1alpha1" \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate.go.txt

group "Deepcopy Gen"

# Depends on generate-groups.sh to install bin/deepcopy-gen
${GOPATH}/bin/deepcopy-gen \
-O zz_generated.deepcopy \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate.go.txt

# Make sure our dependencies are up-to-date
${REPO_ROOT}/hack/update-deps.sh
# save license
go-licenses save "./vendor/..." --save_path="./third_party/VENDOR-LICENSE/" --force
14 changes: 8 additions & 6 deletions pkg/adapter/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,21 @@ func NewEnvConfig() adapter.EnvConfigAccessor {
}

type Adapter struct {
config *adapterConfig
logger *zap.Logger
client cloudevents.Client
config *adapterConfig
logger *zap.Logger
client cloudevents.Client
authClient authClient
}

func NewAdapter(ctx context.Context, processed adapter.EnvConfigAccessor, ceClient cloudevents.Client) adapter.Adapter {
logger := logging.FromContext(ctx).Desugar()
config := processed.(*adapterConfig)

return &Adapter{
config: config,
client: ceClient,
logger: logger,
config: config,
logger: logger,
client: ceClient,
authClient: newAuthClient(ctx),
}
}

Expand Down
111 changes: 111 additions & 0 deletions pkg/adapter/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package adapter

import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"

"go.uber.org/zap"
"knative.dev/pkg/logging"
)

const (
userSecret = "GQL_OIDC_USER"
passwordSecret = "GQL_OIDC_PASSWORD"
clientIDSecret = "GQL_OIDC_CLIENT"
tokenUrl = "GQL_AUTH_TOKEN_URL"
timeOut time.Duration = 5 * time.Second
)

type authClient interface {
setAuthHeader(ctx context.Context, header http.Header)
}

func newAuthClient(ctx context.Context) authClient {
switch {
case os.Getenv("GQL_OIDC_AUTH_ENABLE") == "true":
return newKCClient(ctx)
// implement other auth methods here
}
return nil
}

type kcClient struct {
client *http.Client
authData url.Values
tokenUrl string
}

func newKCClient(ctx context.Context) *kcClient {
c := &http.Client{
Timeout: timeOut,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
DisableKeepAlives: true,
},
}
data := url.Values{}
data.Set("username", getSecret(ctx, userSecret))
data.Set("password", getSecret(ctx, passwordSecret))
data.Set("grant_type", "password")
data.Set("client_id", getSecret(ctx, clientIDSecret))
return &kcClient{
c,
data,
getSecret(ctx, tokenUrl),
}
}

// fetchToken fetches token from keycloak endpoint
func (c *kcClient) setAuthHeader(ctx context.Context, header http.Header) {
logger := logging.FromContext(ctx)
res, err := c.client.Do(c.constructTokenRequest(ctx))
if err != nil {
logger.Error("error getting token from keycloak", zap.Error(err))
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
logger.Error("error getting body from response", zap.Error(err))
return
}
var token map[string]interface{}
err = json.Unmarshal(body, &token)
if err != nil {
logger.Error("fail to parse response body to get token", zap.Error(err))
return
}
if token["access_token"] != nil {
header.Add("Authorization", fmt.Sprintf("Bearer %s", token["access_token"].(string)))
}
return
}

func (c *kcClient) constructTokenRequest(ctx context.Context) *http.Request {
r, err := http.NewRequest("POST", c.tokenUrl, strings.NewReader(c.authData.Encode()))
if err != nil {
logging.FromContext(ctx).Error("fail to construct request to get token", zap.Error(err))
}
r.Close = true
r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
r.Header.Add("Content-Length", strconv.Itoa(len(c.authData.Encode())))

return r
}

func getSecret(ctx context.Context, envName string) string {
secret, present := os.LookupEnv(envName)
if !present {
logging.FromContext(ctx).Fatal("Unable to retrieve auth creds")
}
return secret
}
6 changes: 5 additions & 1 deletion pkg/adapter/gql.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ func (a *Adapter) subscribe(ctx context.Context, q string) {
ctx = cloudevents.ContextWithRetriesExponentialBackoff(ctx, interval, retry)

client := graphql.NewClient(a.config.GqlServer)
subClient, err := client.SubscriptionClient(ctx, http.Header{"Cache-Control": []string{"no-cache"}})
header := http.Header{
"Cache-Control": []string{"no-cache"},
}
a.authClient.setAuthHeader(ctx, header)
subClient, err := client.SubscriptionClient(ctx, header)
if err != nil {
a.logger.Error("Error making subscription client", zap.Error(err))
return
Expand Down
14 changes: 4 additions & 10 deletions pkg/apis/sources/v1alpha1/gql_source_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ func (s *GqlSourceStatus) GetCondition(t apis.ConditionType) *apis.Condition {
return condSet.Manage(s).GetCondition(t)
}

func (s *GqlSourceStatus) GetTopLevelCondition() *apis.Condition {
return condSet.Manage(s).GetTopLevelCondition()
}

// IsReady returns true if the resource is ready overall.
func (s *GqlSourceStatus) IsReady() bool {
return condSet.Manage(s).IsHappy()
Expand All @@ -62,16 +66,6 @@ func (gs *GqlSourceStatus) MarkNoSink(reason, messageFormat string, messageA ...
condSet.Manage(gs).MarkFalse(GqlSourceConditionSinkProvided, reason, messageFormat, messageA...)
}

func DeploymentIsAvailable(d *appsv1.DeploymentStatus, def bool) bool {
// Check if the Deployment is available.
for _, cond := range d.Conditions {
if cond.Type == appsv1.DeploymentAvailable {
return cond.Status == "True"
}
}
return def
}

// MarkDeployed sets the condition that the source has been deployed.
func (s *GqlSourceStatus) MarkDeployed(d *appsv1.Deployment) {
if duck.DeploymentIsAvailable(&d.Status, false) {
Expand Down
Loading

0 comments on commit 13211e3

Please sign in to comment.