github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/client/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 client
    21  
    22  import (
    23  	"bytes"
    24  	"context"
    25  	"encoding/json"
    26  	"fmt"
    27  	"net/url"
    28  
    29  	"golang.org/x/xerrors"
    30  
    31  	"github.com/snapcore/snapd/asserts"
    32  )
    33  
    34  type remodelData struct {
    35  	NewModel string `json:"new-model"`
    36  }
    37  
    38  // Remodel tries to remodel the system with the given assertion data
    39  func (client *Client) Remodel(b []byte) (changeID string, err error) {
    40  	data, err := json.Marshal(&remodelData{
    41  		NewModel: string(b),
    42  	})
    43  	if err != nil {
    44  		return "", fmt.Errorf("cannot marshal remodel data: %v", err)
    45  	}
    46  	headers := map[string]string{
    47  		"Content-Type": "application/json",
    48  	}
    49  
    50  	return client.doAsync("POST", "/v2/model", nil, headers, bytes.NewReader(data))
    51  }
    52  
    53  // CurrentModelAssertion returns the current model assertion
    54  func (client *Client) CurrentModelAssertion() (*asserts.Model, error) {
    55  	assert, err := currentAssertion(client, "/v2/model")
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	modelAssert, ok := assert.(*asserts.Model)
    60  	if !ok {
    61  		return nil, fmt.Errorf("unexpected assertion type (%s) returned", assert.Type().Name)
    62  	}
    63  	return modelAssert, nil
    64  }
    65  
    66  // CurrentSerialAssertion returns the current serial assertion
    67  func (client *Client) CurrentSerialAssertion() (*asserts.Serial, error) {
    68  	assert, err := currentAssertion(client, "/v2/model/serial")
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	serialAssert, ok := assert.(*asserts.Serial)
    73  	if !ok {
    74  		return nil, fmt.Errorf("unexpected assertion type (%s) returned", assert.Type().Name)
    75  	}
    76  	return serialAssert, nil
    77  }
    78  
    79  // helper function for getting assertions from the daemon via a REST path
    80  func currentAssertion(client *Client, path string) (asserts.Assertion, error) {
    81  	q := url.Values{}
    82  
    83  	response, cancel, err := client.rawWithTimeout(context.Background(), "GET", path, q, nil, nil, nil)
    84  	if err != nil {
    85  		fmt := "failed to query current assertion: %w"
    86  		return nil, xerrors.Errorf(fmt, err)
    87  	}
    88  	defer cancel()
    89  	defer response.Body.Close()
    90  	if response.StatusCode != 200 {
    91  		return nil, parseError(response)
    92  	}
    93  
    94  	dec := asserts.NewDecoder(response.Body)
    95  
    96  	// only decode a single assertion - we can't ever get more than a single
    97  	// assertion through these endpoints by design
    98  	assert, err := dec.Decode()
    99  	if err != nil {
   100  		return nil, fmt.Errorf("failed to decode assertions: %v", err)
   101  	}
   102  
   103  	return assert, nil
   104  }