From b3254568d65e53c564a846a666fa6af912c9b474 Mon Sep 17 00:00:00 2001 From: Kiran Kamineni Date: Wed, 3 Oct 2018 22:11:28 -0700 Subject: Add preload commandline app to load secrets Add a preload golang app that reads configuration files and loads the domains and corresponding secrets to SMS. The tool can configured via command line options. P2: Add support for domains as well as domain in JSON P3: Add sampleformat.json Issue-ID: AAF-532 Change-Id: If3f880b8ce61a282dc015cac7df723453b91f114 Signed-off-by: Kiran Kamineni --- sms-service/src/preload/Gopkg.lock | 28 ++++ sms-service/src/preload/Gopkg.toml | 34 ++++ sms-service/src/preload/Makefile | 30 ++++ sms-service/src/preload/preload.go | 256 ++++++++++++++++++++++++++++++ sms-service/src/preload/sampleformat.json | 22 +++ 5 files changed, 370 insertions(+) create mode 100644 sms-service/src/preload/Gopkg.lock create mode 100644 sms-service/src/preload/Gopkg.toml create mode 100644 sms-service/src/preload/Makefile create mode 100644 sms-service/src/preload/preload.go create mode 100644 sms-service/src/preload/sampleformat.json (limited to 'sms-service/src/preload') diff --git a/sms-service/src/preload/Gopkg.lock b/sms-service/src/preload/Gopkg.lock new file mode 100644 index 0000000..94ca755 --- /dev/null +++ b/sms-service/src/preload/Gopkg.lock @@ -0,0 +1,28 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" + name = "github.com/pkg/errors" + packages = ["."] + pruneopts = "UT" + revision = "645ef00459ed84a119197bfb8d8205042c6df63d" + version = "v0.8.0" + +[[projects]] + digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" + name = "gopkg.in/yaml.v2" + packages = ["."] + pruneopts = "UT" + revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" + version = "v2.2.1" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [ + "github.com/pkg/errors", + "gopkg.in/yaml.v2", + ] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/sms-service/src/preload/Gopkg.toml b/sms-service/src/preload/Gopkg.toml new file mode 100644 index 0000000..b1ef744 --- /dev/null +++ b/sms-service/src/preload/Gopkg.toml @@ -0,0 +1,34 @@ +# Gopkg.toml example +# +# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + +[[constraint]] + name = "github.com/pkg/errors" + version = "0.8.0" + +[prune] + go-tests = true + unused-packages = true diff --git a/sms-service/src/preload/Makefile b/sms-service/src/preload/Makefile new file mode 100644 index 0000000..c236049 --- /dev/null +++ b/sms-service/src/preload/Makefile @@ -0,0 +1,30 @@ +GOPATH := $(shell realpath "$(CURDIR)/../../") +BINARY := preload +PLATFORM := linux +DEPENDENCIES := github.com/golang/dep/cmd/dep + +export GOPATH ... + +all: test build +deploy: test build + +build: deps format + CGO_ENABLED=0 GOOS=$(PLATFORM) go build -a \ + -ldflags '-extldflags "-static"' \ + -o $(GOPATH)/target/$(BINARY) -v $(BINARY).go + +clean: + go clean + rm -f $(GOPATH)/target/$(BINARY) + +test: + @echo "Yet to Implement" + +format: + go fmt ./... + +deps: + go get -u $(DEPENDENCIES) + $(GOPATH)/bin/dep ensure + +.PHONY: test diff --git a/sms-service/src/preload/preload.go b/sms-service/src/preload/preload.go new file mode 100644 index 0000000..cbf345f --- /dev/null +++ b/sms-service/src/preload/preload.go @@ -0,0 +1,256 @@ +/* + * Copyright 2018 Intel Corporation, 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 main + +import ( + "bytes" + "crypto/tls" + "crypto/x509" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" + "path/filepath" + "strconv" + "strings" + "time" + + pkgerrors "github.com/pkg/errors" +) + +//DataJSON stores a list of domains from JSON file +type DataJSON struct { + //Support single domain: {} structure in JSON + Domain SecretDomainJSON `json:"domain,omitempty"` + //Support plural domains: [{}] structure in JSON + Domains []SecretDomainJSON `json:"domains,omitempty"` +} + +//SecretDomainJSON stores a name for the Domain and a list of Secrets +type SecretDomainJSON struct { + Name string `json:"name"` + Secrets []SecretJSON `json:"secrets"` +} + +//SecretJSON stores a name for the Secret and a list of Values +type SecretJSON struct { + Name string `json:"name"` + Values map[string]interface{} `json:"values"` +} + +//Processes the JSON file and returns a DataJSON struct +func processJSONFile(name string) (DataJSON, error) { + + data, err := ioutil.ReadFile(name) + if err != nil { + return DataJSON{}, pkgerrors.Cause(err) + } + + d := DataJSON{} + err = json.Unmarshal(data, &d) + if err != nil { + return DataJSON{}, pkgerrors.Cause(err) + } + + return d, nil +} + +type smsClient struct { + BaseURL *url.URL + //In seconds + Timeout int + CaCertPath string + + httpClient *http.Client +} + +func (c *smsClient) init() error { + + skipVerify := false + caCert, err := ioutil.ReadFile(c.CaCertPath) + if err != nil { + fmt.Println(pkgerrors.Cause(err)) + fmt.Println("Using Insecure Server Verification") + skipVerify = true + } + + tlsConfig := &tls.Config{ + MinVersion: tls.VersionTLS12, + } + + tlsConfig.InsecureSkipVerify = skipVerify + + // Add cert information when skipVerify is false + if skipVerify == false { + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + tlsConfig.RootCAs = caCertPool + } + + tr := &http.Transport{ + TLSClientConfig: tlsConfig, + } + + c.httpClient = &http.Client{ + Transport: tr, + Timeout: time.Duration(c.Timeout) * time.Second, + } + + return nil +} + +func (c *smsClient) sendPostRequest(relURL string, message map[string]interface{}) error { + + rel, err := url.Parse(relURL) + if err != nil { + return pkgerrors.Cause(err) + } + u := c.BaseURL.ResolveReference(rel) + + body, err := json.Marshal(message) + if err != nil { + return pkgerrors.Cause(err) + } + + resp, err := c.httpClient.Post(u.String(), "application/json", bytes.NewBuffer(body)) + if err != nil { + return pkgerrors.Cause(err) + } + + if resp.StatusCode >= 400 && resp.StatusCode < 600 { + // Request Failed + errText, _ := ioutil.ReadAll(resp.Body) + return pkgerrors.Errorf("Request Failed with: %s and Error: %s", + resp.Status, string(errText)) + } + + return nil +} + +func (c *smsClient) createDomain(domain string) error { + + message := map[string]interface{}{ + "name": domain, + } + url := "/v1/sms/domain" + err := c.sendPostRequest(url, message) + if err != nil { + return pkgerrors.Cause(err) + } + return nil +} + +func (c *smsClient) createSecret(domain string, secret string, + + values map[string]interface{}) error { + message := map[string]interface{}{ + "name": secret, + "values": values, + } + + url := "/v1/sms/domain/" + strings.TrimSpace(domain) + "/secret" + err := c.sendPostRequest(url, message) + if err != nil { + return pkgerrors.Cause(err) + } + + return nil +} + +//uploadToSMS reads through the domain or domains and uploads +//their corresponding secrets to SMS service +func (c *smsClient) uploadToSMS(data DataJSON) error { + + var ldata []SecretDomainJSON + + //Check if Domain is empty + if strings.TrimSpace(data.Domain.Name) != "" { + ldata = append(ldata, data.Domain) + } else if len(data.Domains) != 0 { + //Check if plural Domains are empty + ldata = append(ldata, data.Domains...) + } else { + return pkgerrors.New("Invalid JSON Data. No domain or domains found") + } + + for _, d := range ldata { + err := c.createDomain(d.Name) + if err != nil { + return pkgerrors.Cause(err) + } + + for _, s := range d.Secrets { + err = c.createSecret(d.Name, s.Name, s.Values) + if err != nil { + return pkgerrors.Cause(err) + } + } + } + + return nil +} + +func main() { + + cacert := flag.String("cacert", "/sms/certs/aaf_root_ca.cer", + "Path to the CA Certificate file") + serviceurl := flag.String("serviceurl", "https://aaf-sms.onap", + "Url for the SMS Service") + serviceport := flag.Int("serviceport", 10443, + "Service port if its different than the default") + jsondir := flag.String("jsondir", ".", + "Folder containing json files to upload") + + flag.Parse() + + files, err := ioutil.ReadDir(*jsondir) + if err != nil { + log.Fatal(pkgerrors.Cause(err)) + } + + serviceURL, err := url.Parse(*serviceurl + ":" + strconv.Itoa(*serviceport)) + if err != nil { + log.Fatal(pkgerrors.Cause(err)) + } + + client := &smsClient{ + Timeout: 30, + BaseURL: serviceURL, + CaCertPath: *cacert, + } + client.init() + + for _, file := range files { + if filepath.Ext(file.Name()) == ".json" { + fmt.Println("Processing ", file.Name()) + d, err := processJSONFile(file.Name()) + if err != nil { + log.Printf("Error Reading %s : %s", file.Name(), pkgerrors.Cause(err)) + continue + } + + err = client.uploadToSMS(d) + if err != nil { + log.Printf("Error Uploading %s : %s", file.Name(), pkgerrors.Cause(err)) + continue + } + } + } +} diff --git a/sms-service/src/preload/sampleformat.json b/sms-service/src/preload/sampleformat.json new file mode 100644 index 0000000..1a8a9f5 --- /dev/null +++ b/sms-service/src/preload/sampleformat.json @@ -0,0 +1,22 @@ +{ + "domains": [{ + "name": "mysecretdomain ", + "secrets": [ + { + "name": "database-credentials", + "values": { + "username": "admin", + "password": "admin", + "ttl": 3600 + } + }, + { + "name": "server-credentials", + "values": { + "username": "suser", + "password": "onap" + } + } + ] + }] +} -- cgit 1.2.3-korg