Skip to content

Commit 3706861

Browse files
authored
Merge pull request #37 from getsumio/develop
Develop
2 parents aa8a812 + a37c304 commit 3706861

File tree

7 files changed

+119
-82
lines changed

7 files changed

+119
-82
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v2.0
1+
v2.0.1

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/getsumio/getsum
33
go 1.13
44

55
require (
6+
github.com/google/uuid v1.1.1
67
github.com/logrusorgru/aurora v0.0.0-20191017060258-dc85c304c434
78
github.com/mitchellh/go-homedir v1.1.0
89
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
2+
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
3+
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
24
github.com/kisielk/errcheck v1.2.0 h1:reN85Pxc5larApoH1keMBiu2GWtPqXQ1nc9gx+jOU+E=
35
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
46
github.com/logrusorgru/aurora v0.0.0-20191017060258-dc85c304c434 h1:im9kkmH0WWwxzegiv18gSUJbuXR9y028rXrWuPp6Jug=

internal/config/config.go

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,26 @@ package config
22

33
//config dto
44
type Config struct {
5-
File *string `json:"file"`
6-
LocalOnly *bool `json:"local_only"`
7-
Proxy *string `json:"proxy"`
8-
Algorithm []string `json:"algorithm"`
9-
Cheksum *string `json:"cheksum"`
10-
RemoteOnly *bool `json:"remote_only"`
11-
OnlyChecksum *bool `json:"only_checksum"`
12-
LogLevel *string `json:"log_level"`
13-
Timeout *int `json:"timeout"`
14-
All *bool `json:"all"`
15-
Key *string `json:"key"`
16-
Supplier *string `json:"supplier"`
17-
OnFailure *string `json:"on_failure"`
18-
Serve *bool `json:"serve"`
19-
Listen *string `json:"listen"`
20-
Port *int `json:"port"`
21-
Servers ServerConfigs `json:"servers"`
22-
Dir *string `json:"dir"`
23-
TLSKey *string `json:"tls_key"`
24-
TLSCert *string `json:"tls_cert"`
25-
ServerConfig *string `json:"server_config"`
26-
Keep *bool `json:"keep"`
5+
File *string `json:"file"`
6+
LocalOnly *bool
7+
Proxy *string `json:"proxy"`
8+
Algorithm []string `json:"algorithm"`
9+
Cheksum *string `json:"cheksum"`
10+
RemoteOnly *bool
11+
LogLevel *string
12+
Timeout *int `json:"timeout"`
13+
All *bool `json:"all"`
14+
Key *string `json:"key"`
15+
Supplier *string `json:"supplier"`
16+
Serve *bool
17+
Listen *string
18+
Port *int
19+
Servers ServerConfigs
20+
Dir *string
21+
TLSKey *string
22+
TLSCert *string
23+
ServerConfig *string
24+
Keep *bool
2725
}
2826

2927
//this is for collecting server info from yaml files

internal/provider/onpremiseprovider.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package providers
33
import (
44
"bytes"
55
"encoding/json"
6+
"fmt"
67
"net/http"
78

89
"github.com/getsumio/getsum/internal/config"
@@ -22,6 +23,7 @@ type RemoteProvider struct {
2223
address string
2324
status *status.Status
2425
hasRunError bool
26+
processId string
2527
}
2628

2729
//notifies server to run
@@ -52,6 +54,7 @@ func remoteRun(l *RemoteProvider) {
5254
if err != nil {
5355
//set error as provider status
5456
//status() method will handle
57+
logger.Debug("Run response has an error: %s", err.Error())
5558
setErrorStatus(err, l)
5659
l.hasRunError = true
5760
return
@@ -61,10 +64,15 @@ func remoteRun(l *RemoteProvider) {
6164
decoder := json.NewDecoder(resp.Body)
6265
err = decoder.Decode(l.status)
6366
if err != nil {
67+
logger.Debug("Run response received for %s but got an error: %s", l.Name, err.Error())
6468
setErrorStatus(err, l)
6569
l.hasRunError = true
6670
}
6771
l.hasRunError = l.status.Type == status.ERROR
72+
if !l.hasRunError {
73+
logger.Trace("Process id for runner: %s", l.status.Value)
74+
l.processId = l.status.Value
75+
}
6876

6977
}
7078

@@ -77,8 +85,9 @@ func closeResponse(response *http.Response) {
7785

7886
//fetches server using GET and collects its status
7987
func remoteStatus(l *RemoteProvider) *status.Status {
88+
statusAddress := fmt.Sprintf("%s/%s", l.address, l.processId)
8089
//reach the server
81-
resp, err := l.client.Get(l.address)
90+
resp, err := l.client.Get(statusAddress)
8291
if err != nil {
8392
setErrorStatus(err, l)
8493
return l.status
@@ -99,7 +108,8 @@ func remoteStatus(l *RemoteProvider) *status.Status {
99108
//trigger termination on remote server using http DELETE
100109
func remoteTerminate(l *RemoteProvider) error {
101110
//let the server know process terminated
102-
req, err := http.NewRequest("DELETE", l.address, nil)
111+
deleteAddress := fmt.Sprintf("%s/%s", l.address, l.processId)
112+
req, err := http.NewRequest("DELETE", deleteAddress, nil)
103113
if err != nil {
104114
return err
105115
}

internal/servers/onpremise.go

Lines changed: 77 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ package servers
33
import (
44
"encoding/json"
55
"fmt"
6+
"html"
67
"net/http"
8+
"path"
9+
"regexp"
710
"sync"
811

912
"github.com/getsumio/getsum/internal/config"
@@ -12,22 +15,32 @@ import (
1215
"github.com/getsumio/getsum/internal/status"
1316
. "github.com/getsumio/getsum/internal/supplier"
1417
"github.com/getsumio/getsum/internal/validation"
18+
"github.com/google/uuid"
1519
)
1620

1721
//server instance to run in server mode
1822
type OnPremiseServer struct {
1923
StoragePath string
20-
Supplier Supplier
21-
mux sync.Mutex
24+
mux *sync.RWMutex
25+
suppliers map[string]Supplier
2226
}
2327

2428
var factory ISupplierFactory
2529

30+
const uuidPattern = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$"
31+
32+
var regex *regexp.Regexp = regexp.MustCompile(uuidPattern)
33+
34+
const default_capacity = 250
35+
const threshold = 150
36+
2637
//start server in given config listen address and port or tls details
2738
//TODO add interface support
2839
func (s *OnPremiseServer) Start(config *config.Config) error {
2940
logger.Level = logger.LevelInfo
3041
factory = new(SupplierFactory)
42+
s.suppliers = make(map[string]Supplier)
43+
s.mux = &sync.RWMutex{}
3144
http.HandleFunc("/", s.handle)
3245
listenAddress := fmt.Sprintf("%s:%d", *config.Listen, *config.Port)
3346
var err error
@@ -45,14 +58,18 @@ func (s *OnPremiseServer) Start(config *config.Config) error {
4558

4659
//get executed to reach status
4760
//so collect status if there is any runner
48-
func handleGet(s *OnPremiseServer, w http.ResponseWriter, r *http.Request) {
61+
func handleGet(s *OnPremiseServer, w http.ResponseWriter, r *http.Request, id string) {
4962
//check if any runner
50-
if s.Supplier == nil {
63+
s.mux.RLock()
64+
defer s.mux.RUnlock()
65+
logger.Info("Checking if there is a process with id : %s", id)
66+
supplier, ok := s.suppliers[id]
67+
if !ok {
5168
handleError("There is no running process", w)
5269
return
5370
}
5471
//collect status and return
55-
stat := s.Supplier.Status()
72+
stat := supplier.Status()
5673
status, err := json.Marshal(stat)
5774
if err != nil {
5875
handleError("System can not parse given status %s", w, err.Error())
@@ -64,14 +81,8 @@ func handleGet(s *OnPremiseServer, w http.ResponseWriter, r *http.Request) {
6481

6582
//post executed to Run a new calculation
6683
func handlePost(s *OnPremiseServer, w http.ResponseWriter, r *http.Request) {
67-
//check if any runner
68-
if s.Supplier != nil {
69-
stat := s.Supplier.Status()
70-
if stat.Type <= status.RUNNING {
71-
handleError("Server already running another process", w)
72-
return
73-
}
74-
}
84+
s.mux.Lock()
85+
defer s.mux.Unlock()
7586
//read the config from request
7687
jsonDecoder := json.NewDecoder(r.Body)
7788
config := &Config{}
@@ -85,49 +96,73 @@ func handlePost(s *OnPremiseServer, w http.ResponseWriter, r *http.Request) {
8596
if err != nil {
8697
handleError(err.Error(), w)
8798
return
88-
8999
}
90100

91101
//get supplier instance, only single algo supported on server mode
92102
var algorithm = ValueOf(&config.Algorithm[0])
93103
config.Dir = &s.StoragePath
94-
s.Supplier, err = factory.GetSupplierByAlgo(config, &algorithm)
104+
supplier, err := factory.GetSupplierByAlgo(config, &algorithm)
95105
//something went wrong, TODO add error handler
96106
if err != nil {
97107
handleError("Can not create algorithm runner instance: "+err.Error(), w)
98108
return
99109
}
100-
go s.Supplier.Run()
101-
handleGet(s, w, r)
110+
processId := uuid.New().String()
111+
stat := supplier.Status()
112+
stat.Type = status.STARTED
113+
stat.Value = processId
114+
jsonStat, err := json.Marshal(stat)
115+
if err != nil {
116+
handleError("Can not parse status"+err.Error(), w)
117+
return
118+
}
119+
w.Write(jsonStat)
120+
go supplier.Run()
121+
s.suppliers[processId] = supplier
102122
logger.Info("Process started")
123+
s.ensureCapacity()
103124
}
104125

105126
//terminates running calculation
106-
func handleDelete(s *OnPremiseServer, w http.ResponseWriter, r *http.Request) {
107-
if s.Supplier == nil {
127+
func handleDelete(s *OnPremiseServer, w http.ResponseWriter, r *http.Request, id string) {
128+
s.mux.Lock()
129+
defer s.mux.Unlock()
130+
supplier, ok := s.suppliers[id]
131+
if !ok {
108132
handleError("There is no running process", w)
109133
return
110134
}
111-
s.Supplier.Terminate()
112-
s.Supplier.Delete()
135+
136+
supplier.Terminate()
137+
supplier.Delete()
113138
w.WriteHeader(http.StatusOK)
114-
s.Supplier = nil
139+
delete(s.suppliers, id)
115140
logger.Info("Process terminated")
116141
}
117142

118143
//delegates GET POST DELETE main server listener
119144
func (s *OnPremiseServer) handle(w http.ResponseWriter, r *http.Request) {
120-
s.mux.Lock()
121-
defer s.mux.Unlock()
122-
123145
logger.LogRequest(r)
124146
switch r.Method {
125147
case "GET":
126-
handleGet(s, w, r)
148+
requestId := path.Base(html.EscapeString(r.URL.Path))
149+
if !regex.MatchString(requestId) {
150+
logger.Error("Request is not a valid request id! %s", requestId)
151+
w.WriteHeader(http.StatusNotFound)
152+
return
153+
}
154+
handleGet(s, w, r, requestId)
127155
case "POST":
128156
handlePost(s, w, r)
129157
case "DELETE":
130-
handleDelete(s, w, r)
158+
requestId := path.Base(html.EscapeString(r.URL.Path))
159+
if !regex.MatchString(requestId) {
160+
logger.Error("Request is not a valid request id! %s", requestId)
161+
w.WriteHeader(http.StatusNotFound)
162+
return
163+
}
164+
165+
handleDelete(s, w, r, requestId)
131166
default:
132167
w.WriteHeader(http.StatusMethodNotAllowed)
133168
logger.Error("Can not handle request method rejecting request")
@@ -147,3 +182,18 @@ func handleError(message string, w http.ResponseWriter, params ...interface{}) {
147182
w.Write(jsonStatus)
148183
return
149184
}
185+
186+
func (s *OnPremiseServer) ensureCapacity() {
187+
if len(s.suppliers) >= default_capacity {
188+
var i int = 0
189+
for k := range s.suppliers {
190+
if i >= threshold {
191+
supplier := s.suppliers[k]
192+
if supplier.Status().Type >= status.RUNNING {
193+
delete(s.suppliers, k)
194+
}
195+
}
196+
i++
197+
}
198+
}
199+
}

internal/supplier/supplierfactory.go

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ func (factory *SupplierFactory) GetSupplierByAlgo(config *Config, algorithm *Alg
3535

3636
}
3737

38-
var cache map[string]Supplier = make(map[string]Supplier)
39-
4038
func isSupplierSupportsAlgorithm(supplier Supplier, algo *Algorithm) bool {
4139
supports := false
4240
for _, supportedAlgo := range supplier.Supports() {
@@ -50,39 +48,21 @@ func isSupplierSupportsAlgorithm(supplier Supplier, algo *Algorithm) bool {
5048
//creates supplier instance
5149
func getSupplierInstance(config *Config, algo *Algorithm) (Supplier, error) {
5250
if *config.Supplier == "go" {
53-
s, ok := cache["go"+string(*algo)]
54-
if !ok {
55-
s = &GoSupplier{}
56-
cache["go"+string(*algo)] = s
57-
58-
}
51+
s := &GoSupplier{}
5952
setFields(s.Data(), *algo, config)
6053
return s, nil
6154
} else if *config.Supplier == "openssl" {
62-
s, ok := cache["openssl"+string(*algo)]
63-
if !ok {
64-
s = &CommandSupplier{Type: OPENSSL}
65-
cache["openssl"+string(*algo)] = s
66-
67-
}
55+
s := &CommandSupplier{Type: OPENSSL}
6856
setFields(s.Data(), *algo, config)
6957
return s, nil
7058
}
7159
switch runtime.GOOS {
7260
case "linux", "mac":
73-
s, ok := cache["mac"+string(*algo)]
74-
if !ok {
75-
s = &CommandSupplier{Type: UNIX}
76-
cache["mac"+string(*algo)] = s
77-
}
61+
s := &CommandSupplier{Type: UNIX}
7862
setFields(s.Data(), *algo, config)
7963
return s, nil
8064
case "windows":
81-
s, ok := cache["windows"+string(*algo)]
82-
if !ok {
83-
s = &CommandSupplier{Type: WINDOWS}
84-
cache["windows"+string(*algo)] = s
85-
}
65+
s := &CommandSupplier{Type: WINDOWS}
8666
setFields(s.Data(), *algo, config)
8767
return s, nil
8868
default:
@@ -94,17 +74,13 @@ func getSupplierInstance(config *Config, algo *Algorithm) (Supplier, error) {
9474

9575
//utility to set fields
9676
func setFields(base *BaseSupplier, algo Algorithm, config *Config) {
97-
if base.status == nil {
98-
base.status = &status.Status{}
99-
}
77+
base.status = &status.Status{}
10078
base.status.Type = status.PREPARED
10179
base.status.Value = ""
10280
base.status.Checksum = ""
10381
base.Algorithm = algo
10482
base.Key = *config.Key
105-
if base.File == nil {
106-
base.File = &File{}
107-
}
83+
base.File = &File{}
10884
base.File.Reset()
10985
base.File.Url = *config.File
11086
base.File.Status = base.status

0 commit comments

Comments
 (0)