diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..458ed92 --- /dev/null +++ b/Makefile @@ -0,0 +1,58 @@ +VERSION = $(shell cat version) +REVISION := $(shell git rev-parse --short HEAD) +INFO_COLOR=\033[1;34m +RESET=\033[0m +BOLD=\033[1m +TEST ?= $(shell go list ./... | grep -v -e vendor -e keys -e tmp) + +GOVERSION=$(shell go version) +GO ?= GO111MODULE=on go + +TESTCONFIG="misc/test.conf" + +DIST ?= unknown +PREFIX=/usr +BINDIR=$(PREFIX)/sbin +SOURCES=Makefile go.mod go.sum version cmd cache_stnsd main.go package/ +BUILD=tmp/bin +UNAME_S := $(shell uname -s) +.DEFAULT_GOAL := build + +.PHONY: build +## build: build the nke +build: + $(GO) build -o $(BUILD)/cache-stnsd -ldflags "-X github.com/STNS/cache-stnsd/cmd.version=$(VERSION)" + +.PHONY: release +## release: release nke (tagging and exec goreleaser) +release: + goreleaser --rm-dist + +.PHONY: bump +bump: + git semv minor --bump + git tag | tail -n1 | sed 's/v//g' > version + +.PHONY: releasedeps +releasedeps: git-semv goreleaser + +.PHONY: git-semv +git-semv: + brew tap linyows/git-semv + brew install git-semv + + +.PHONY: goreleaser +goreleaser: + brew install goreleaser/tap/goreleaser + brew install goreleaser + +.PHONY: test +test: + @echo "$(INFO_COLOR)==> $(RESET)$(BOLD)Testing$(RESET)" + $(GO) test -v $(TEST) -timeout=30s -parallel=4 + $(GO) test -race $(TEST) + +.PHONY: github_release +github_release: ## Create some distribution packages + ghr -u STNS --replace v$(VERSION) builds/ diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a404e16 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module github.com/STNS/libstns-go + +go 1.14 + +require ( + github.com/hashicorp/go-retryablehttp v0.6.7 + github.com/sirupsen/logrus v1.7.0 + github.com/thoas/go-funk v0.7.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..21a330a --- /dev/null +++ b/go.sum @@ -0,0 +1,19 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-retryablehttp v0.6.7 h1:8/CAEZt/+F7kR7GevNHulKkUjLht3CPmn7egmhieNKo= +github.com/hashicorp/go-retryablehttp v0.6.7/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/thoas/go-funk v0.7.0 h1:GmirKrs6j6zJbhJIficOsz2aAI7700KsU/5YrdHRM1Y= +github.com/thoas/go-funk v0.7.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/libstns/http.go b/libstns/http.go new file mode 100644 index 0000000..d51ed49 --- /dev/null +++ b/libstns/http.go @@ -0,0 +1,191 @@ +package libstns + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/url" + "path" + "strings" + "time" + + "github.com/hashicorp/go-retryablehttp" + "github.com/sirupsen/logrus" + "github.com/thoas/go-funk" +) + +var version string + +type TLS struct { + CA string + Cert string + Key string +} + +type HttpOptions struct { + ApiEndpoint string + AuthToken string + User string + Password string + UserAgent string + SSLVerify bool + HttpProxy string + RequestTimeout int + RequestRetry int + HttpHeaders map[string]string + TLS TLS +} +type Http struct { + opt *HttpOptions +} + +type Response struct { + StatusCode int + Headers map[string]string + Body []byte +} + +func NewHttp(opt *HttpOptions) *Http { + if opt.UserAgent == "" { + opt.UserAgent = "libstns-go" + } + return &Http{ + opt: opt, + } +} +func (h *Http) Request(path string) (*Response, error) { + supportHeaders := []string{ + "user-highest-id", + "user-lowest-id", + "group-highest-id", + "group-lowest-id", + } + + req, err := http.NewRequest("GET", path, nil) + if err != nil { + logrus.Errorf("make http request error:%s", err.Error()) + return nil, err + } + + h.setHeaders(req) + h.setBasicAuth(req) + + tc, err := h.tlsConfig() + if err != nil { + logrus.Errorf("make tls config error:%s", err.Error()) + return nil, err + } + + tr := &http.Transport{ + TLSClientConfig: tc, + Dial: (&net.Dialer{ + Timeout: time.Duration(h.opt.RequestTimeout) * time.Second, + }).Dial, + } + + tr.Proxy = http.ProxyFromEnvironment + if h.opt.HttpProxy != "" { + proxyUrl, err := url.Parse(h.opt.HttpProxy) + if err == nil { + tr.Proxy = http.ProxyURL(proxyUrl) + } + } + retryClient := retryablehttp.NewClient() + retryClient.RetryMax = h.opt.RequestRetry + + client := retryClient.StandardClient() + client.Transport = tr + resp, err := client.Do(req) + if err != nil { + logrus.Errorf("http request error:%s", err.Error()) + return nil, err + } + defer resp.Body.Close() + + headers := map[string]string{} + for k, v := range resp.Header { + if funk.ContainsString(supportHeaders, strings.ToLower(k)) { + headers[k] = v[0] + } + } + + switch resp.StatusCode { + case http.StatusOK: + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + r := Response{ + StatusCode: resp.StatusCode, + Body: body, + Headers: headers, + } + + return &r, nil + default: + r := Response{ + StatusCode: resp.StatusCode, + Headers: headers, + } + return &r, nil + } +} + +func (h *Http) RequestURL(requestPath, query string) (*url.URL, error) { + u, err := url.Parse(h.opt.ApiEndpoint) + if err != nil { + return nil, err + } + + u.Path = path.Join(u.Path, requestPath) + u.RawQuery = query + return u, nil + +} + +func (h *Http) setHeaders(req *http.Request) { + for k, v := range h.opt.HttpHeaders { + req.Header.Add(k, v) + } + req.Header.Set("User-Agent", fmt.Sprintf("%s/%s", h.opt.UserAgent, version)) +} + +func (h *Http) setBasicAuth(req *http.Request) { + if h.opt.User != "" && h.opt.Password != "" { + req.SetBasicAuth(h.opt.User, h.opt.Password) + } +} + +func (h *Http) tlsConfig() (*tls.Config, error) { + tlsConfig := &tls.Config{InsecureSkipVerify: !h.opt.SSLVerify} + if h.opt.TLS.CA != "" { + CA_Pool := x509.NewCertPool() + + severCert, err := ioutil.ReadFile(h.opt.TLS.CA) + if err != nil { + return nil, err + } + CA_Pool.AppendCertsFromPEM(severCert) + + tlsConfig.RootCAs = CA_Pool + } + + if h.opt.TLS.Cert != "" && h.opt.TLS.Key != "" { + x509Cert, err := tls.LoadX509KeyPair(h.opt.TLS.Cert, h.opt.TLS.Key) + if err != nil { + return nil, err + } + tlsConfig.Certificates = make([]tls.Certificate, 1) + tlsConfig.Certificates[0] = x509Cert + } + + if len(tlsConfig.Certificates) == 0 && tlsConfig.RootCAs == nil { + tlsConfig = nil + } + + return tlsConfig, nil +}