blob: ccd3b0c4c5d3af207df19e0aa4603d3dbffecf57 [file] [log] [blame]
// Copyright 2017 Google Inc.
//
// 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 apiGatewayConfDeploy
import (
"encoding/json"
"io/ioutil"
"net/http"
"net/url"
"crypto/rand"
"fmt"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
mathrand "math/rand"
"os"
"strconv"
"time"
)
const (
apiTestUrl = "http://127.0.0.1:9000"
testBlobId = "gcs:SHA-512:39ca7ae89bb9468af34df8bc873748b4035210c91bcc01359c092c1d51364b5f3df06bc69a40621acfaa46791af9ea41bc0f3429a84738ba1a7c8d394859601a"
)
var _ = Describe("api", func() {
var testCount int
var dummyDbMan *dummyDbManager
var testApiMan *apiManager
var _ = BeforeEach(func() {
testCount += 1
dummyDbMan = &dummyDbManager{}
testApiMan = &apiManager{
dbMan: dummyDbMan,
deploymentsEndpoint: deploymentsEndpoint + strconv.Itoa(testCount),
blobEndpoint: blobEndpointPath + strconv.Itoa(testCount) + "/{blobId}",
eTag: int64(testCount * 10),
deploymentsChanged: make(chan interface{}, 5),
addSubscriber: make(chan chan deploymentsResult),
removeSubscriber: make(chan chan deploymentsResult),
}
testApiMan.InitAPI()
time.Sleep(100 * time.Millisecond)
})
var _ = AfterEach(func() {
testApiMan = nil
})
Context("GET /configurations", func() {
It("should get empty set if no deployments", func() {
// setup http client
uri, err := url.Parse(apiTestUrl)
Expect(err).Should(Succeed())
uri.Path = deploymentsEndpoint + strconv.Itoa(testCount)
// http get
res, err := http.Get(uri.String())
Expect(err).Should(Succeed())
defer res.Body.Close()
Expect(res.StatusCode).Should(Equal(http.StatusOK))
// parse response
var depRes ApiDeploymentResponse
body, err := ioutil.ReadAll(res.Body)
Expect(err).Should(Succeed())
err = json.Unmarshal(body, &depRes)
Expect(err).Should(Succeed())
// verify response
Expect(len(depRes.ApiDeploymentsResponse)).To(Equal(0))
Expect(depRes.Kind).Should(Equal(kindCollection))
Expect(depRes.Self).Should(Equal(apiTestUrl + deploymentsEndpoint + strconv.Itoa(testCount)))
})
It("should get correct config format", func() {
// setup http client
uri, err := url.Parse(apiTestUrl)
Expect(err).Should(Succeed())
uri.Path = deploymentsEndpoint + strconv.Itoa(testCount)
// set test data
details := setTestDeployments(dummyDbMan, uri.String())
// http get
res, err := http.Get(uri.String())
Expect(err).Should(Succeed())
defer res.Body.Close()
Expect(res.StatusCode).Should(Equal(http.StatusOK))
// parse response
var depRes ApiDeploymentResponse
body, err := ioutil.ReadAll(res.Body)
Expect(err).Should(Succeed())
err = json.Unmarshal(body, &depRes)
Expect(err).Should(Succeed())
// verify response
Expect(depRes.Kind).Should(Equal(kindCollection))
Expect(depRes.Self).Should(Equal(uri.String()))
Expect(depRes.ApiDeploymentsResponse).Should(Equal(details))
})
It("should get 304 for no change", func() {
// setup http client
uri, err := url.Parse(apiTestUrl)
Expect(err).Should(Succeed())
uri.Path = deploymentsEndpoint + strconv.Itoa(testCount)
// set test data
setTestDeployments(dummyDbMan, uri.String())
// http get
res, err := http.Get(uri.String())
Expect(err).Should(Succeed())
defer res.Body.Close()
Expect(res.StatusCode).Should(Equal(http.StatusOK))
etag := res.Header.Get("etag")
Expect(etag).ShouldNot(BeEmpty())
// send second request
req, err := http.NewRequest("GET", uri.String(), nil)
req.Header.Add("Content-Type", "application/json")
req.Header.Add("If-None-Match", etag)
// get response
res, err = http.DefaultClient.Do(req)
Expect(err).ShouldNot(HaveOccurred())
defer res.Body.Close()
Expect(res.StatusCode).To(Equal(http.StatusNotModified))
})
// block is not enabled now
XIt("should get empty set after blocking if no deployments", func() {
start := time.Now()
// setup http client
uri, err := url.Parse(apiTestUrl)
Expect(err).Should(Succeed())
uri.Path = deploymentsEndpoint + strconv.Itoa(testCount)
query := uri.Query()
query.Add("block", "1")
uri.RawQuery = query.Encode()
// http get
res, err := http.Get(uri.String())
Expect(err).Should(Succeed())
defer res.Body.Close()
Expect(res.StatusCode).Should(Equal(http.StatusOK))
//verify blocking time
blockingTime := time.Since(start)
log.Warnf("time used: %v", blockingTime.Seconds())
Expect(blockingTime.Seconds() > 0.9).Should(BeTrue())
// parse response
var depRes ApiDeploymentResponse
body, err := ioutil.ReadAll(res.Body)
Expect(err).Should(Succeed())
err = json.Unmarshal(body, &depRes)
Expect(err).Should(Succeed())
// verify response
Expect(len(depRes.ApiDeploymentsResponse)).To(Equal(0))
Expect(depRes.Kind).Should(Equal(kindCollection))
Expect(depRes.Self).Should(Equal(apiTestUrl + deploymentsEndpoint + strconv.Itoa(testCount)))
}, 2)
It("should get iso8601 time", func() {
testTimes := []string{"", "2017-04-05 04:47:36.462 +0000 UTC", "2017-04-05 04:47:36.462-07:00", "2017-04-05T04:47:36.462Z", "2017-04-05 23:23:38.162+00:00", "2017-06-22 16:41:02.334"}
isoTime := []string{"", "2017-04-05T04:47:36.462Z", "2017-04-05T04:47:36.462-07:00", "2017-04-05T04:47:36.462Z", "2017-04-05T23:23:38.162Z", "2017-06-22T16:41:02.334Z"}
// setup http client
uri, err := url.Parse(apiTestUrl)
Expect(err).Should(Succeed())
uri.Path = deploymentsEndpoint + strconv.Itoa(testCount)
for i, t := range testTimes {
log.Debug("insert deployment with timestamp: " + t)
// set test data
dep := makeTestDeployment()
dep.Created = t
dep.Updated = t
dummyDbMan.readyDeployments = []DataDeployment{*dep}
detail := makeExpectedDetail(dep, uri.String())
detail.Created = isoTime[i]
detail.Updated = isoTime[i]
// http get
res, err := http.Get(uri.String())
Expect(err).Should(Succeed())
defer res.Body.Close()
Expect(res.StatusCode).Should(Equal(http.StatusOK))
// parse response
var depRes ApiDeploymentResponse
body, err := ioutil.ReadAll(res.Body)
Expect(err).Should(Succeed())
err = json.Unmarshal(body, &depRes)
Expect(err).Should(Succeed())
// verify response
Expect(depRes.ApiDeploymentsResponse).Should(Equal([]ApiDeploymentDetails{*detail}))
}
})
It("should debounce requests", func(done Done) {
var in = make(chan interface{})
var out = make(chan []interface{})
go testApiMan.debounce(in, out, 3*time.Millisecond)
go func() {
defer GinkgoRecover()
received, ok := <-out
Expect(ok).To(BeTrue())
Expect(len(received)).To(Equal(2))
close(in)
received, ok = <-out
Expect(ok).To(BeFalse())
close(done)
}()
in <- "x"
in <- "y"
})
})
Context("GET /blobs", func() {
It("should get file bytesfrom endpoint", func() {
// setup http client
uri, err := url.Parse(apiTestUrl)
Expect(err).Should(Succeed())
uri.Path = blobEndpointPath + strconv.Itoa(testCount) + "/test"
// set test data
testFile, err := ioutil.TempFile(bundlePath, "test")
randString := GenerateUUID()
testFile.Write([]byte(randString))
err = testFile.Close()
Expect(err).Should(Succeed())
dummyDbMan.localFSLocation = testFile.Name()
log.Debug("randString: " + randString)
// http get
res, err := http.Get(uri.String())
Expect(err).Should(Succeed())
defer res.Body.Close()
Expect(res.StatusCode).Should(Equal(http.StatusOK))
// parse response
body, err := ioutil.ReadAll(res.Body)
Expect(err).Should(Succeed())
Expect(string(body)).Should(Equal(randString))
})
})
})
func setTestDeployments(dummyDbMan *dummyDbManager, self string) []ApiDeploymentDetails {
mathrand.Seed(time.Now().UnixNano())
count := mathrand.Intn(5) + 1
deployments := make([]DataDeployment, count)
details := make([]ApiDeploymentDetails, count)
for i := 0; i < count; i++ {
dep := makeTestDeployment()
detail := makeExpectedDetail(dep, self)
deployments[i] = *dep
details[i] = *detail
}
dummyDbMan.readyDeployments = deployments
return details
}
func makeTestDeployment() *DataDeployment {
dep := &DataDeployment{
ID: GenerateUUID(),
OrgID: GenerateUUID(),
EnvID: GenerateUUID(),
BlobID: testBlobId,
BlobResourceID: "",
Type: "virtual-host",
Name: "vh-secure",
Revision: "1",
Path: "/organizations/Org1/",
Created: time.Now().Format(time.RFC3339),
CreatedBy: "haoming@google.com",
Updated: time.Now().Format(time.RFC3339),
UpdatedBy: "haoming@google.com",
}
return dep
}
func makeExpectedDetail(dep *DataDeployment, self string) *ApiDeploymentDetails {
detail := &ApiDeploymentDetails{
Self: self + "/" + dep.ID,
Name: dep.Name,
Type: dep.Type,
Revision: dep.Revision,
BeanBlobUrl: getBlobUrl(dep.BlobID),
Org: dep.OrgID,
Env: dep.EnvID,
ResourceBlobUrl: "",
Path: dep.Path,
Created: dep.Created,
Updated: dep.Updated,
}
return detail
}
type dummyDbManager struct {
unreadyBlobIds []string
readyDeployments []DataDeployment
localFSLocation string
fileResponse chan string
version string
}
func (d *dummyDbManager) setDbVersion(version string) {
d.version = version
}
func (d *dummyDbManager) initDb() error {
return nil
}
func (d *dummyDbManager) getUnreadyBlobs() ([]string, error) {
return d.unreadyBlobIds, nil
}
func (d *dummyDbManager) getReadyDeployments() ([]DataDeployment, error) {
return d.readyDeployments, nil
}
func (d *dummyDbManager) updateLocalFsLocation(blobId, localFsLocation string) error {
file, err := os.Open(localFsLocation)
if err != nil {
return err
}
buff := make([]byte, 36)
_, err = file.Read(buff)
if err != nil {
return err
}
d.fileResponse <- string(buff)
return nil
}
func (d *dummyDbManager) getLocalFSLocation(string) (string, error) {
return d.localFSLocation, nil
}
func GenerateUUID() string {
buff := make([]byte, 16)
numRead, err := rand.Read(buff)
if numRead != len(buff) || err != nil {
panic(err)
}
/* uuid v4 spec */
buff[6] = (buff[6] | 0x40) & 0x4F
buff[8] = (buff[8] | 0x80) & 0xBF
return fmt.Sprintf("%x-%x-%x-%x-%x", buff[0:4], buff[4:6], buff[6:8], buff[8:10], buff[10:])
}