github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/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
    21  
    22  import (
    23  	"bytes"
    24  	"encoding/json"
    25  	"net/http"
    26  
    27  	"gopkg.in/check.v1"
    28  
    29  	"github.com/snapcore/snapd/overlord/state"
    30  	"github.com/snapcore/snapd/testutil"
    31  	"github.com/snapcore/snapd/timings"
    32  )
    33  
    34  var _ = check.Suite(&postDebugSuite{})
    35  
    36  type postDebugSuite struct {
    37  	APIBaseSuite
    38  }
    39  
    40  func (s *postDebugSuite) TestPostDebugEnsureStateSoon(c *check.C) {
    41  	s.daemonWithOverlordMock(c)
    42  
    43  	soon := 0
    44  	ensureStateSoon = func(st *state.State) {
    45  		soon++
    46  		ensureStateSoonImpl(st)
    47  	}
    48  
    49  	buf := bytes.NewBufferString(`{"action": "ensure-state-soon"}`)
    50  	req, err := http.NewRequest("POST", "/v2/debug", buf)
    51  	c.Assert(err, check.IsNil)
    52  
    53  	rsp := postDebug(debugCmd, req, nil).(*resp)
    54  
    55  	c.Check(rsp.Type, check.Equals, ResponseTypeSync)
    56  	c.Check(rsp.Result, check.Equals, true)
    57  	c.Check(soon, check.Equals, 1)
    58  }
    59  
    60  func (s *postDebugSuite) TestPostDebugGetBaseDeclaration(c *check.C) {
    61  	_ = s.daemon(c)
    62  
    63  	buf := bytes.NewBufferString(`{"action": "get-base-declaration"}`)
    64  	req, err := http.NewRequest("POST", "/v2/debug", buf)
    65  	c.Assert(err, check.IsNil)
    66  
    67  	rsp := postDebug(debugCmd, req, nil).(*resp)
    68  
    69  	c.Check(rsp.Type, check.Equals, ResponseTypeSync)
    70  	c.Check(rsp.Result.(map[string]interface{})["base-declaration"],
    71  		testutil.Contains, "type: base-declaration")
    72  }
    73  
    74  func (s *postDebugSuite) testDebugConnectivityHappy(c *check.C, post bool) {
    75  	_ = s.daemon(c)
    76  
    77  	s.connectivityResult = map[string]bool{
    78  		"good.host.com":         true,
    79  		"another.good.host.com": true,
    80  	}
    81  
    82  	var rsp *resp
    83  	if post {
    84  		buf := bytes.NewBufferString(`{"action": "connectivity"}`)
    85  		req, err := http.NewRequest("POST", "/v2/debug", buf)
    86  		c.Assert(err, check.IsNil)
    87  
    88  		rsp = postDebug(debugCmd, req, nil).(*resp)
    89  	} else {
    90  		req, err := http.NewRequest("POST", "/v2/debug?aspect=connectivity", nil)
    91  		c.Assert(err, check.IsNil)
    92  		rsp = getDebug(debugCmd, req, nil).(*resp)
    93  
    94  	}
    95  
    96  	c.Check(rsp.Type, check.Equals, ResponseTypeSync)
    97  	c.Check(rsp.Result, check.DeepEquals, ConnectivityStatus{
    98  		Connectivity: true,
    99  		Unreachable:  []string(nil),
   100  	})
   101  }
   102  
   103  func (s *postDebugSuite) TestPostDebugConnectivityHappy(c *check.C) {
   104  	s.testDebugConnectivityHappy(c, true)
   105  }
   106  
   107  func (s *postDebugSuite) TestGetDebugConnectivityHappy(c *check.C) {
   108  	s.testDebugConnectivityHappy(c, false)
   109  }
   110  
   111  func (s *postDebugSuite) testDebugConnectivityUnhappy(c *check.C, post bool) {
   112  	_ = s.daemon(c)
   113  
   114  	s.connectivityResult = map[string]bool{
   115  		"good.host.com": true,
   116  		"bad.host.com":  false,
   117  	}
   118  
   119  	var rsp *resp
   120  	if post {
   121  		buf := bytes.NewBufferString(`{"action": "connectivity"}`)
   122  		req, err := http.NewRequest("POST", "/v2/debug", buf)
   123  		c.Assert(err, check.IsNil)
   124  
   125  		rsp = postDebug(debugCmd, req, nil).(*resp)
   126  	} else {
   127  		req, err := http.NewRequest("GET", "/v2/debug?aspect=connectivity", nil)
   128  		c.Assert(err, check.IsNil)
   129  		rsp = getDebug(debugCmd, req, nil).(*resp)
   130  	}
   131  
   132  	c.Check(rsp.Type, check.Equals, ResponseTypeSync)
   133  	c.Check(rsp.Result, check.DeepEquals, ConnectivityStatus{
   134  		Connectivity: false,
   135  		Unreachable:  []string{"bad.host.com"},
   136  	})
   137  }
   138  
   139  func (s *postDebugSuite) TestPostDebugConnectivityUnhappy(c *check.C) {
   140  	s.testDebugConnectivityUnhappy(c, true)
   141  }
   142  
   143  func (s *postDebugSuite) TestGetDebugConnectivityUnhappy(c *check.C) {
   144  	s.testDebugConnectivityUnhappy(c, false)
   145  }
   146  
   147  func (s *postDebugSuite) TestGetDebugBaseDeclaration(c *check.C) {
   148  	_ = s.daemon(c)
   149  
   150  	req, err := http.NewRequest("GET", "/v2/debug?aspect=base-declaration", nil)
   151  	c.Assert(err, check.IsNil)
   152  
   153  	rsp := getDebug(debugCmd, req, nil).(*resp)
   154  
   155  	c.Check(rsp.Type, check.Equals, ResponseTypeSync)
   156  	c.Check(rsp.Result.(map[string]interface{})["base-declaration"],
   157  		testutil.Contains, "type: base-declaration")
   158  }
   159  
   160  func mockDurationThreshold() func() {
   161  	oldDurationThreshold := timings.DurationThreshold
   162  	restore := func() {
   163  		timings.DurationThreshold = oldDurationThreshold
   164  	}
   165  	timings.DurationThreshold = 0
   166  	return restore
   167  }
   168  
   169  func (s *postDebugSuite) getDebugTimings(c *check.C, request string) []interface{} {
   170  	defer mockDurationThreshold()()
   171  
   172  	s.daemonWithOverlordMock(c)
   173  
   174  	req, err := http.NewRequest("GET", request, nil)
   175  	c.Assert(err, check.IsNil)
   176  
   177  	st := s.d.overlord.State()
   178  	st.Lock()
   179  
   180  	chg1 := st.NewChange("foo", "...")
   181  	task1 := st.NewTask("bar", "...")
   182  	chg1.AddTask(task1)
   183  	task1.SetStatus(state.DoingStatus)
   184  
   185  	chg2 := st.NewChange("foo", "...")
   186  	task2 := st.NewTask("bar", "...")
   187  	chg2.AddTask(task2)
   188  
   189  	chg3 := st.NewChange("foo", "...")
   190  	task3 := st.NewTask("bar", "...")
   191  	chg3.AddTask(task3)
   192  
   193  	tm1 := state.TimingsForTask(task3)
   194  	sp1 := tm1.StartSpan("span", "span...")
   195  	sp1.Stop()
   196  	tm1.Save(st)
   197  
   198  	tm2 := timings.New(map[string]string{"ensure": "foo", "change-id": chg1.ID()})
   199  	sp2 := tm2.StartSpan("span", "span...")
   200  	sp2.Stop()
   201  	tm2.Save(st)
   202  
   203  	tm3 := timings.New(map[string]string{"ensure": "foo", "change-id": chg2.ID()})
   204  	sp3 := tm3.StartSpan("span", "span...")
   205  	sp3.Stop()
   206  	tm3.Save(st)
   207  
   208  	tm4 := timings.New(map[string]string{"ensure": "bar", "change-id": chg3.ID()})
   209  	sp4 := tm3.StartSpan("span", "span...")
   210  	sp4.Stop()
   211  	tm4.Save(st)
   212  
   213  	st.Unlock()
   214  
   215  	rsp := getDebug(debugCmd, req, nil).(*resp)
   216  	data, err := json.Marshal(rsp.Result)
   217  	c.Assert(err, check.IsNil)
   218  	var dataJSON []interface{}
   219  	json.Unmarshal(data, &dataJSON)
   220  
   221  	c.Assert(rsp.Type, check.Equals, ResponseTypeSync)
   222  	return dataJSON
   223  }
   224  
   225  func (s *postDebugSuite) TestGetDebugTimingsSingleChange(c *check.C) {
   226  	dataJSON := s.getDebugTimings(c, "/v2/debug?aspect=change-timings&change-id=1")
   227  
   228  	c.Check(dataJSON, check.HasLen, 1)
   229  	tmData := dataJSON[0].(map[string]interface{})
   230  	c.Check(tmData["change-id"], check.DeepEquals, "1")
   231  	c.Check(tmData["change-timings"], check.NotNil)
   232  }
   233  
   234  func (s *postDebugSuite) TestGetDebugTimingsEnsureLatest(c *check.C) {
   235  	dataJSON := s.getDebugTimings(c, "/v2/debug?aspect=change-timings&ensure=foo&all=false")
   236  	c.Assert(dataJSON, check.HasLen, 1)
   237  
   238  	tmData := dataJSON[0].(map[string]interface{})
   239  	c.Check(tmData["change-id"], check.DeepEquals, "2")
   240  	c.Check(tmData["change-timings"], check.NotNil)
   241  	c.Check(tmData["total-duration"], check.NotNil)
   242  }
   243  
   244  func (s *postDebugSuite) TestGetDebugTimingsEnsureAll(c *check.C) {
   245  	dataJSON := s.getDebugTimings(c, "/v2/debug?aspect=change-timings&ensure=foo&all=true")
   246  
   247  	c.Assert(dataJSON, check.HasLen, 2)
   248  	tmData := dataJSON[0].(map[string]interface{})
   249  	c.Check(tmData["change-id"], check.DeepEquals, "1")
   250  	c.Check(tmData["change-timings"], check.NotNil)
   251  	c.Check(tmData["total-duration"], check.NotNil)
   252  
   253  	tmData = dataJSON[1].(map[string]interface{})
   254  	c.Check(tmData["change-id"], check.DeepEquals, "2")
   255  	c.Check(tmData["change-timings"], check.NotNil)
   256  	c.Check(tmData["total-duration"], check.NotNil)
   257  }
   258  
   259  func (s *postDebugSuite) TestGetDebugTimingsError(c *check.C) {
   260  	s.daemonWithOverlordMock(c)
   261  
   262  	req, err := http.NewRequest("GET", "/v2/debug?aspect=change-timings&ensure=unknown", nil)
   263  	c.Assert(err, check.IsNil)
   264  	rsp := getDebug(debugCmd, req, nil).(*resp)
   265  	c.Check(rsp.Status, check.Equals, 400)
   266  
   267  	req, err = http.NewRequest("GET", "/v2/debug?aspect=change-timings&change-id=9999", nil)
   268  	c.Assert(err, check.IsNil)
   269  	rsp = getDebug(debugCmd, req, nil).(*resp)
   270  	c.Check(rsp.Status, check.Equals, 400)
   271  }
   272  
   273  func (s *postDebugSuite) TestMinLane(c *check.C) {
   274  	st := state.New(nil)
   275  	st.Lock()
   276  	defer st.Unlock()
   277  
   278  	t := st.NewTask("bar", "")
   279  	c.Check(MinLane(t), check.Equals, 0)
   280  
   281  	lane1 := st.NewLane()
   282  	t.JoinLane(lane1)
   283  	c.Check(MinLane(t), check.Equals, lane1)
   284  
   285  	lane2 := st.NewLane()
   286  	t.JoinLane(lane2)
   287  	c.Check(MinLane(t), check.Equals, lane1)
   288  
   289  	// sanity
   290  	c.Check(t.Lanes(), check.DeepEquals, []int{lane1, lane2})
   291  }