github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/devicestate/handlers.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  /*
     3   * Copyright (C) 2016-2020 Canonical Ltd
     4   *
     5   * This program is free software: you can redistribute it and/or modify
     6   * it under the terms of the GNU General Public License version 3 as
     7   * published by the Free Software Foundation.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   *
    17   */
    18  
    19  package devicestate
    20  
    21  import (
    22  	"fmt"
    23  	"os/exec"
    24  	"time"
    25  
    26  	"gopkg.in/tomb.v2"
    27  
    28  	"github.com/snapcore/snapd/interfaces"
    29  	"github.com/snapcore/snapd/logger"
    30  	"github.com/snapcore/snapd/overlord/snapstate"
    31  	"github.com/snapcore/snapd/overlord/state"
    32  	"github.com/snapcore/snapd/progress"
    33  	"github.com/snapcore/snapd/wrappers"
    34  )
    35  
    36  func (m *DeviceManager) doMarkPreseeded(t *state.Task, _ *tomb.Tomb) error {
    37  	st := t.State()
    38  	st.Lock()
    39  	defer st.Unlock()
    40  
    41  	snaps, err := snapstate.All(st)
    42  	if err != nil {
    43  		return err
    44  	}
    45  
    46  	systemKey, err := interfaces.RecordedSystemKey()
    47  	if err != nil {
    48  		return fmt.Errorf("cannot get recorded system key: %v", err)
    49  	}
    50  
    51  	if m.preseed {
    52  		var preseeded bool
    53  		// the "preseeded" flag on this task is set to allow skipping the logic
    54  		// below in case this handler is retried in preseeding mode due to an
    55  		// EnsureBefore(0) done somewhere else.
    56  		// XXX: we should probably drop the flag from the task now that we have
    57  		// one on the state.
    58  		if err := t.Get("preseeded", &preseeded); err != nil && err != state.ErrNoState {
    59  			return err
    60  		}
    61  		if !preseeded {
    62  			preseeded = true
    63  			t.Set("preseeded", preseeded)
    64  			// unmount all snaps
    65  			// TODO: move to snapstate.UnmountAllSnaps.
    66  			for _, snapSt := range snaps {
    67  				info, err := snapSt.CurrentInfo()
    68  				if err != nil {
    69  					return err
    70  				}
    71  				logger.Debugf("unmounting snap %s at %s", info.InstanceName(), info.MountDir())
    72  				if _, err := exec.Command("umount", "-d", "-l", info.MountDir()).CombinedOutput(); err != nil {
    73  					return err
    74  				}
    75  			}
    76  
    77  			st.Set("preseeded", preseeded)
    78  			st.Set("preseed-system-key", systemKey)
    79  			st.Set("preseed-time", timeNow())
    80  
    81  			// do not mark this task done as this makes it racy against taskrunner tear down (the next task
    82  			// could start). Let this task finish after snapd restart when preseed mode is off.
    83  			st.RequestRestart(state.StopDaemon)
    84  		}
    85  
    86  		return &state.Retry{Reason: "mark-preseeded will be marked done when snapd is executed in normal mode"}
    87  	}
    88  
    89  	// normal snapd run after snapd restart (not in preseed mode anymore)
    90  
    91  	st.Set("seed-restart-system-key", systemKey)
    92  	if err := m.setTimeOnce("seed-restart-time", startTime); err != nil {
    93  		return err
    94  	}
    95  
    96  	// enable all services generated as part of preseeding, but not enabled
    97  	// XXX: this should go away once the problem of install & services is fixed.
    98  	for _, snapSt := range snaps {
    99  		info, err := snapSt.CurrentInfo()
   100  		if err != nil {
   101  			return err
   102  		}
   103  		if err := wrappers.EnableSnapServices(info, progress.Null); err != nil {
   104  			return err
   105  		}
   106  	}
   107  
   108  	return nil
   109  }
   110  
   111  type seededSystem struct {
   112  	// System carries the recovery system label that was used to seed the
   113  	// current system
   114  	System string `json:"system"`
   115  	Model  string `json:"model"`
   116  	// BrandID is the brand account ID
   117  	BrandID string `json:"brand-id"`
   118  	// Revision of the model assertion
   119  	Revision int `json:"revision"`
   120  	// Timestamp of model assertion
   121  	Timestamp time.Time `json:"timestamp"`
   122  	// SeedTime holds the timestamp when the system was seeded
   123  	SeedTime time.Time `json:"seed-time"`
   124  }
   125  
   126  func (m *DeviceManager) recordSeededSystem(st *state.State, whatSeeded *seededSystem) error {
   127  	var seeded []seededSystem
   128  	if err := st.Get("seeded-systems", &seeded); err != nil && err != state.ErrNoState {
   129  		return err
   130  	}
   131  	seeded = append([]seededSystem{*whatSeeded}, seeded...)
   132  	st.Set("seeded-systems", seeded)
   133  	return nil
   134  }
   135  
   136  func (m *DeviceManager) doMarkSeeded(t *state.Task, _ *tomb.Tomb) error {
   137  	st := t.State()
   138  	st.Lock()
   139  	defer st.Unlock()
   140  
   141  	if m.preseed {
   142  		return fmt.Errorf("internal error: mark-seeded task not expected in pre-seeding mode")
   143  	}
   144  
   145  	deviceCtx, err := DeviceCtx(st, t, nil)
   146  	if err != nil {
   147  		return fmt.Errorf("cannot get device context: %v", err)
   148  	}
   149  
   150  	if deviceCtx.HasModeenv() && deviceCtx.RunMode() {
   151  		modeEnv, err := maybeReadModeenv()
   152  		if err != nil {
   153  			return err
   154  		}
   155  		if modeEnv == nil {
   156  			return fmt.Errorf("missing modeenv, cannot proceed")
   157  		}
   158  		// unset recovery_system because that is only needed during install mode
   159  		modeEnv.RecoverySystem = ""
   160  		err = modeEnv.Write()
   161  		if err != nil {
   162  			return err
   163  		}
   164  	}
   165  
   166  	now := time.Now()
   167  	var whatSeeded *seededSystem
   168  	if err := t.Get("seed-system", &whatSeeded); err != nil && err != state.ErrNoState {
   169  		return err
   170  	}
   171  	if whatSeeded != nil && deviceCtx.RunMode() {
   172  		// record what seeded in the state only when in run mode
   173  
   174  		whatSeeded.SeedTime = now
   175  		// TODO:UC20 what about remodels?
   176  		if err := m.recordSeededSystem(st, whatSeeded); err != nil {
   177  			return fmt.Errorf("cannot record the seeded system: %v", err)
   178  		}
   179  	}
   180  	st.Set("seed-time", now)
   181  	st.Set("seeded", true)
   182  	// avoid possibly recording the same system multiple times etc.
   183  	t.SetStatus(state.DoneStatus)
   184  	// make sure we setup a fallback model/consider the next phase
   185  	// (registration) timely
   186  	st.EnsureBefore(0)
   187  	return nil
   188  }