Skip to content

Commit

Permalink
- moved GenericGateway to a separate package
Browse files Browse the repository at this point in the history
- altered brig project create so that genericGateway secret is generated on advanced options
  • Loading branch information
dgkanatsios committed Jan 28, 2019
1 parent b3a1e8b commit 457a4d0
Show file tree
Hide file tree
Showing 20 changed files with 354 additions and 187 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/brigade-controller/rootfs/brigade-controller
/brigade-cr-gateway/rootfs/brigade-cr-gateway
/brigade-github-gateway/rootfs/brigade-github-gateway
/brigade-generic-gateway/rootfs/brigade-generic-gateway
/brigade-vacuum/rootfs/brigade-vacuum
/brigade-worker/dist/
/brigade-worker/doc/
Expand Down
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ DOCKER_REGISTRY ?= deis
DOCKER_BUILD_FLAGS :=
LDFLAGS :=


# Helm chart/release defaults
BRIGADE_RELEASE ?= brigade-server
BRIGADE_NAMESPACE ?= default
BRIGADE_GITHUB_GW_SERVICE := $(BRIGADE_RELEASE)-brigade-github-gw
BRIGADE_GITHUB_GW_PORT := 7744

BINS = brigade-api brigade-controller brigade-github-gateway brigade-cr-gateway brigade-vacuum brig
IMAGES = brigade-api brigade-controller brigade-github-gateway brigade-cr-gateway brigade-vacuum brig brigade-worker git-sidecar
BINS = brigade-api brigade-controller brigade-github-gateway brigade-cr-gateway brigade-generic-gateway brigade-vacuum brig
IMAGES = brigade-api brigade-controller brigade-github-gateway brigade-cr-gateway brigade-generic-gateway brigade-vacuum brig brigade-worker git-sidecar


.PHONY: echo-images
echo-images:
Expand Down
41 changes: 32 additions & 9 deletions brig/cmd/brig/commands/project_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"

"github.com/Masterminds/goutils"
Expand Down Expand Up @@ -296,15 +297,6 @@ func projectCreatePrompts(p *brigade.Project, store storage.Store) error {
fmt.Printf("Auto-generated a Shared Secret: %q\n", p.SharedSecret)
}

if strings.TrimSpace(p.GenericWebhookSecret) == "" {
var err error
p.GenericWebhookSecret, err = goutils.RandomAlphaNumeric(5)
if err != nil {
return fmt.Errorf("Error in generating Generic Webhook Secret: %s", err.Error())
}
fmt.Printf("Auto-generated Generic Webhook Secret (for Brigade API Server): %s\n", p.GenericWebhookSecret)
}

configureGitHub := false
if err := survey.AskOne(&survey.Confirm{
Message: "Configure GitHub Access?",
Expand Down Expand Up @@ -538,9 +530,40 @@ func projectAdvancedPrompts(p *brigade.Project, store storage.Store) error {
p.DefaultScript = loadFileStr(fname)
}

err = survey.AskOne(&survey.Input{
Message: "Secret for the Generic Gateway (alphanumeric characters only). Press Enter if you want it to be auto-generated",
Help: "This is the secret that secures the Generic Gateway. Only alphanumeric characters are accepted. Provide an empty string if you want an auto-generated one",
}, &p.GenericGatewaySecret, genericGatewaySecretValidator)
if err != nil {
return fmt.Errorf(abort, err)
}

// user pressed Enter key, so let's auto-generate a GenericGateway secret
if p.GenericGatewaySecret == "" {
var err error
p.GenericGatewaySecret, err = goutils.RandomAlphaNumeric(5)
if err != nil {
return fmt.Errorf("Error in generating Generic Gateway Secret: %s", err.Error())
}
fmt.Printf("Auto-generated Generic Gateway Secret: %s\n", p.GenericGatewaySecret)
}

return nil
}

// genericGatewaySecretValidator validates the secret provided by user for the Generic Gateway
// this can be either "" (so it will be auto-generated) or alphanumeric
func genericGatewaySecretValidator(val interface{}) error {
re := regexp.MustCompile("^[a-zA-Z0-9]*$")
if val.(string) == "" {
return nil
}
if re.MatchString(val.(string)) {
return nil
}
return fmt.Errorf("Generic Gateway secret should only contain alphanumeric characters")
}

// loadFileValidator validates that a file exists and can be read.
func loadFileValidator(val interface{}) error {
name := os.ExpandEnv(val.(string))
Expand Down
23 changes: 23 additions & 0 deletions brig/cmd/brig/commands/project_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,26 @@ func TestReplaceNewlines(t *testing.T) {
t.Fatalf("Expected %q, got %q", expect, got)
}
}

func TestGenericGatewaySecretValidator(t *testing.T) {
s1 := "asdf"
if err := genericGatewaySecretValidator(s1); err != nil {
t.Fatal("Expected nil, got error")
}
s2 := "AsDf1"
if err := genericGatewaySecretValidator(s2); err != nil {
t.Fatal("Expected nil, got error")
}
s3 := "jfdkfd^&"
if err := genericGatewaySecretValidator(s3); err == nil {
t.Fatal("Expected error, got nil")
}
s4 := ""
if err := genericGatewaySecretValidator(s4); err != nil {
t.Fatal("Expected nil, got error")
}
s5 := " "
if err := genericGatewaySecretValidator(s5); err == nil {
t.Fatal("Expected error, got nil")
}
}
43 changes: 5 additions & 38 deletions brigade-api/cmd/brigade-api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/Azure/brigade/pkg/api"
"github.com/Azure/brigade/pkg/brigade"
"github.com/Azure/brigade/pkg/storage/kube"
"github.com/Azure/brigade/pkg/webhook"

restful "github.com/emicklei/go-restful"
restfulspec "github.com/emicklei/go-restful-openapi"
Expand All @@ -20,12 +19,11 @@ import (
)

var (
apiPort string
kubeconfig string
master string
namespace string
verbose bool
genericGateway bool
apiPort string
kubeconfig string
master string
namespace string
verbose bool
)

func init() {
Expand All @@ -34,7 +32,6 @@ func init() {
flag.StringVar(&namespace, "namespace", defaultNamespace(), "kubernetes namespace")
flag.StringVar(&apiPort, "api-port", defaultAPIPort(), "TCP port to use for brigade-api")
flag.BoolVar(&verbose, "verbose", false, "enables detailed logging of http request matching and filter invocation")
flag.BoolVar(&genericGateway, "generic-gateway", false, "enables the generic webhook server")
}

type jobService struct {
Expand All @@ -49,10 +46,6 @@ type projectService struct {
server api.API
}

type genericWebhookService struct {
server api.API
}

type healthService struct {
}

Expand Down Expand Up @@ -168,28 +161,6 @@ func (ps projectService) WebService() *restful.WebService {
return ws
}

func (gh genericWebhookService) WebService() *restful.WebService {
ws := new(restful.WebService)

ws.
Path("/webhook").
Consumes(restful.MIME_JSON).
Produces(restful.MIME_JSON, restful.MIME_XML, "plain/text", "text/javascript")

tags := []string{"genericwebhook"}

ws.Route(ws.POST("/{projectID}/{secret}").To(webhook.NewGenericWebhook(gh.server.Store())).
Doc("create a webhook event").
Metadata(restfulspec.KeyOpenAPITags, tags).
Param(ws.PathParameter("projectID", "identifier of the projectID").DataType("string")).
Param(ws.PathParameter("secret", "shared secret").DataType("string")).
Writes([]byte{}).
Returns(200, "OK", []byte{}).
Returns(404, "Not Found", nil))

return ws
}

func (hs healthService) WebService() *restful.WebService {
ws := new(restful.WebService)

Expand Down Expand Up @@ -227,16 +198,12 @@ func main() {
j := jobService{server: storageServer}
b := buildService{server: storageServer}
p := projectService{server: storageServer}
gh := genericWebhookService{server: storageServer}
h := healthService{}

restful.DefaultContainer.Add(j.WebService())
restful.DefaultContainer.Add(b.WebService())
restful.DefaultContainer.Add(p.WebService())
restful.DefaultContainer.Add(h.WebService())
if genericGateway {
restful.DefaultContainer.Add(gh.WebService())
}
restful.DefaultContainer.Filter(NCSACommonLogFormatLogger())

config := restfulspec.Config{
Expand Down
2 changes: 1 addition & 1 deletion brigade-cr-gateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ If you do not see your preferred registry above, you can do any of the following
us.
2. Write a custom gateway. This may be necessary if your chosen container registry
uses even moderately exotic auth patterns.
3. File an issue in the issue queue here, and see if you can rally some support
3. File an issue in the issue queue [here](https://github.com/Azure/brigade/issues), and see if you can rally some support
for building one.
10 changes: 10 additions & 0 deletions brigade-generic-gateway/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM alpine:3.8

RUN apk update && apk add --no-cache \
ca-certificates \
git \
&& update-ca-certificates

COPY rootfs/brigade-generic-gateway /usr/bin/brigade-generic-gateway

CMD /usr/bin/brigade-generic-gateway
3 changes: 3 additions & 0 deletions brigade-generic-gateway/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Brigade Generic Gateway

This server provides a generic gateway. You can check [here](https://azure.github.io/brigade/topics/genericgateway.html) for the relevant documentation.
73 changes: 73 additions & 0 deletions brigade-generic-gateway/cmd/brigade-generic-gateway/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package main

import (
"flag"
"log"
"net/http"
"os"

gin "gopkg.in/gin-gonic/gin.v1"

v1 "k8s.io/api/core/v1"

"github.com/Azure/brigade/pkg/storage"
"github.com/Azure/brigade/pkg/storage/kube"
"github.com/Azure/brigade/pkg/webhook"
)

var (
kubeconfig string
master string
namespace string
)

func init() {
flag.StringVar(&kubeconfig, "kubeconfig", "", "absolute path to the kubeconfig file")
flag.StringVar(&master, "master", "", "master url")
flag.StringVar(&namespace, "namespace", defaultNamespace(), "kubernetes namespace")
}

func main() {
flag.Parse()

clientset, err := kube.GetClient(master, kubeconfig)
if err != nil {
log.Fatal(err)
}

if namespace == "" {
namespace = v1.NamespaceDefault
}

store := kube.New(clientset, namespace)

router := newRouter(store)
router.Run(":8000")
}

func newRouter(store storage.Store) *gin.Engine {
router := gin.New()
router.Use(gin.Recovery())

handler := webhook.NewGenericWebhook(store)

events := router.Group("/webhook")
{
events.Use(gin.Logger())
events.POST("/:projectID/:secret", handler)
}

router.GET("/healthz", healthz)
return router
}

func healthz(c *gin.Context) {
c.String(http.StatusOK, http.StatusText(http.StatusOK))
}

func defaultNamespace() string {
if ns, ok := os.LookupEnv("BRIGADE_NAMESPACE"); ok {
return ns
}
return v1.NamespaceDefault
}
72 changes: 72 additions & 0 deletions brigade-generic-gateway/cmd/brigade-generic-gateway/server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package main

import (
"bytes"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"

"github.com/Azure/brigade/pkg/storage/mock"
)

func TestNewRouter(t *testing.T) {
s := mock.New()
s.ProjectList[0].ID = "brigade-4625a05cf6914e556aa254cb2af234203744de2f"
s.ProjectList[0].Name = "deis/empty-testbed"
s.ProjectList[0].GenericGatewaySecret = "mysecret"
r := newRouter(s)

if r == nil {
t.Fail()
}

ts := httptest.NewServer(r)
defer ts.Close()

res, err := http.Get(ts.URL + "/healthz")
if err != nil {
t.Fatal(err)
}
if res.StatusCode != 200 {
t.Fatalf("Unexpected status on healthz: %s", res.Status)
}

body, err := ioutil.ReadFile("./testdata/genericwebhook.json")
if err != nil {
t.Fatal(err)
}

route400 := "/webhook/brigade-4625a05cf6914e556aa254cb2af234203744de2f_WRONG_URL/mysecret"
res, err = http.Post(ts.URL+route400, "application/json", bytes.NewBuffer(body))
if err != nil {
t.Fatal(err)
}
if res.StatusCode != 400 {
t.Fatalf("Expected 400 status, got: %s", res.Status)
}

route401 := "/webhook/brigade-4625a05cf6914e556aa254cb2af234203744de2f/mysecret2"
res, err = http.Post(ts.URL+route401, "application/json", bytes.NewBuffer(body))
if err != nil {
t.Fatal(err)
}
if res.StatusCode != 401 {
t.Fatalf("Expected 401 status, got: %s", res.Status)
}

corruptbody, err := ioutil.ReadFile("./testdata/genericwebhook.json.corrupt")
if err != nil {
t.Fatal(err)
}

routeCorruptBody := "/webhook/brigade-4625a05cf6914e556aa254cb2af234203744de2f/mysecret"
res, err = http.Post(ts.URL+routeCorruptBody, "application/json", bytes.NewBuffer(corruptbody))
if err != nil {
t.Fatal(err)
}
if res.StatusCode != 400 {
t.Fatalf("Expected 400 status, got: %s", res.Status)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"ref": "0.2",
"commit": "1e175a8"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"ref": "0.2",
"commit": "1e175a8"
BLABLABLA
}
Loading

0 comments on commit 457a4d0

Please sign in to comment.