HTTP API: Implement acquiring worker
authorMaciej Wereski <m.wereski@partner.samsung.com>
Fri, 29 Sep 2017 10:22:28 +0000 (12:22 +0200)
committerMaciej Wereski <m.wereski@partner.samsung.com>
Tue, 5 Jun 2018 10:48:12 +0000 (12:48 +0200)
AccessInfo structure provides key in golang rsa.PrivateKey format, so
handler repacks it and replies with AccessInfo2 structure, which has key
in PEM format. This is temporary solution - private keys will be removed
when proper user support is added (user public key will be used).

Change-Id: Ia4b8bc4a0ed007f0a6a0c0ff8e3ef48750646e51
Signed-off-by: Maciej Wereski <m.wereski@partner.samsung.com>
server/api/v1/api.go
server/api/v1/handlers.go
server/api/v1/handlers_test.go
server/api/v1/testdata/acquire-worker-POST.json [deleted file]
server/api/v1/testdata/acquire-worker-bad-id-POST.json [new file with mode: 0644]
server/api/v1/testdata/acquire-worker-missing-POST.json [new file with mode: 0644]
server/api/v1/testdata/acquire-worker-valid-POST.json [new file with mode: 0644]

index 9022419..5cff344 100644 (file)
@@ -22,6 +22,7 @@ package v1
 import (
        "encoding/json"
        "fmt"
+       "net"
        "net/http"
        "strconv"
 
@@ -38,6 +39,19 @@ type reqIDPack struct {
        ReqID
 }
 
+// AccessInfo2 structure is used by HTTP instead of AccessInfo when acquiring
+// worker. The only difference is that key field is in PEM format instead of
+// rsa.PrivateKey. It is temporary solution - session private keys will be
+// replaces with users' public keys when proper user support is added.
+type AccessInfo2 struct {
+       // Addr is necessary information to connect to a tunnel to Dryad.
+       Addr net.Addr
+       // Key is private RSA key in PEM format.
+       Key string
+       // Username is a login name for the job session.
+       Username string
+}
+
 // reqHandler denotes function that parses HTTP request and returns responseData.
 type reqHandler func(*http.Request, map[string]string) responseData
 
index 80aa825..8ab22b6 100644 (file)
@@ -19,7 +19,9 @@
 package v1
 
 import (
+       "crypto/x509"
        "encoding/json"
+       "encoding/pem"
        "io"
        "net/http"
 
@@ -111,7 +113,26 @@ func (api *API) listRequestsHandler(r *http.Request, ps map[string]string) respo
 // acquireWorkerHandler parses HTTP request for acquiring worker for Boruta
 // request and calls AcquireWorker().
 func (api *API) acquireWorkerHandler(r *http.Request, ps map[string]string) responseData {
-       return newServerError(ErrNotImplemented, "acquire worker")
+       defer r.Body.Close()
+
+       reqid, err := parseReqID(ps["id"])
+       if err != nil {
+               return newServerError(ErrBadID)
+       }
+
+       accessInfo, err := api.reqs.AcquireWorker(reqid)
+       if err != nil {
+               return newServerError(err)
+       }
+       key := string(pem.EncodeToMemory(&pem.Block{
+               Type:  "RSA PRIVATE KEY",
+               Bytes: x509.MarshalPKCS1PrivateKey(&accessInfo.Key),
+       }))
+       return AccessInfo2{
+               Addr:     accessInfo.Addr,
+               Key:      key,
+               Username: accessInfo.Username,
+       }
 }
 
 // prolongAccessHandler parses HTTP request for prolonging previously acquired
index 9d8a556..3fcf486 100644 (file)
 package v1
 
 import (
+       "crypto/x509"
        "encoding/json"
+       "encoding/pem"
        "errors"
        "fmt"
+       "net"
        "net/http"
        "testing"
        "time"
@@ -269,15 +272,40 @@ func TestAcquireWorkerHandler(t *testing.T) {
        assert, m, api := initTest(t)
        defer m.finish()
 
+       methods := []string{http.MethodPost}
+       prefix := "acquire-worker-"
+       pathfmt := "/api/v1/reqs/%s/acquire_worker"
+
+       keyPem := "-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQCyBgKbrwKh75BDoigwltbazFGDLdlxf9YLpFj5v+4ieKgsaN+W\n+kRvamSuB5CC2tqFql5x7kPt1U+vVMwkzVRewF/HHzRYxgLHlge6d1ZALpCWywaz\nslt5pNCmF7NoZ//WTSrafufDI4IRoNgkHtEKvnWdBaPPnY4Cf+PCbZOYNQIDAQAB\nAoGBAJvoz5fxKekQmdPhzDjhocF1d13fZbQVNSx0/seb476k1QQvxMHA5PZ+wzX2\nwgUYDpFJp/U3qp48VtFC/pasjNoG7zLPLLUJcg15eOoh4Ld7I1e4lRkLl3CwnqMk\nbc6UoKQRLli4O3cmaMxVHXal0o72s3o0qnHlRlZXLekwi6aBAkEA69j3bnbAybsF\n/NHelRYDH8bou+LCX2d/p6ReUR0bJ4yCAWRi/9Ld0ng482xinxGSpfovbIplBMFx\nH2eT2Cw0OQJBAME8LLz/3zb/vLG/t8Lfsequ1ZhVca/LlVR4yJLlyaVcywT9SJlO\nmKCy13SpKl8TY7czyufYrY4lobZjYaIsm90CQQCKhkRGWG/BzRymMyp2DJjHKFB4\nUqbx3FuJPqy7HcpeP1P4t1rCgbsSLNTefRGr9mlZHYqPSPYuheQImxCmTshZAkEA\nwAp5u+vfft1yPoT2r+l4/G99P8PLFJcTdbwEOlm8qWcrLW47dIE0FqEml3536b1v\nYGdMxFYHRjoIGSdzpKUI0QJAAqPdDp+y7kWaeIbKkp3Z3bLrj5wii2QAy2YlBDKe\npXrvruWJvL75OCYcxRju3DpVaoYqEmso+UEiQEDRB42YYg==\n-----END RSA PRIVATE KEY-----\n"
+       block, _ := pem.Decode([]byte(keyPem))
+       assert.NotNil(block)
+       key, _ := x509.ParsePKCS1PrivateKey(block.Bytes)
+
+       access := AccessInfo{
+               Addr: &net.TCPAddr{
+                       IP:   net.IPv4(127, 0, 0, 1),
+                       Port: 22,
+               },
+               Key:      *key,
+               Username: "wołchw",
+       }
+
+       m.rq.EXPECT().AcquireWorker(ReqID(1)).Return(access, nil)
+       notFoundTest := testFromTempl(notFoundTestTempl, prefix, fmt.Sprintf(pathfmt, "2"), methods...)
+       m.rq.EXPECT().AcquireWorker(ReqID(2)).Return(AccessInfo{}, NotFoundError("Request"))
+       invalidIDTest := testFromTempl(invalidIDTestTempl, prefix, fmt.Sprintf(pathfmt, invalidID), methods...)
+
        tests := []requestTest{
                {
-                       name:        "acquire-worker",
-                       path:        "/api/v1/reqs/8/acquire_worker",
-                       methods:     []string{http.MethodPost},
-                       json:        ``,
+                       name:        prefix + "valid",
+                       path:        fmt.Sprintf(pathfmt, "1"),
+                       methods:     methods,
+                       json:        string(jsonMustMarshal(access)),
                        contentType: contentTypeJSON,
-                       status:      http.StatusNotImplemented,
+                       status:      http.StatusOK,
                },
+               notFoundTest,
+               invalidIDTest,
        }
 
        runTests(assert, api, tests)
diff --git a/server/api/v1/testdata/acquire-worker-POST.json b/server/api/v1/testdata/acquire-worker-POST.json
deleted file mode 100644 (file)
index 6cada1f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-{"error":"not implemented yet: acquire worker"}
\ No newline at end of file
diff --git a/server/api/v1/testdata/acquire-worker-bad-id-POST.json b/server/api/v1/testdata/acquire-worker-bad-id-POST.json
new file mode 100644 (file)
index 0000000..9a1b539
--- /dev/null
@@ -0,0 +1 @@
+{"error":"invalid request: ID provided in URL isn't valid"}
\ No newline at end of file
diff --git a/server/api/v1/testdata/acquire-worker-missing-POST.json b/server/api/v1/testdata/acquire-worker-missing-POST.json
new file mode 100644 (file)
index 0000000..bb60840
--- /dev/null
@@ -0,0 +1 @@
+{"error":"Request not found"}
\ No newline at end of file
diff --git a/server/api/v1/testdata/acquire-worker-valid-POST.json b/server/api/v1/testdata/acquire-worker-valid-POST.json
new file mode 100644 (file)
index 0000000..c8ee7f4
--- /dev/null
@@ -0,0 +1 @@
+{"Addr":{"IP":"127.0.0.1","Port":22,"Zone":""},"Key":"-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQCyBgKbrwKh75BDoigwltbazFGDLdlxf9YLpFj5v+4ieKgsaN+W\n+kRvamSuB5CC2tqFql5x7kPt1U+vVMwkzVRewF/HHzRYxgLHlge6d1ZALpCWywaz\nslt5pNCmF7NoZ//WTSrafufDI4IRoNgkHtEKvnWdBaPPnY4Cf+PCbZOYNQIDAQAB\nAoGBAJvoz5fxKekQmdPhzDjhocF1d13fZbQVNSx0/seb476k1QQvxMHA5PZ+wzX2\nwgUYDpFJp/U3qp48VtFC/pasjNoG7zLPLLUJcg15eOoh4Ld7I1e4lRkLl3CwnqMk\nbc6UoKQRLli4O3cmaMxVHXal0o72s3o0qnHlRlZXLekwi6aBAkEA69j3bnbAybsF\n/NHelRYDH8bou+LCX2d/p6ReUR0bJ4yCAWRi/9Ld0ng482xinxGSpfovbIplBMFx\nH2eT2Cw0OQJBAME8LLz/3zb/vLG/t8Lfsequ1ZhVca/LlVR4yJLlyaVcywT9SJlO\nmKCy13SpKl8TY7czyufYrY4lobZjYaIsm90CQQCKhkRGWG/BzRymMyp2DJjHKFB4\nUqbx3FuJPqy7HcpeP1P4t1rCgbsSLNTefRGr9mlZHYqPSPYuheQImxCmTshZAkEA\nwAp5u+vfft1yPoT2r+l4/G99P8PLFJcTdbwEOlm8qWcrLW47dIE0FqEml3536b1v\nYGdMxFYHRjoIGSdzpKUI0QJAAqPdDp+y7kWaeIbKkp3Z3bLrj5wii2QAy2YlBDKe\npXrvruWJvL75OCYcxRju3DpVaoYqEmso+UEiQEDRB42YYg==\n-----END RSA PRIVATE KEY-----\n","Username":"wołchw"}
\ No newline at end of file