github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/daemon/api_model.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 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  	"encoding/json"
    24  	"net/http"
    25  
    26  	"github.com/snapcore/snapd/asserts"
    27  	"github.com/snapcore/snapd/client"
    28  	"github.com/snapcore/snapd/overlord/auth"
    29  	"github.com/snapcore/snapd/overlord/devicestate"
    30  	"github.com/snapcore/snapd/overlord/state"
    31  )
    32  
    33  var (
    34  	serialModelCmd = &Command{
    35  		Path:   "/v2/model/serial",
    36  		GET:    getSerial,
    37  		UserOK: true,
    38  	}
    39  	modelCmd = &Command{
    40  		Path:   "/v2/model",
    41  		POST:   postModel,
    42  		GET:    getModel,
    43  		UserOK: true,
    44  	}
    45  )
    46  
    47  type assertType int
    48  
    49  const (
    50  	serialType assertType = iota
    51  	modelType
    52  )
    53  
    54  var devicestateRemodel = devicestate.Remodel
    55  
    56  type postModelData struct {
    57  	NewModel string `json:"new-model"`
    58  }
    59  
    60  type modelAssertJSON struct {
    61  	Headers map[string]interface{} `json:"headers,omitempty"`
    62  	Body    string                 `json:"body,omitempty"`
    63  }
    64  
    65  func postModel(c *Command, r *http.Request, _ *auth.UserState) Response {
    66  	defer r.Body.Close()
    67  	var data postModelData
    68  	decoder := json.NewDecoder(r.Body)
    69  	if err := decoder.Decode(&data); err != nil {
    70  		return BadRequest("cannot decode request body into remodel operation: %v", err)
    71  	}
    72  	rawNewModel, err := asserts.Decode([]byte(data.NewModel))
    73  	if err != nil {
    74  		return BadRequest("cannot decode new model assertion: %v", err)
    75  	}
    76  	newModel, ok := rawNewModel.(*asserts.Model)
    77  	if !ok {
    78  		return BadRequest("new model is not a model assertion: %v", newModel.Type())
    79  	}
    80  
    81  	st := c.d.overlord.State()
    82  	st.Lock()
    83  	defer st.Unlock()
    84  
    85  	chg, err := devicestateRemodel(st, newModel)
    86  	if err != nil {
    87  		return BadRequest("cannot remodel device: %v", err)
    88  	}
    89  	ensureStateSoon(st)
    90  
    91  	return AsyncResponse(nil, &Meta{Change: chg.ID()})
    92  
    93  }
    94  
    95  // getModel gets the current model assertion using the DeviceManager
    96  func getModel(c *Command, r *http.Request, _ *auth.UserState) Response {
    97  	opts, err := parseHeadersFormatOptionsFromURL(r.URL.Query())
    98  	if err != nil {
    99  		return BadRequest(err.Error())
   100  	}
   101  
   102  	st := c.d.overlord.State()
   103  	st.Lock()
   104  	defer st.Unlock()
   105  
   106  	devmgr := c.d.overlord.DeviceManager()
   107  
   108  	model, err := devmgr.Model()
   109  	if err == state.ErrNoState {
   110  		res := &errorResult{
   111  			Message: "no model assertion yet",
   112  			Kind:    client.ErrorKindAssertionNotFound,
   113  			Value:   "model",
   114  		}
   115  
   116  		return &resp{
   117  			Type:   ResponseTypeError,
   118  			Result: res,
   119  			Status: 404,
   120  		}
   121  	}
   122  	if err != nil {
   123  		return InternalError("accessing model failed: %v", err)
   124  	}
   125  
   126  	if opts.jsonResult {
   127  		modelJSON := modelAssertJSON{}
   128  
   129  		modelJSON.Headers = model.Headers()
   130  		if !opts.headersOnly {
   131  			modelJSON.Body = string(model.Body())
   132  		}
   133  
   134  		return SyncResponse(modelJSON, nil)
   135  	}
   136  
   137  	return AssertResponse([]asserts.Assertion{model}, false)
   138  }
   139  
   140  // getSerial gets the current serial assertion using the DeviceManager
   141  func getSerial(c *Command, r *http.Request, _ *auth.UserState) Response {
   142  	opts, err := parseHeadersFormatOptionsFromURL(r.URL.Query())
   143  	if err != nil {
   144  		return BadRequest(err.Error())
   145  	}
   146  
   147  	st := c.d.overlord.State()
   148  	st.Lock()
   149  	defer st.Unlock()
   150  
   151  	devmgr := c.d.overlord.DeviceManager()
   152  
   153  	serial, err := devmgr.Serial()
   154  	if err == state.ErrNoState {
   155  		res := &errorResult{
   156  			Message: "no serial assertion yet",
   157  			Kind:    client.ErrorKindAssertionNotFound,
   158  			Value:   "serial",
   159  		}
   160  
   161  		return &resp{
   162  			Type:   ResponseTypeError,
   163  			Result: res,
   164  			Status: 404,
   165  		}
   166  	}
   167  	if err != nil {
   168  		return InternalError("accessing serial failed: %v", err)
   169  	}
   170  
   171  	if opts.jsonResult {
   172  		serialJSON := modelAssertJSON{}
   173  
   174  		serialJSON.Headers = serial.Headers()
   175  		if !opts.headersOnly {
   176  			serialJSON.Body = string(serial.Body())
   177  		}
   178  
   179  		return SyncResponse(serialJSON, nil)
   180  	}
   181  
   182  	return AssertResponse([]asserts.Assertion{serial}, false)
   183  }