github.com/eth-easl/loader@v0.0.0-20230908084258-8a37e1d94279/pkg/driver/http_client.go (about) 1 /* 2 * MIT License 3 * 4 * Copyright (c) 2023 EASL and the vHive community 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in all 14 * copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 25 package driver 26 27 import ( 28 "bytes" 29 "crypto/tls" 30 "encoding/json" 31 "net/http" 32 "os/exec" 33 "strings" 34 "sync" 35 "time" 36 37 "github.com/eth-easl/loader/pkg/common" 38 "github.com/eth-easl/loader/pkg/config" 39 mc "github.com/eth-easl/loader/pkg/metric" 40 log "github.com/sirupsen/logrus" 41 ) 42 43 type ActivationMetadata struct { 44 Duration uint32 //ms 45 StartType mc.StartType 46 WaitTime int64 //ms 47 InitTime int64 //ms 48 } 49 50 func InvokeOpenWhisk(function *common.Function, runtimeSpec *common.RuntimeSpecification, cfg *config.LoaderConfiguration, AnnouceDoneExe *sync.WaitGroup, ReadOpenWhiskMetadata *sync.Mutex) (bool, *mc.ExecutionRecordOpenWhisk) { 51 log.Tracef("(Invoke)\t %s: %d[ms], %d[MiB]", function.Name, runtimeSpec.Runtime, runtimeSpec.Memory) 52 53 record := &mc.ExecutionRecordOpenWhisk{ 54 ExecutionRecordBase: mc.ExecutionRecordBase{ 55 RequestedDuration: uint32(runtimeSpec.Runtime * 1e3), 56 }, 57 } 58 59 //////////////////////////////////// 60 // INVOKE FUNCTION 61 //////////////////////////////////// 62 start := time.Now() 63 record.StartTime = start.UnixMicro() 64 record.Instance = function.Name 65 66 http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} 67 requestURL := function.Endpoint 68 req, err := http.NewRequest(http.MethodGet, requestURL, nil) 69 if err != nil { 70 log.Debugf("http request creation failed for function %s - %s", function.Name, err) 71 72 record.ResponseTime = time.Since(start).Microseconds() 73 record.ConnectionTimeout = true 74 75 AnnouceDoneExe.Done() 76 77 return false, record 78 } 79 80 res, err := http.DefaultClient.Do(req) 81 if err != nil { 82 log.Debugf("http timeout exceeded for function %s - %s", function.Name, err) 83 84 record.ResponseTime = time.Since(start).Microseconds() 85 record.ConnectionTimeout = true 86 87 AnnouceDoneExe.Done() 88 89 return false, record 90 } 91 92 record.HttpStatusCode = res.StatusCode 93 if record.HttpStatusCode < 200 || record.HttpStatusCode >= 300 { 94 log.Debugf("http request for function %s failed - error code: %d", function.Name, record.HttpStatusCode) 95 96 record.ResponseTime = time.Since(start).Microseconds() 97 record.ConnectionTimeout = true 98 99 AnnouceDoneExe.Done() 100 101 return false, record 102 } 103 104 record.ActivationID = res.Header.Get("X-Openwhisk-Activation-Id") 105 record.ResponseTime = time.Since(start).Microseconds() 106 107 AnnouceDoneExe.Done() 108 AnnouceDoneExe.Wait() 109 110 ReadOpenWhiskMetadata.Lock() 111 112 //read data from OpenWhisk based on the activation ID 113 cmd := exec.Command("wsk", "-i", "activation", "get", record.ActivationID) 114 var out bytes.Buffer 115 cmd.Stdout = &out 116 err = cmd.Run() 117 if err != nil { 118 log.Debugf("error reading activation information from OpenWhisk %s - %s", function.Name, err) 119 120 ReadOpenWhiskMetadata.Unlock() 121 122 return false, record 123 } 124 125 ReadOpenWhiskMetadata.Unlock() 126 127 err, activationMetadata := parseActivationMetadata(out.String()) 128 if err != nil { 129 log.Debugf("error parsing activation metadata %s - %s", function.Name, err) 130 131 return false, record 132 } 133 134 record.ActualDuration = activationMetadata.Duration * 1000 //ms to micro sec 135 record.StartType = activationMetadata.StartType 136 record.InitTime = activationMetadata.InitTime * 1000 //ms to micro sec 137 record.WaitTime = activationMetadata.WaitTime * 1000 //ms to micro sec 138 139 log.Tracef("(Replied)\t %s: %d[ms]", function.Name, record.ActualDuration) 140 log.Tracef("(E2E Latency) %s: %.2f[ms]\n", function.Name, float64(record.ResponseTime)/1e3) 141 log.Tracef("(Client status code) %s: %d", function.Name, record.HttpStatusCode) 142 143 return true, record 144 } 145 146 func parseActivationMetadata(response string) (error, ActivationMetadata) { 147 var result ActivationMetadata 148 var jsonMap map[string]interface{} 149 150 ind := strings.Index(response, "{") 151 err := json.Unmarshal([]byte(response[ind:]), &jsonMap) 152 if err != nil { 153 return err, result 154 } 155 156 result.Duration = uint32(jsonMap["duration"].(float64)) 157 result.StartType = mc.Hot 158 result.InitTime = 0 159 annotations := jsonMap["annotations"].([]interface{}) 160 for i := 0; i < len(annotations); i++ { 161 annotation := annotations[i].(map[string]interface{}) 162 163 if annotation["key"] == "waitTime" { 164 result.WaitTime = int64(annotation["value"].(float64)) 165 } else if annotation["key"] == "initTime" { 166 result.StartType = mc.Cold 167 result.InitTime = int64(annotation["value"].(float64)) 168 } 169 } 170 171 return nil, result 172 }