Work on mock server.
diff --git a/README.md b/README.md
index 134b6bd..86cfed4 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,7 @@
# Istio Apigee Adapter
This workspace holds an apigee adapter for Istio.
+
+## Installation
+
+In order to use this, right now, you need a build of Istio
\ No newline at end of file
diff --git a/WORKSPACE b/WORKSPACE
index c29e9ff..a7ddb8c 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -45,6 +45,13 @@
importpath = "github.com/golang/protobuf",
)
+new_git_repository(
+ name = "com_github_googleapis_googleapis",
+ build_file = "bazel/BUILD.googleapis",
+ commit = "13ac2436c5e3d568bd0e938f6ed58b77a48aba15",
+ remote = "https://github.com/googleapis/googleapis.git",
+)
+
new_go_repository(
name = "com_github_hashicorp_go_multierror",
commit = "ed905158d87462226a13fe39ddf685ea65f1c11f",
@@ -57,9 +64,8 @@
importpath = "github.com/hashicorp/errwrap",
)
-new_git_repository(
- name = "com_github_googleapis_googleapis",
- build_file = "bazel/BUILD.googleapis",
- commit = "13ac2436c5e3d568bd0e938f6ed58b77a48aba15",
- remote = "https://github.com/googleapis/googleapis.git",
-)
\ No newline at end of file
+new_go_repository(
+ name = "com_github_julienschmidt_httprouter",
+ commit = "975b5c4c7c21c0e3d2764200bf2aa8e34657ae6e",
+ importpath = "github.com/julienschmidt/httprouter"
+)
diff --git a/common/BUILD b/common/BUILD
new file mode 100644
index 0000000..5c09cc7
--- /dev/null
+++ b/common/BUILD
@@ -0,0 +1,12 @@
+package(default_visibility = ["//visibility:public"])
+
+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+
+go_library(
+ name = "go_default_library",
+ srcs = [
+ "types.go",
+ ],
+ deps = [
+ ],
+)
diff --git a/common/types.go b/common/types.go
new file mode 100644
index 0000000..da90279
--- /dev/null
+++ b/common/types.go
@@ -0,0 +1,25 @@
+package common
+
+type Attribute struct {
+ Name string `json:"name"`
+ Value string `json:"value"`
+}
+
+type APIProduct struct {
+ APIResources []string `json:"apiResources"`
+ ApprovalType string `json:"approvalType"`
+ Attributes []Attribute `json:"attributes"`
+ CreatedAt int64 `json:"createdAt"`
+ CreatedBy string `json:"createdBy"`
+ Description string `json:"description"`
+ DisplayName string `json:"displayName"`
+ Environments []string `json:"environments"`
+ LastModifiedAt int64 `json:"lastModifiedAt"`
+ LastModifiedBy string `json:"lastModifiedBy"`
+ Name string `json:"name"`
+ Proxies []string `json:"proxies"`
+ Quota string `json:"quota"`
+ QuotaInterval string `json:"quotaInterval"`
+ QuotaTimeUnit string `json:"quotaTimeUnit"`
+ Scopes []string `json:"scopes"`
+}
diff --git a/mock/BUILD b/mock/BUILD
new file mode 100644
index 0000000..84be835
--- /dev/null
+++ b/mock/BUILD
@@ -0,0 +1,24 @@
+package(default_visibility = ["//visibility:public"])
+
+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+
+go_library(
+ name = "go_default_library",
+ srcs = [
+ "keys.go",
+ "mockserver.go",
+ ],
+ deps = [
+ "//common:go_default_library",
+ "@com_github_julienschmidt_httprouter//:go_default_library",
+ ],
+)
+
+go_test(
+ name = "small_tests",
+ size = "small",
+ srcs = [
+ "mockserver_test.go",
+ ],
+ library = ":go_default_library",
+)
diff --git a/mock/keys.go b/mock/keys.go
new file mode 100644
index 0000000..d5170cf
--- /dev/null
+++ b/mock/keys.go
@@ -0,0 +1,55 @@
+package mock
+
+import (
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "time"
+ "encoding/pem"
+ "math/big"
+)
+
+var mockKeyPEM, mockCertPEM []byte
+
+func makeKeys() {
+ key, err := rsa.GenerateKey(rand.Reader, 2048)
+ if err != nil {
+ panic(err.Error())
+ }
+
+ mockKeyPEM = pem.EncodeToMemory(&pem.Block{
+ Type: "RSA PRIVATE KEY",
+ Bytes: x509.MarshalPKCS1PrivateKey(key),
+ })
+
+ templ := makeCertTemplate()
+
+ certBytes, err := x509.CreateCertificate(rand.Reader, templ, templ, key.Public(), key)
+ if err != nil {
+ panic(err.Error())
+ }
+
+ mockCertPEM = pem.EncodeToMemory(&pem.Block{
+ Type: "CERTIFICATE",
+ Bytes: certBytes,
+ })
+}
+
+func makeCertTemplate() *x509.Certificate {
+ now := time.Now()
+
+ return &x509.Certificate{
+ SerialNumber: big.NewInt(1),
+ Subject: pkix.Name{
+ Country: []string{"US"},
+ Organization: []string{"Google"},
+ OrganizationalUnit: []string{"Cloud"},
+ Locality: []string{"Mountain View"},
+ Province: []string{"CA"},
+ CommonName: "mockserver",
+ },
+ NotBefore: now,
+ NotAfter: now.Add(time.Hour),
+ }
+}
\ No newline at end of file
diff --git a/mock/mockserver.go b/mock/mockserver.go
new file mode 100644
index 0000000..1d4392d
--- /dev/null
+++ b/mock/mockserver.go
@@ -0,0 +1,88 @@
+package mock
+
+import (
+ "fmt"
+ "net"
+ "net/http"
+ "sync"
+ "encoding/json"
+ "time"
+ "github.com/julienschmidt/httprouter"
+ "github.com/apid/istioApigeeAdapter/common"
+)
+
+var mockInit = &sync.Once{}
+
+type MockServer struct {
+ listener net.Listener
+}
+
+func StartMockServer(port int) (*MockServer, error) {
+ mockInit.Do(makeKeys)
+
+ l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
+ if err != nil {
+ return nil, err
+ }
+
+ ms := &MockServer{
+ listener: l,
+ }
+
+ router := httprouter.New()
+ router.GET("/publicKey", ms.getPublicKey)
+ router.GET("/products", ms.getProducts)
+
+ go func() {
+ http.Serve(l, router)
+ }()
+
+ return ms, nil
+}
+
+func (m *MockServer) Address() string {
+ return m.listener.Addr().String()
+}
+
+func (m *MockServer) Stop() {
+ m.listener.Close()
+}
+
+func (m *MockServer) getPublicKey(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ w.Header().Set("content-type", "text/plain")
+ w.Write(mockCertPEM)
+}
+
+func (m *MockServer) getProducts(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
+ now := time.Now()
+ p := []common.APIProduct{
+ {
+ Name: "First",
+ DisplayName: "First Product",
+ Description: "First test product",
+ CreatedAt: now.UnixNano(),
+ CreatedBy: "foo@bar.com",
+ LastModifiedAt: now.UnixNano(),
+ LastModifiedBy: "foo@bar.com",
+
+ APIResources: []string{"/**"},
+ ApprovalType: "auto",
+ Attributes: []common.Attribute{
+ {
+ Name: "access",
+ Value: "public",
+ },
+ },
+ Environments: []string{"test", "prod"},
+ Proxies: []string{"ProxyOne"},
+ Quota: "10",
+ QuotaInterval: "1",
+ QuotaTimeUnit: "minute",
+ },
+ }
+
+ w.Header().Set("content-type", "application/json")
+
+ enc := json.NewEncoder(w)
+ enc.Encode(p)
+}
\ No newline at end of file
diff --git a/mock/mockserver_test.go b/mock/mockserver_test.go
new file mode 100644
index 0000000..8f8fd23
--- /dev/null
+++ b/mock/mockserver_test.go
@@ -0,0 +1,88 @@
+package mock
+
+import (
+ "fmt"
+ "net/http"
+ "os"
+ "testing"
+ "encoding/pem"
+ "encoding/json"
+ "io/ioutil"
+ "crypto/x509"
+ "github.com/apid/istioApigeeAdapter/common"
+)
+
+var testMockServer *MockServer
+
+func TestMain(m *testing.M) {
+ var err error
+ testMockServer, err = StartMockServer(0)
+ if err != nil {
+ panic(err.Error())
+ }
+
+ result := m.Run()
+ testMockServer.Stop()
+ os.Exit(result)
+}
+
+func TestServerSanity(t *testing.T) {
+ if mockKeyPEM == nil {
+ t.Fatal("Expected mock key to be generated")
+ }
+ if mockCertPEM == nil {
+ t.Fatal("Expected mock cert to be generated")
+ }
+}
+
+func TestPublicKey(t *testing.T) {
+ resp, err := http.Get(fmt.Sprintf("http://%s/publicKey", testMockServer.Address()))
+ if err != nil {
+ t.Fatalf("Network error: %s", err)
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != 200 {
+ t.Fatalf("Got HTTP status code %d", resp.StatusCode)
+ }
+
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ t.Fatalf("Error reading body: %s", err)
+ }
+ pb, _ := pem.Decode(body)
+ if pb == nil {
+ t.Fatalf("Failed decoding public key body \"%s\"", string(body))
+ }
+ if pb.Type != "CERTIFICATE" {
+ t.Fatalf("Got back invalid PEM type %s", pb.Type)
+ }
+
+ cert, err := x509.ParseCertificate(pb.Bytes)
+ if err != nil {
+ t.Fatalf("Error decoding certificate: %s", err)
+ }
+ if cert.Subject.CommonName != "mockserver" {
+ t.Fatalf("Common name does not match \"mockserver\": \"%s\"", cert.Subject.CommonName)
+ }
+}
+
+func TestProducts(t *testing.T) {
+ resp, err := http.Get(fmt.Sprintf("http://%s/products", testMockServer.Address()))
+ if err != nil {
+ t.Fatalf("Network error: %s", err)
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != 200 {
+ t.Fatalf("Got HTTP status code %d", resp.StatusCode)
+ }
+ if resp.Header.Get("content-type") != "application/json" {
+ t.Fatalf("Invalid content typs %s", resp.Header.Get("content-type"))
+ }
+
+ dec := json.NewDecoder(resp.Body)
+ var products []common.APIProduct
+ err = dec.Decode(&products)
+ if err != nil {
+ t.Fatalf("Error decoding response json: %s", err)
+ }
+}
\ No newline at end of file