gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/configstate/hooks.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 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 configstate
    21  
    22  import (
    23  	"fmt"
    24  
    25  	"github.com/snapcore/snapd/overlord/configstate/config"
    26  	"github.com/snapcore/snapd/overlord/hookstate"
    27  	"github.com/snapcore/snapd/overlord/snapstate"
    28  	"github.com/snapcore/snapd/overlord/state"
    29  )
    30  
    31  // configureHandler is the handler for the configure hook.
    32  type configureHandler struct {
    33  	context *hookstate.Context
    34  }
    35  
    36  // cachedTransaction is the index into the context cache where the initialized
    37  // transaction is stored.
    38  type cachedTransaction struct{}
    39  
    40  // ContextTransaction retrieves the transaction cached within the context (and
    41  // creates one if it hasn't already been cached).
    42  func ContextTransaction(context *hookstate.Context) *config.Transaction {
    43  	// Check for one already cached
    44  	tr, ok := context.Cached(cachedTransaction{}).(*config.Transaction)
    45  	if ok {
    46  		return tr
    47  	}
    48  
    49  	// It wasn't already cached, so create and cache a new one
    50  	tr = config.NewTransaction(context.State())
    51  
    52  	context.OnDone(func() error {
    53  		tr.Commit()
    54  		if context.InstanceName() == "core" {
    55  			// make sure the Ensure logic can process
    56  			// system configuration changes as soon as possible
    57  			context.State().EnsureBefore(0)
    58  		}
    59  		return nil
    60  	})
    61  
    62  	context.Cache(cachedTransaction{}, tr)
    63  	return tr
    64  }
    65  
    66  func newConfigureHandler(context *hookstate.Context) hookstate.Handler {
    67  	return &configureHandler{context: context}
    68  }
    69  
    70  // Before is called by the HookManager before the configure hook is run.
    71  func (h *configureHandler) Before() error {
    72  	h.context.Lock()
    73  	defer h.context.Unlock()
    74  
    75  	tr := ContextTransaction(h.context)
    76  
    77  	// Initialize the transaction if there's a patch provided in the
    78  	// context or useDefaults is set in which case gadget defaults are used.
    79  
    80  	var patch map[string]interface{}
    81  	var useDefaults bool
    82  	if err := h.context.Get("use-defaults", &useDefaults); err != nil && err != state.ErrNoState {
    83  		return err
    84  	}
    85  
    86  	instanceName := h.context.InstanceName()
    87  	st := h.context.State()
    88  	if useDefaults {
    89  		task, _ := h.context.Task()
    90  		deviceCtx, err := snapstate.DeviceCtx(st, task, nil)
    91  		if err != nil {
    92  			return err
    93  		}
    94  
    95  		patch, err = snapstate.ConfigDefaults(st, deviceCtx, instanceName)
    96  		if err != nil && err != state.ErrNoState {
    97  			return err
    98  		}
    99  		// core is handled internally and does not need a configure
   100  		// hook, for other snaps double check that the hook is present
   101  		if len(patch) != 0 && instanceName != "core" {
   102  			// TODO: helper on context?
   103  			info, err := snapstate.CurrentInfo(st, instanceName)
   104  			if err != nil {
   105  				return err
   106  			}
   107  			if info.Hooks["configure"] == nil {
   108  				return fmt.Errorf("cannot apply gadget config defaults for snap %q, no configure hook", instanceName)
   109  			}
   110  		}
   111  	} else {
   112  		if err := h.context.Get("patch", &patch); err != nil && err != state.ErrNoState {
   113  			return err
   114  		}
   115  	}
   116  
   117  	if err := config.Patch(tr, instanceName, patch); err != nil {
   118  		return err
   119  	}
   120  
   121  	return nil
   122  }
   123  
   124  // Done is called by the HookManager after the configure hook has exited
   125  // successfully.
   126  func (h *configureHandler) Done() error {
   127  	return nil
   128  }
   129  
   130  // Error is called by the HookManager after the configure hook has exited
   131  // non-zero, and includes the error.
   132  func (h *configureHandler) Error(err error) (bool, error) {
   133  	return false, nil
   134  }