github.com/kubeshop/testkube@v1.17.23/pkg/api/v1/client/common.go (about) 1 package client 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/json" 7 "fmt" 8 "io" 9 10 "github.com/kubeshop/testkube/pkg/api/v1/testkube" 11 "github.com/kubeshop/testkube/pkg/executor/output" 12 "github.com/kubeshop/testkube/pkg/logs/events" 13 "github.com/kubeshop/testkube/pkg/utils" 14 ) 15 16 // Version is client version literal 17 const Version = "v1" 18 19 // TestkubeInstallationNamespace where Testkube is installed 20 const TestkubeInstallationNamespace = "testkube" 21 22 type TestingType string 23 24 const ( 25 Test TestingType = "test" 26 Execution TestingType = "execution" 27 ) 28 29 // StreamToLogsChannel converts io.Reader with SSE data like `data: {"type": "event", "message":"something"}` 30 // to channel of output.Output objects, helps with logs streaming from SSE endpoint (passed from job executor) 31 func StreamToLogsChannel(resp io.Reader, logs chan output.Output) { 32 reader := bufio.NewReader(resp) 33 34 for { 35 b, err := utils.ReadLongLine(reader) 36 if err != nil { 37 if err != io.EOF { 38 fmt.Printf("Read long line error: %+v' \n", err) 39 } 40 41 break 42 } 43 chunk := trimDataChunk(b) 44 45 // ignore lines which are not JSON objects 46 if len(chunk) < 2 || chunk[0] != '{' { 47 continue 48 } 49 50 // convert to output.Output object 51 out := output.Output{} 52 err = json.Unmarshal(chunk, &out) 53 if err != nil { 54 fmt.Printf("Unmarshal chunk error: %+v, json:'%s' \n", err, chunk) 55 continue 56 } 57 58 logs <- out 59 } 60 } 61 62 // StreamToLogsChannelV2 converts io.Reader with SSE data like `data: {"type": "event", "message":"something"}` 63 // to channel of output.Output objects, helps with logs version 2 streaming from SSE endpoint (passed from job executor) 64 func StreamToLogsChannelV2(resp io.Reader, logs chan events.Log) { 65 reader := bufio.NewReader(resp) 66 67 for { 68 b, err := utils.ReadLongLine(reader) 69 if err != nil { 70 if err != io.EOF { 71 fmt.Printf("Read long line error: %+v' \n", err) 72 } 73 74 break 75 } 76 chunk := trimDataChunk(b) 77 78 // ignore lines which are not JSON objects 79 if len(chunk) < 2 || chunk[0] != '{' { 80 continue 81 } 82 83 // convert to events.Log object 84 out := events.Log{} 85 err = json.Unmarshal(chunk, &out) 86 if err != nil { 87 fmt.Printf("Unmarshal chunk error: %+v, json:'%s' \n", err, chunk) 88 continue 89 } 90 91 logs <- out 92 } 93 } 94 95 // StreamToTestWorkflowExecutionNotificationsChannel converts io.Reader with SSE data to channel of actual notifications 96 func StreamToTestWorkflowExecutionNotificationsChannel(resp io.Reader, notifications chan testkube.TestWorkflowExecutionNotification) { 97 reader := bufio.NewReader(resp) 98 99 for { 100 b, err := utils.ReadLongLine(reader) 101 if err != nil { 102 if err != io.EOF { 103 fmt.Printf("Read long line error: %+v' \n", err) 104 } 105 106 break 107 } 108 chunk := trimDataChunk(b) 109 110 // ignore lines which are not JSON objects 111 if len(chunk) < 2 || chunk[0] != '{' { 112 continue 113 } 114 115 out := testkube.TestWorkflowExecutionNotification{} 116 err = json.Unmarshal(chunk, &out) 117 if err != nil { 118 fmt.Printf("Unmarshal chunk error: %+v, json:'%s' \n", err, chunk) 119 continue 120 } 121 122 notifications <- out 123 } 124 } 125 126 // trimDataChunk remove data: and newlines from incoming SSE data line 127 func trimDataChunk(in []byte) []byte { 128 prefix := []byte("data: ") 129 postfix := []byte("\\n\\n") 130 chunk := bytes.Replace(in, prefix, []byte{}, 1) 131 chunk = bytes.Replace(chunk, postfix, []byte{}, 1) 132 133 return chunk 134 }