github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/daemon/api_debug_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2019 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package daemon_test 21 22 import ( 23 "bytes" 24 "encoding/json" 25 "net/http" 26 27 "gopkg.in/check.v1" 28 29 "github.com/snapcore/snapd/daemon" 30 "github.com/snapcore/snapd/overlord/state" 31 "github.com/snapcore/snapd/testutil" 32 "github.com/snapcore/snapd/timings" 33 ) 34 35 var _ = check.Suite(&postDebugSuite{}) 36 37 type postDebugSuite struct { 38 apiBaseSuite 39 } 40 41 func (s *postDebugSuite) TestPostDebugEnsureStateSoon(c *check.C) { 42 s.daemonWithOverlordMock(c) 43 44 soon := 0 45 var origEnsureStateSoon func(*state.State) 46 origEnsureStateSoon, restore := daemon.MockEnsureStateSoon(func(st *state.State) { 47 soon++ 48 origEnsureStateSoon(st) 49 }) 50 defer restore() 51 52 buf := bytes.NewBufferString(`{"action": "ensure-state-soon"}`) 53 req, err := http.NewRequest("POST", "/v2/debug", buf) 54 c.Assert(err, check.IsNil) 55 56 rsp := s.syncReq(c, req, nil) 57 c.Check(rsp.Result, check.Equals, true) 58 c.Check(soon, check.Equals, 1) 59 } 60 61 func (s *postDebugSuite) TestPostDebugGetBaseDeclaration(c *check.C) { 62 _ = s.daemon(c) 63 64 buf := bytes.NewBufferString(`{"action": "get-base-declaration"}`) 65 req, err := http.NewRequest("POST", "/v2/debug", buf) 66 c.Assert(err, check.IsNil) 67 68 rsp := s.syncReq(c, req, nil) 69 c.Check(rsp.Result.(map[string]interface{})["base-declaration"], 70 testutil.Contains, "type: base-declaration") 71 } 72 73 func (s *postDebugSuite) testDebugConnectivityHappy(c *check.C, post bool) { 74 _ = s.daemon(c) 75 76 s.connectivityResult = map[string]bool{ 77 "good.host.com": true, 78 "another.good.host.com": true, 79 } 80 81 var req *http.Request 82 var err error 83 if post { 84 buf := bytes.NewBufferString(`{"action": "connectivity"}`) 85 req, err = http.NewRequest("POST", "/v2/debug", buf) 86 } else { 87 req, err = http.NewRequest("GET", "/v2/debug?aspect=connectivity", nil) 88 } 89 c.Assert(err, check.IsNil) 90 91 rsp := s.syncReq(c, req, nil) 92 c.Check(rsp.Result, check.DeepEquals, daemon.ConnectivityStatus{ 93 Connectivity: true, 94 Unreachable: []string(nil), 95 }) 96 } 97 98 func (s *postDebugSuite) TestPostDebugConnectivityHappy(c *check.C) { 99 s.testDebugConnectivityHappy(c, true) 100 } 101 102 func (s *postDebugSuite) TestGetDebugConnectivityHappy(c *check.C) { 103 s.testDebugConnectivityHappy(c, false) 104 } 105 106 func (s *postDebugSuite) testDebugConnectivityUnhappy(c *check.C, post bool) { 107 _ = s.daemon(c) 108 109 s.connectivityResult = map[string]bool{ 110 "good.host.com": true, 111 "bad.host.com": false, 112 } 113 114 var req *http.Request 115 var err error 116 if post { 117 buf := bytes.NewBufferString(`{"action": "connectivity"}`) 118 req, err = http.NewRequest("POST", "/v2/debug", buf) 119 } else { 120 req, err = http.NewRequest("GET", "/v2/debug?aspect=connectivity", nil) 121 } 122 c.Assert(err, check.IsNil) 123 124 rsp := s.syncReq(c, req, nil) 125 c.Check(rsp.Result, check.DeepEquals, daemon.ConnectivityStatus{ 126 Connectivity: false, 127 Unreachable: []string{"bad.host.com"}, 128 }) 129 } 130 131 func (s *postDebugSuite) TestPostDebugConnectivityUnhappy(c *check.C) { 132 s.testDebugConnectivityUnhappy(c, true) 133 } 134 135 func (s *postDebugSuite) TestGetDebugConnectivityUnhappy(c *check.C) { 136 s.testDebugConnectivityUnhappy(c, false) 137 } 138 139 func (s *postDebugSuite) TestGetDebugBaseDeclaration(c *check.C) { 140 _ = s.daemon(c) 141 142 req, err := http.NewRequest("GET", "/v2/debug?aspect=base-declaration", nil) 143 c.Assert(err, check.IsNil) 144 145 rsp := s.syncReq(c, req, nil) 146 147 c.Check(rsp.Result.(map[string]interface{})["base-declaration"], 148 testutil.Contains, "type: base-declaration") 149 } 150 151 func mockDurationThreshold() func() { 152 oldDurationThreshold := timings.DurationThreshold 153 restore := func() { 154 timings.DurationThreshold = oldDurationThreshold 155 } 156 timings.DurationThreshold = 0 157 return restore 158 } 159 160 func (s *postDebugSuite) getDebugTimings(c *check.C, request string) []interface{} { 161 defer mockDurationThreshold()() 162 163 s.daemonWithOverlordMock(c) 164 165 req, err := http.NewRequest("GET", request, nil) 166 c.Assert(err, check.IsNil) 167 168 st := s.d.Overlord().State() 169 st.Lock() 170 171 chg1 := st.NewChange("foo", "...") 172 task1 := st.NewTask("bar", "...") 173 chg1.AddTask(task1) 174 task1.SetStatus(state.DoingStatus) 175 176 chg2 := st.NewChange("foo", "...") 177 task2 := st.NewTask("bar", "...") 178 chg2.AddTask(task2) 179 180 chg3 := st.NewChange("foo", "...") 181 task3 := st.NewTask("bar", "...") 182 chg3.AddTask(task3) 183 184 tm1 := state.TimingsForTask(task3) 185 sp1 := tm1.StartSpan("span", "span...") 186 sp1.Stop() 187 tm1.Save(st) 188 189 tm2 := timings.New(map[string]string{"ensure": "foo", "change-id": chg1.ID()}) 190 sp2 := tm2.StartSpan("span", "span...") 191 sp2.Stop() 192 tm2.Save(st) 193 194 tm3 := timings.New(map[string]string{"ensure": "foo", "change-id": chg2.ID()}) 195 sp3 := tm3.StartSpan("span", "span...") 196 sp3.Stop() 197 tm3.Save(st) 198 199 tm4 := timings.New(map[string]string{"ensure": "bar", "change-id": chg3.ID()}) 200 sp4 := tm3.StartSpan("span", "span...") 201 sp4.Stop() 202 tm4.Save(st) 203 204 st.Unlock() 205 206 rsp := s.syncReq(c, req, nil) 207 data, err := json.Marshal(rsp.Result) 208 c.Assert(err, check.IsNil) 209 var dataJSON []interface{} 210 json.Unmarshal(data, &dataJSON) 211 212 return dataJSON 213 } 214 215 func (s *postDebugSuite) TestGetDebugTimingsSingleChange(c *check.C) { 216 dataJSON := s.getDebugTimings(c, "/v2/debug?aspect=change-timings&change-id=1") 217 218 c.Check(dataJSON, check.HasLen, 1) 219 tmData := dataJSON[0].(map[string]interface{}) 220 c.Check(tmData["change-id"], check.DeepEquals, "1") 221 c.Check(tmData["change-timings"], check.NotNil) 222 } 223 224 func (s *postDebugSuite) TestGetDebugTimingsEnsureLatest(c *check.C) { 225 dataJSON := s.getDebugTimings(c, "/v2/debug?aspect=change-timings&ensure=foo&all=false") 226 c.Assert(dataJSON, check.HasLen, 1) 227 228 tmData := dataJSON[0].(map[string]interface{}) 229 c.Check(tmData["change-id"], check.DeepEquals, "2") 230 c.Check(tmData["change-timings"], check.NotNil) 231 c.Check(tmData["total-duration"], check.NotNil) 232 } 233 234 func (s *postDebugSuite) TestGetDebugTimingsEnsureAll(c *check.C) { 235 dataJSON := s.getDebugTimings(c, "/v2/debug?aspect=change-timings&ensure=foo&all=true") 236 237 c.Assert(dataJSON, check.HasLen, 2) 238 tmData := dataJSON[0].(map[string]interface{}) 239 c.Check(tmData["change-id"], check.DeepEquals, "1") 240 c.Check(tmData["change-timings"], check.NotNil) 241 c.Check(tmData["total-duration"], check.NotNil) 242 243 tmData = dataJSON[1].(map[string]interface{}) 244 c.Check(tmData["change-id"], check.DeepEquals, "2") 245 c.Check(tmData["change-timings"], check.NotNil) 246 c.Check(tmData["total-duration"], check.NotNil) 247 } 248 249 func (s *postDebugSuite) TestGetDebugTimingsError(c *check.C) { 250 s.daemonWithOverlordMock(c) 251 252 req, err := http.NewRequest("GET", "/v2/debug?aspect=change-timings&ensure=unknown", nil) 253 c.Assert(err, check.IsNil) 254 rsp := s.errorReq(c, req, nil) 255 c.Check(rsp.Status, check.Equals, 400) 256 257 req, err = http.NewRequest("GET", "/v2/debug?aspect=change-timings&change-id=9999", nil) 258 c.Assert(err, check.IsNil) 259 rsp = s.errorReq(c, req, nil) 260 c.Check(rsp.Status, check.Equals, 400) 261 } 262 263 func (s *postDebugSuite) TestMinLane(c *check.C) { 264 st := state.New(nil) 265 st.Lock() 266 defer st.Unlock() 267 268 t := st.NewTask("bar", "") 269 c.Check(daemon.MinLane(t), check.Equals, 0) 270 271 lane1 := st.NewLane() 272 t.JoinLane(lane1) 273 c.Check(daemon.MinLane(t), check.Equals, lane1) 274 275 lane2 := st.NewLane() 276 t.JoinLane(lane2) 277 c.Check(daemon.MinLane(t), check.Equals, lane1) 278 279 // sanity 280 c.Check(t.Lanes(), check.DeepEquals, []int{lane1, lane2}) 281 }