Adding TLS authentication 15/1715/1
authorenyinna1234 <enyinna.ochulor@intel.com>
Fri, 13 Sep 2019 00:13:40 +0000 (17:13 -0700)
committerKuralamudhan Ramakrishnan <kuralamudhan.ramakrishnan@intel.com>
Wed, 2 Oct 2019 20:43:10 +0000 (13:43 -0700)
This creates auth.go and adds tls authentication to main.go.
Also, containerization is introduced using Dockerfile.
It also fixes patch 1431 revert issues

Signed-off-by: Enyinna Ochulor <enyinna.ochulor@intel.com>
Change-Id: I7f6d8d210618e4630e9240c3596ced2f672df67e

cmd/bpa-restapi-agent/Makefile
cmd/bpa-restapi-agent/README.md
cmd/bpa-restapi-agent/api/imagehandler.go
cmd/bpa-restapi-agent/build/Dockerfile [new file with mode: 0644]
cmd/bpa-restapi-agent/go.mod
cmd/bpa-restapi-agent/go.sum
cmd/bpa-restapi-agent/internal/app/image.go
cmd/bpa-restapi-agent/internal/auth/auth.go [new file with mode: 0644]
cmd/bpa-restapi-agent/main.go

index ba40a6f..2e70365 100644 (file)
@@ -1,24 +1,32 @@
 
-# The name of the executable (default is current directory name)
-TARGET := $(shell echo $${PWD\#\#*/})
-.DEFAULT_GOAL: $(TARGET)
-
-# These will be provided to the target
-VERSION := 1.0.0
-BUILD := `git rev-parse HEAD`
-
-# Use linker flags to provide version/build settings to the target
-LDFLAGS=-ldflags "-X=main.Version=$(VERSION) -X=main.Build=$(BUILD)"
-
-# go source files, ignore vendor directory
-SRC = $(shell find . -type f -name '*.go' -not -path "./vendor/*")
-
-.PHONY: all build
-
-all: build
-
-$(TARGET): $(SRC)
-       @go build $(LDFLAGS) -o $(TARGET)
-
-build: $(TARGET)
-       @true
+# # The name of the executable (default is current directory name)
+# TARGET := $(shell echo $${PWD\#\#*/})
+# .DEFAULT_GOAL: $(TARGET)
+#
+# # These will be provided to the target
+# VERSION := 1.0.0
+# BUILD := `git rev-parse HEAD`
+#
+# # Use linker flags to provide version/build settings to the target
+# LDFLAGS=-ldflags "-X=main.Version=$(VERSION) -X=main.Build=$(BUILD)"
+#
+# # go source files, ignore vendor directory
+# SRC = $(shell find . -type f -name '*.go' -not -path "./vendor/*")
+#
+# .PHONY: all build
+#
+# all: build
+#
+# $(TARGET): $(SRC)
+#      @go build $(LDFLAGS) -o $(TARGET)
+#
+# build: $(TARGET)
+#      @true
+
+.PHONY: build
+
+build:
+       go build -mod=vendor -o build/_output/bin/bpa-restapi-agent main.go
+
+docker:
+       docker build -t akraino.org/icn/bpa-restapi-agent:latest . -f build/Dockerfile
index 27a7a51..a3300c0 100644 (file)
@@ -1,9 +1,187 @@
 ### Running the server
 To run the server, follow these simple steps:
 
-```
+Integrated Cloud Native (ICN) RESTful API
+
+This is a Golang application providing a RESTful API to interact with and upload image objects.
+
+The API application source files are in the icn/cmd/bpa-restapi-agent directory.
+
+While the database back-end is extensible, this initial release requires mongodb.
+
+Install
+
+Install and start mongodb. For instructions: https://docs.mongodb.com/manual/installation/
+
+git clone "https://gerrit.akraino.org/r/icn"
+cd icn/cmd/bpa-restapi-agent
+
+Run the application
 go run main.go
-```
+
+Output without a  config file:
+
+2019/08/22 14:08:41 Error loading config file. Using defaults
+2019/08/22 14:08:41 Starting Integrated Cloud Native API
+
+RESTful API usage examples
+
+Sample Post Request
+
+curl -i -F "metadata=<jsonfile;type=application/json" -F file=@/home/<username>/<dir>/jsonfile -X POST http://NODE_IP:9015//baremetalcluster/{owner}/{clustername}/<image_type>
+
+#image type can be binary_image, container_image, or os_image
+
+Example requests and responses:
+
+Create image - POST
+
+#Using a json file called sample.json
+#image_length in sample.json can be determined with the command
+ls -al <image_file>
+
+Request
+
+curl -i -F "metadata=<sample.json;type=application/json" -F file=@/home/enyi/workspace/icn/cmd/bpa-restapi-agent/sample.json -X POST http://localhost:9015/v1/baremetalcluster/alpha/beta/container_images
+
+Response
+
+HTTP/1.1 100 Continue
+
+HTTP/1.1 201 Created
+Content-Type: application/json
+Date: Thu, 22 Aug 2019 22:56:16 GMT
+Content-Length: 239
+
+{"owner":"alpha","cluster_name":"beta","type":"container","image_name":"asdf246","image_offset":0,"image_length":29718177,"upload_complete":false,"description":{"image_records":[{"image_record_name":"iuysdi1234","repo":"icn","tag":"1"}]}}
+
+#this creates a database entry for the image, and an empty file in the file system
+
+List image - GET
+
+curl -i -X GET http://localhost:9015/v1/baremetalcluster/{owner}/{clustername}/<image_type>/{imgname}
+
+
+example:
+#continuing with our container image from above
+
+Request
+
+curl -i -X GET http://localhost:9015/v1/baremetalcluster/alpha/beta/container_images/asdf246
+
+Response
+
+HTTP/1.1 200 OK
+Content-Type: application/json
+Date: Thu, 22 Aug 2019 22:57:10 GMT
+Content-Length: 239
+
+{"owner":"alpha","cluster_name":"beta","type":"container","image_name":"asdf246","image_offset":0,"image_length":29718177,"upload_complete":false,"description":{"image_records":[{"image_record_name":"iuysdi1234","repo":"icn","tag":"1"}]}}
+
+Upload container image - PATCH
+Request
+
+curl --request PATCH --data-binary "@/home/enyi/workspace/icn/cmd/bpa-restapi-agent/sample_image" http://localhost:9015/v1/baremetalcluster/alpha/beta/container_images/asdf246 --header "Upload-Offset: 0" --header "Expect:" -i
+
+
+Response
+
+HTTP/1.1 204 No Content
+Upload-Offset: 29718177
+Date: Thu, 22 Aug 2019 23:19:44 GMT
+
+Check uploaded image - GET
+
+Request
+
+curl -i -X GET http://localhost:9015/v1/baremetalcluster/alpha/beta/container_images/asdf246
+
+Response
+
+HTTP/1.1 200 OK
+Content-Type: application/json
+Date: Fri, 23 Aug 2019 17:12:07 GMT
+Content-Length: 245
+
+{"owner":"alpha","cluster_name":"beta","type":"container","image_name":"asdf246","image_offset":29718177,"image_length":29718177,"upload_complete":true,"description":{"image_records":[{"image_record_name":"iuysdi1234","repo":"icn","tag":"1"}]}}
+
+#after the upload, the image_offset is now the same as image_length and upload_complete changed to true
+#if upload was incomplete
+
+Resumable upload instructions
+
+Resumable upload -PATCH
+
+#this is the current resumable upload mechanism
+
+Request
+
+curl --request PATCH --data-binary "@/home/enyi/workspace/icn/cmd/bpa-restapi-agent/sample_image" http://localhost:9015/v1/baremetalcluster/alpha/beta/container_images/asdf246 --header "Upload-Offset: 0" --header "Expect:" -i --limit-rate 200K
+
+#the above request limits transfer for testing purposes
+#'ctl c' out after a few seconds, to stop file transfer
+#check image_offset with a GET
+
+Check upload - GET
+
+Request
+
+curl -i -X GET http://localhost:9015/v1/baremetalcluster/alpha/beta/container_images/asdf246
+
+Response
+
+HTTP/1.1 200 OK
+Content-Type: application/json
+Date: Sat, 24 Aug 2019 00:30:00 GMT
+Content-Length: 245
+
+{"owner":"alpha","cluster_name":"beta","type":"container","image_name":"asdf246","image_offset":4079616,"image_length":29718177,"upload_complete":false,"description":{"image_records":[{"image_record_name":"iuysdi1234","repo":"icn","tag":"2"}]}}
+
+#from our response you can see that image_offset is still less than image_length and #upload_complete is still false
+#next we use the dd command (no limiting this time)
+
+Request
+
+dd if=/home/enyi/workspace/icn/cmd/bpa-restapi-agent/sample_image skip=4079616 bs=1 | curl --request PATCH --data-binary @- http://localhost:9015/v1/baremetalcluster/alpha/beta/container_images/asdf246 --header "Upload-Offset: 4079616" --header "Expect:" -i
+
+#the request skips already uploaded 4079616 bytes of data
+
+Response
+
+25638561+0 records in
+25638561+0 records out
+25638561 bytes (26 MB, 24 MiB) copied, 207.954 s, 123 kB/s
+HTTP/1.1 204 No Content
+Upload-Offset: 29718177
+Date: Sat, 24 Aug 2019 00:43:18 GMT
+
+Update image description - PUT
+
+# let's change the tag in description from 1 to latest
+# once the  change is made in sample.json (or your json file)
+
+Request
+
+curl -i -F "metadata=<sample.json;type=application/json" -F file=@/home/enyi/workspace/icn/cmd/bpa-restapi-agent/sample.json -X PUT http://localhost:9015/v1/baremetalcluster/alpha/beta/container_images/asdf246
+
+Response
+
+HTTP/1.1 100 Continue
+
+HTTP/1.1 201 Created
+Content-Type: application/json
+Date: Fri, 23 Aug 2019 17:21:01 GMT
+Content-Length: 239
+
+{"owner":"alpha","cluster_name":"beta","type":"container","image_name":"asdf246","image_offset":0,"image_length":29718177,"upload_complete":false,"description":{"image_records":[{"image_record_name":"iuysdi1234","repo":"icn","tag":"2"}]}}
+
+Delete an image - DELETE
+
+Request
+
+curl -i -X DELETE http://localhost:9015/v1/baremetalcluster/alpha/beta/container_images/asdf246
+
+Response
 
 # Cloud Storage with MinIO
 
@@ -31,4 +209,3 @@ You can check the status by opening a browser: http://127.0.0.1:9000/
 MinIO Client implementation integrated in REST API agent and will automatically
 initialize in main.go, and create 3 buckets: binary, container, operatingsystem.
 The Upload image will "PUT" to corresponding buckets by HTTP PATCH request url.
-
index d9b1844..2b68d6e 100644 (file)
@@ -2,16 +2,13 @@ package api
 
 import (
        "bytes"
-       "encoding/base64"
        "encoding/json"
        "fmt"
        "io"
        "io/ioutil"
        "net/http"
        "os"
-       "os/user"
        "log"
-       "path"
        "strconv"
 
        image "bpa-restapi-agent/internal/app"
@@ -75,12 +72,6 @@ func (h imageHandler) createHandler(w http.ResponseWriter, r *http.Request) {
                return
        }
 
-       //Create file directory
-       dir, err := createFileDir(v.Type)
-       if err != nil {
-               log.Fatal("Error creating file server directory", err)
-       }
-
        //Read the file section and ignore the header
        file, _, err := r.FormFile("file")
        if err != nil {
@@ -90,31 +81,12 @@ func (h imageHandler) createHandler(w http.ResponseWriter, r *http.Request) {
 
        defer file.Close()
 
-       //Convert the file content to base64 for storage
-       content, err := ioutil.ReadAll(file)
-       if err != nil {
-               http.Error(w, "Unable to read file", http.StatusUnprocessableEntity)
-               return
-       }
-
-       v.Config = base64.StdEncoding.EncodeToString(content)
 
        ret, err := h.client.Create(v)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
        }
-       h.dirPath = dir
-       filePath := path.Join(h.dirPath, v.ImageName)
-       file1, err := os.Create(filePath)
-       if err != nil {
-               e := "Error creating file in filesystem"
-               log.Printf("%s %s\n", e, err)
-               w.WriteHeader(http.StatusInternalServerError)
-               return
-       }
-
-       defer file1.Close()
 
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(http.StatusCreated)
@@ -125,30 +97,10 @@ func (h imageHandler) createHandler(w http.ResponseWriter, r *http.Request) {
        }
 }
 
-// Create file
-
-func createFileDir(dirName string) (string, error) {
-    u, err := user.Current()
-    if err != nil {
-        log.Println("Error while fetching user home directory", err)
-        return "", err
-    }
-    home := u.HomeDir
-    dirPath := path.Join(home, "images", dirName)
-    err = os.MkdirAll(dirPath, 0744)
-    if err != nil {
-        log.Println("Error while creating file server directory", err)
-        return "", err
-    }
-    return dirPath, nil
-}
-
 // getHandler handles GET operations on a particular name
 // Returns an Image
 func (h imageHandler) getHandler(w http.ResponseWriter, r *http.Request) {
        vars := mux.Vars(r)
-       // ownerName := vars["owner"]
-       // clusterName := vars["clustername"]
        imageName := vars["imgname"]
 
        ret, err := h.client.Get(imageName)
@@ -169,8 +121,6 @@ func (h imageHandler) getHandler(w http.ResponseWriter, r *http.Request) {
 // deleteHandler handles DELETE operations on a particular record
 func (h imageHandler) deleteHandler(w http.ResponseWriter, r *http.Request) {
        vars := mux.Vars(r)
-       // ownerName := vars["owner"]
-       // clusterName := vars["clustername"]
        imageName := vars["imgname"]
 
        err := h.client.Delete(imageName)
@@ -228,15 +178,6 @@ func (h imageHandler) updateHandler(w http.ResponseWriter, r *http.Request) {
 
        defer file.Close()
 
-       //Convert the file content to base64 for storage
-       content, err := ioutil.ReadAll(file)
-       if err != nil {
-               http.Error(w, "Unable to read file", http.StatusUnprocessableEntity)
-               return
-       }
-
-       v.Config = base64.StdEncoding.EncodeToString(content)
-
        ret, err := h.client.Update(imageName, v)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -308,15 +249,12 @@ func (h imageHandler) patchHandler(w http.ResponseWriter, r *http.Request) {
                log.Println("Size of received file ", len(body))
        }
 
-       u, err := user.Current()
+       fp, _, err := h.client.GetDirPath(imageName)
        if err != nil {
-                       log.Println("Error while fetching user home directory", err)
-                       return
+               log.Printf("unable to get file path %s\n", err)
+               w.WriteHeader(http.StatusInternalServerError)
+               return
        }
-       home := u.HomeDir
-       dir := path.Join(home, "images", file.Type)
-       h.dirPath = dir
-       fp := fmt.Sprintf("%s/%s", h.dirPath, imageName)
        f, err := os.OpenFile(fp, os.O_APPEND|os.O_WRONLY, 0644)
        if err != nil {
                log.Printf("unable to open file %s\n", err)
@@ -352,12 +290,12 @@ func (h imageHandler) patchHandler(w http.ResponseWriter, r *http.Request) {
                *file.UploadComplete = true
        }
 
-       // err = h.updateFile(file)
-       // if err != nil {
-       //      log.Println("Error while updating file", err)
-       //      w.WriteHeader(http.StatusInternalServerError)
-       //      return
-       // }
+       _, err = h.client.Update(imageName, file)
+       if err != nil {
+               log.Println("Error while updating file", err)
+               w.WriteHeader(http.StatusInternalServerError)
+               return
+       }
        w.WriteHeader(http.StatusNoContent)
 
        return
diff --git a/cmd/bpa-restapi-agent/build/Dockerfile b/cmd/bpa-restapi-agent/build/Dockerfile
new file mode 100644 (file)
index 0000000..33a6c5f
--- /dev/null
@@ -0,0 +1,12 @@
+FROM registry.svc.ci.openshift.org/openshift/release:golang-1.12 AS builder
+RUN mkdir /bpa-restapi-agent
+ADD . /bpa-restapi-agent
+WORKDIR /bpa-restapi-agent
+
+RUN make build
+
+FROM registry.svc.ci.openshift.org/openshift/release:golang-1.12
+
+COPY --from=builder /bpa-restapi-agent/build/_output/bin/bpa-restapi-agent /bpa-restapi-agent
+
+ENTRYPOINT ["/bpa-restapi-agent"]
index 5f99a12..4ed8ccb 100644 (file)
@@ -7,6 +7,7 @@ require (
        github.com/golang/snappy v0.0.1 // indirect
        github.com/gorilla/handlers v1.4.2
        github.com/gorilla/mux v1.7.3
+       github.com/minio/minio-go/v6 v6.0.35
        github.com/pkg/errors v0.8.1
        github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect
        github.com/xdg/stringprep v1.0.0 // indirect
index 50f7957..0bd1467 100644 (file)
@@ -1,25 +1,60 @@
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/a8m/mark v0.1.1-0.20170507133748-44f2db618845/go.mod h1:c8Mh99Cw82nrsAnPgxQSZHkswVOJF7/MqZb1ZdvriLM=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/gernest/wow v0.1.0/go.mod h1:dEPabJRi5BneI1Nev1VWo0ZlcTWibHWp43qxKms4elY=
 github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
 github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
 github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
 github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/minio/cli v1.20.0/go.mod h1:bYxnK0uS629N3Bq+AOZZ+6lwF77Sodk4+UL9vNuXhOY=
+github.com/minio/minio-go/v6 v6.0.35 h1:D0WTTfDvXfYaz3/LKr13M9D+SZdf+in6eYVF8FPuQWw=
+github.com/minio/minio-go/v6 v6.0.35/go.mod h1:vaNT59cWULS37E+E9zkuN/BVnKHyXtVGS+b04Boc66Y=
+github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
 github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0=
 github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
 go.mongodb.org/mongo-driver v1.0.4 h1:bHxbjH6iwh1uInchXadI6hQR107KEbgYsMzoblDONmQ=
 go.mongodb.org/mongo-driver v1.0.4/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
+golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo=
+golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190116161447-11f53e031339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
+gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
index a1beed8..28cda77 100644 (file)
@@ -1,10 +1,10 @@
 package app
 
 import (
-  //"encoding/base64"
        "encoding/json"
-       //"io/ioutil"
-
+       "os"
+       "os/user"
+       "path"
        "bpa-restapi-agent/internal/db"
 
        pkgerrors "github.com/pkg/errors"
@@ -16,7 +16,6 @@ type Image struct {
        ClusterName         string               `json:"cluster_name"`
        Type                string               `json:"type"`
        ImageName           string               `json:"image_name"`
-       Config                                                  string                                                   `json:"config"`
        ImageOffset                                     *int                                                       `json:"image_offset"`
        ImageLength                                     int                                                                      `json:"image_length"`
        UploadComplete                  *bool                                                            `json:"upload_complete"`
@@ -52,6 +51,7 @@ type ImageManager interface {
        Delete(imageName string) error
        Update(imageName string, c Image) (Image, error)
        GetImageRecordByName(imgname, imageName string) (map[string]string, error)
+       GetDirPath(imageName string) (string, string, error)
 }
 
 // ImageClient implements the ImageManager
@@ -66,21 +66,21 @@ type ImageClient struct {
 // which implements the ImageManager
 func NewBinaryImageClient() *ImageClient {
        return &ImageClient{
-               storeName: "binary_image",
+               storeName: "binary_images",
                tagMeta:   "metadata",
        }
 }
 
 func NewContainerImageClient() *ImageClient {
        return &ImageClient{
-               storeName: "container_image",
+               storeName: "container_images",
                tagMeta:   "metadata",
        }
 }
 
 func NewOSImageClient() *ImageClient {
        return &ImageClient{
-               storeName: "os_image",
+               storeName: "os_images",
                tagMeta:   "metadata",
        }
 }
@@ -106,9 +106,37 @@ func (v *ImageClient) Create(c Image) (Image, error) {
                return Image{}, pkgerrors.Wrap(err, "Creating DB Entry")
        }
 
+       err = v.CreateFile(v.storeName, c)
+       if err != nil {
+               return Image{}, pkgerrors.Wrap(err, "Creating File in FS")
+       }
+
        return c, nil
 }
 
+// Create file
+
+func (v *ImageClient) CreateFile(dirName string, c Image) error {
+       filePath, dirPath, err := v.GetDirPath(c.ImageName)
+       if err != nil {
+               return pkgerrors.Wrap(err, "Get file path")
+       }
+  err = os.MkdirAll(dirPath, 0744)
+  if err != nil {
+               return pkgerrors.Wrap(err, "Make image directory")
+  }
+       file1, err := os.Create(filePath)
+       if err != nil {
+               return pkgerrors.Wrap(err, "Create image file")
+       }
+
+       defer file1.Close()
+
+
+  return nil
+}
+
+
 // Get returns Image for corresponding to name
 func (v *ImageClient) Get(imageName string) (Image, error) {
 
@@ -154,6 +182,18 @@ func (v *ImageClient) GetImageRecordByName(imgName string,
        return nil, pkgerrors.New("Image record " + imageRecordName + " not found")
 }
 
+func (v *ImageClient) GetDirPath(imageName string) (string, string, error) {
+       u, err := user.Current()
+       if err != nil {
+               return "", "", pkgerrors.Wrap(err, "Current user")
+       }
+       home := u.HomeDir
+       dirPath := path.Join(home, "images", v.storeName)
+       filePath := path.Join(dirPath, imageName)
+
+       return filePath, dirPath, err
+}
+
 // Delete the Image from database
 func (v *ImageClient) Delete(imageName string) error {
 
@@ -167,6 +207,16 @@ func (v *ImageClient) Delete(imageName string) error {
        if err != nil {
                return pkgerrors.Wrap(err, "Delete Image")
        }
+
+       //Delete image from FS
+       filePath, _, err := v.GetDirPath(imageName)
+       if err != nil {
+               return pkgerrors.Wrap(err, "Get file path")
+       }
+       err = os.Remove(filePath)
+       if err != nil {
+               return pkgerrors.Wrap(err, "Delete image file")
+       }
        return nil
 }
 
@@ -174,8 +224,6 @@ func (v *ImageClient) Delete(imageName string) error {
 func (v *ImageClient) Update(imageName string, c Image) (Image, error) {
 
        key := ImageKey{
-               // Owner:       c.Owner,
-               // ClusterName: c.ClusterName,
                ImageName: imageName,
        }
 
diff --git a/cmd/bpa-restapi-agent/internal/auth/auth.go b/cmd/bpa-restapi-agent/internal/auth/auth.go
new file mode 100644 (file)
index 0000000..3da8f2a
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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 auth
+
+import (
+       "crypto/tls"
+       "crypto/x509"
+       "encoding/base64"
+       "encoding/pem"
+       "io/ioutil"
+       "log"
+
+       pkgerrors "github.com/pkg/errors"
+)
+
+// GetTLSConfig initializes a tlsConfig using the CA's certificate
+// This config is then used to enable the server for mutual TLS
+func GetTLSConfig(caCertFile string, certFile string, keyFile string) (*tls.Config, error) {
+
+       // Initialize tlsConfig once
+       caCert, err := ioutil.ReadFile(caCertFile)
+
+       if err != nil {
+               return nil, pkgerrors.Wrap(err, "Read CA Cert file")
+       }
+
+       caCertPool := x509.NewCertPool()
+       caCertPool.AppendCertsFromPEM(caCert)
+
+       tlsConfig := &tls.Config{
+               // Change to RequireAndVerify once we have mandatory certs
+               ClientAuth: tls.VerifyClientCertIfGiven,
+               ClientCAs:  caCertPool,
+               MinVersion: tls.VersionTLS12,
+       }
+
+       certPEMBlk, err := readPEMBlock(certFile)
+       if err != nil {
+               return nil, pkgerrors.Wrap(err, "Read Cert File")
+       }
+
+       keyPEMBlk, err := readPEMBlock(keyFile)
+       if err != nil {
+               return nil, pkgerrors.Wrap(err, "Read Key File")
+       }
+
+       tlsConfig.Certificates = make([]tls.Certificate, 1)
+       tlsConfig.Certificates[0], err = tls.X509KeyPair(certPEMBlk, keyPEMBlk)
+       if err != nil {
+               return nil, pkgerrors.Wrap(err, "Load x509 cert and key")
+       }
+
+       tlsConfig.BuildNameToCertificate()
+       return tlsConfig, nil
+}
+
+func readPEMBlock(filename string) ([]byte, error) {
+
+       pemData, err := ioutil.ReadFile(filename)
+       if err != nil {
+               return nil, pkgerrors.Wrap(err, "Read PEM File")
+       }
+
+       pemBlock, rest := pem.Decode(pemData)
+       if len(rest) > 0 {
+               log.Println("Pemfile has extra data")
+       }
+
+       if x509.IsEncryptedPEMBlock(pemBlock) {
+               password, err := ioutil.ReadFile(filename + ".pass")
+               if err != nil {
+                       return nil, pkgerrors.Wrap(err, "Read Password File")
+               }
+
+               pByte, err := base64.StdEncoding.DecodeString(string(password))
+               if err != nil {
+                       return nil, pkgerrors.Wrap(err, "Decode PEM Password")
+               }
+
+               pemData, err = x509.DecryptPEMBlock(pemBlock, pByte)
+               if err != nil {
+                       return nil, pkgerrors.Wrap(err, "Decrypt PEM Data")
+               }
+               var newPEMBlock pem.Block
+               newPEMBlock.Type = pemBlock.Type
+               newPEMBlock.Bytes = pemData
+               // Converting back to PEM from DER data you get from
+               // DecryptPEMBlock
+               pemData = pem.EncodeToMemory(&newPEMBlock)
+       }
+
+       return pemData, nil
+}
index 6a8960b..26f0ba4 100644 (file)
@@ -10,16 +10,16 @@ import (
   "os/signal"
   "time"
 
-  //To Do - Implement internal for checking config
+
   "github.com/gorilla/handlers"
 
   "bpa-restapi-agent/api"
   utils "bpa-restapi-agent/internal"
+  "bpa-restapi-agent/internal/auth"
   "bpa-restapi-agent/internal/config"
 )
 
 func main() {
-  // To Do - Implement initial settings
   // check initial config
   err := utils.CheckInitialSettings()
   if err != nil{
@@ -36,7 +36,6 @@ func main() {
   // Create custom http server
   httpServer := &http.Server{
     Handler: loggedRouter,
-    // To Do - Implement config
     Addr:    ":" + config.GetConfiguration().ServicePort,
   }
   connectionsClose := make(chan struct{})
@@ -49,6 +48,13 @@ func main() {
   }()
 
   // Start server
-  log.Fatal(httpServer.ListenAndServe())
-
+  tlsConfig, err := auth.GetTLSConfig("ca.cert", "server.cert", "server.key")
+  if err != nil {
+    log.Println("Error Getting TLS Configuration. Starting without TLS...")
+    log.Fatal(httpServer.ListenAndServe())
+  } else {
+    httpServer.TLSConfig = tlsConfig
+    // empty strings because tlsconfig already has this information
+    err = httpServer.ListenAndServeTLS("", "")
+  }
 }