github.com/LukasHeimann/cloudfoundrycli/v8@v8.4.4/integration/shared/isolated/logs_command_test.go (about) 1 package isolated 2 3 import ( 4 "encoding/base64" 5 "fmt" 6 "net/http" 7 "runtime" 8 "strconv" 9 "time" 10 11 "github.com/LukasHeimann/cloudfoundrycli/v8/integration/helpers" 12 . "github.com/onsi/ginkgo" 13 . "github.com/onsi/gomega" 14 . "github.com/onsi/gomega/gbytes" 15 . "github.com/onsi/gomega/gexec" 16 "github.com/onsi/gomega/ghttp" 17 ) 18 19 var _ = Describe("logs command", func() { 20 var server *ghttp.Server 21 22 BeforeEach(func() { 23 server = helpers.StartAndTargetMockServerWithAPIVersions(helpers.DefaultV2Version, helpers.DefaultV3Version) 24 helpers.AddLoginRoutes(server) 25 26 helpers.AddHandler(server, 27 http.MethodGet, 28 "/v3/organizations?order_by=name", 29 http.StatusOK, 30 []byte( 31 `{ 32 "total_results": 1, 33 "total_pages": 1, 34 "resources": [ 35 { 36 "guid": "f3ea75ba-ea6b-439f-8889-b07abf718e6a", 37 "name": "some-fake-org" 38 } 39 ]}`), 40 ) 41 42 // The v6 version of this command makes the below request when logging in. 43 // See below for comparison with v7 version. 44 helpers.AddHandler(server, 45 http.MethodGet, 46 "/v3/spaces?organization_guids=f3ea75ba-ea6b-439f-8889-b07abf718e6a", 47 http.StatusOK, 48 []byte( 49 `{ 50 "total_results": 1, 51 "total_pages": 1, 52 "resources": [ 53 { 54 "guid": "1704b4e7-14bb-4b7b-bc23-0b8d23a60238", 55 "name": "some-fake-space" 56 } 57 ]}`), 58 ) 59 60 // The v7 version of this command makes the below request when logging in, 61 // which is similar to the v6 version above except for the additional 'order_by' 62 // query parameter. Rather than split these tests across two files, we just add 63 // a handler for both routes (with and without 'order_by'). 64 helpers.AddHandler(server, 65 http.MethodGet, 66 "/v3/spaces?order_by=name&organization_guids=f3ea75ba-ea6b-439f-8889-b07abf718e6a", 67 http.StatusOK, 68 []byte( 69 `{ 70 "total_results": 1, 71 "total_pages": 1, 72 "resources": [ 73 { 74 "guid": "1704b4e7-14bb-4b7b-bc23-0b8d23a60238", 75 "name": "some-fake-space" 76 } 77 ]}`), 78 ) 79 80 helpers.AddHandler(server, 81 http.MethodGet, 82 "/v2/apps?q=name%3Asome-fake-app&q=space_guid%3A1704b4e7-14bb-4b7b-bc23-0b8d23a60238", 83 http.StatusOK, 84 []byte( 85 `{ 86 "total_results": 1, 87 "total_pages": 1, 88 "resources": [ 89 { 90 "metadata": { 91 "guid": "d5d27772-315f-474b-8673-57e34ce2db2c" 92 }, 93 "entity": { 94 "name": "some-fake-app" 95 } 96 } 97 ]}`), 98 ) 99 100 helpers.AddHandler(server, 101 http.MethodGet, 102 "/v3/apps?names=some-fake-app&space_guids=1704b4e7-14bb-4b7b-bc23-0b8d23a60238", 103 http.StatusOK, 104 []byte( 105 `{ 106 "total_results": 1, 107 "total_pages": 1, 108 "resources": [ 109 { 110 "guid": "d5d27772-315f-474b-8673-57e34ce2db2c", 111 "name": "some-fake-app" 112 } 113 ]}`), 114 ) 115 116 helpers.AddHandler(server, 117 http.MethodGet, 118 "/api/v1/info", 119 http.StatusOK, 120 []byte(`{"version":"2.6.8"}`), 121 ) 122 }) 123 124 AfterEach(func() { 125 server.Close() 126 }) 127 128 Describe("streaming logs", func() { 129 130 const logMessage = "hello from log-cache" 131 var returnEmptyEnvelope bool 132 133 onWindows := runtime.GOOS == "windows" 134 135 BeforeEach(func() { 136 latestEnvelopeTimestamp := "1581447006352020890" 137 latestEnvelopeTimestampMinusOneSecond := "1581447005352020890" 138 nextEnvelopeTimestamp := "1581447009352020890" 139 nextEnvelopeTimestampPlusOneNanosecond := "1581447009352020891" 140 141 server.RouteToHandler( 142 http.MethodGet, 143 "/api/v1/read/d5d27772-315f-474b-8673-57e34ce2db2c", 144 func(w http.ResponseWriter, r *http.Request) { 145 w.WriteHeader(http.StatusOK) 146 switch r.URL.RawQuery { 147 case fmt.Sprintf("descending=true&limit=1&start_time=%s", strconv.FormatInt(time.Time{}.UnixNano(), 10)): 148 if returnEmptyEnvelope { 149 _, err := w.Write([]byte(`{}`)) 150 Expect(err).ToNot(HaveOccurred()) 151 returnEmptyEnvelope = false // Allow the CLI to continue after receiving an empty envelope 152 } else { 153 _, err := w.Write([]byte(fmt.Sprintf(` 154 { 155 "envelopes": { 156 "batch": [ 157 { 158 "timestamp": "%s", 159 "source_id": "d5d27772-315f-474b-8673-57e34ce2db2c" 160 } 161 ] 162 } 163 }`, latestEnvelopeTimestamp))) 164 Expect(err).ToNot(HaveOccurred()) 165 } 166 case fmt.Sprintf("envelope_types=LOG&start_time=%s", latestEnvelopeTimestampMinusOneSecond): 167 _, err := w.Write([]byte(fmt.Sprintf(`{ 168 "envelopes": { 169 "batch": [ 170 { 171 "timestamp": "%s", 172 "source_id": "d5d27772-315f-474b-8673-57e34ce2db2c", 173 "tags": { 174 "__v1_type": "LogMessage" 175 }, 176 "log": { 177 "payload": "%s", 178 "type": "OUT" 179 } 180 } 181 ] 182 } 183 }`, nextEnvelopeTimestamp, base64.StdEncoding.EncodeToString([]byte(logMessage))))) 184 Expect(err).ToNot(HaveOccurred()) 185 case fmt.Sprintf("envelope_types=LOG&start_time=%s", nextEnvelopeTimestampPlusOneNanosecond): 186 _, err := w.Write([]byte("{}")) 187 Expect(err).ToNot(HaveOccurred()) 188 default: 189 Fail(fmt.Sprintf("Unhandled log-cache api query string: %s", r.URL.RawQuery)) 190 } 191 }) 192 }) 193 194 When("there already is an envelope in the log cache", func() { 195 JustBeforeEach(func() { 196 returnEmptyEnvelope = false 197 }) 198 199 It("fetches logs with a timestamp just prior to the latest log envelope", func() { 200 username, password := helpers.GetCredentials() 201 session := helpers.CF("login", "-a", server.URL(), "-u", username, "-p", password, "--skip-ssl-validation") 202 Eventually(session).Should(Exit(0)) 203 204 session = helpers.CF("logs", "some-fake-app") 205 Eventually(session).Should(Say(logMessage)) 206 if onWindows { 207 session.Kill() 208 Eventually(session).Should(Exit()) 209 } else { 210 session.Interrupt() 211 Eventually(session).Should(Exit(0), "Interrupt should be handled and fail gracefully") 212 } 213 }) 214 }) 215 216 When("there is not yet an envelope in the log cache", func() { 217 JustBeforeEach(func() { 218 returnEmptyEnvelope = true 219 }) 220 221 // TODO: the case where log-cache has no envelopes yet may be "special": we may want to switch to "start from your oldest envelope" approach. 222 It("retries until there is an initial envelope, and then fetches logs with a timestamp just prior to the latest log envelope", func() { 223 username, password := helpers.GetCredentials() 224 session := helpers.CF("login", "-a", server.URL(), "-u", username, "-p", password, "--skip-ssl-validation") 225 Eventually(session).Should(Exit(0)) 226 227 session = helpers.CF("logs", "some-fake-app") 228 Eventually(session).Should(Say(logMessage)) 229 if onWindows { 230 session.Kill() 231 Eventually(session).Should(Exit()) 232 } else { 233 session.Interrupt() 234 Eventually(session).Should(Exit(0)) 235 } 236 }) 237 }) 238 }) 239 })