github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/overlord/configstate/configcore/refresh.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  // +build !nomanagers
     3  
     4  /*
     5   * Copyright (C) 2017-2018 Canonical Ltd
     6   *
     7   * This program is free software: you can redistribute it and/or modify
     8   * it under the terms of the GNU General Public License version 3 as
     9   * published by the Free Software Foundation.
    10   *
    11   * This program is distributed in the hope that it will be useful,
    12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14   * GNU General Public License for more details.
    15   *
    16   * You should have received a copy of the GNU General Public License
    17   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18   *
    19   */
    20  
    21  package configcore
    22  
    23  import (
    24  	"fmt"
    25  	"strconv"
    26  	"time"
    27  
    28  	"github.com/snapcore/snapd/overlord/configstate/config"
    29  	"github.com/snapcore/snapd/overlord/devicestate"
    30  	"github.com/snapcore/snapd/strutil"
    31  	"github.com/snapcore/snapd/timeutil"
    32  )
    33  
    34  func init() {
    35  	supportedConfigurations["core.refresh.hold"] = true
    36  	supportedConfigurations["core.refresh.schedule"] = true
    37  	supportedConfigurations["core.refresh.timer"] = true
    38  	supportedConfigurations["core.refresh.metered"] = true
    39  	supportedConfigurations["core.refresh.retain"] = true
    40  	supportedConfigurations["core.refresh.rate-limit"] = true
    41  }
    42  
    43  func reportOrIgnoreInvalidManageRefreshes(tr config.Conf, optName string) error {
    44  	// check if the option is set as part of transaction changes; if not than
    45  	// it's already set in the config state and we shouldn't error out about it
    46  	// now. refreshScheduleManaged will do the right thing when refresh cannot
    47  	// be managed anymore.
    48  	for _, k := range tr.Changes() {
    49  		if k == "core."+optName {
    50  			return fmt.Errorf("cannot set schedule to managed")
    51  		}
    52  	}
    53  	return nil
    54  }
    55  
    56  func validateRefreshSchedule(tr config.Conf) error {
    57  	refreshRetainStr, err := coreCfg(tr, "refresh.retain")
    58  	if err != nil {
    59  		return err
    60  	}
    61  	if refreshRetainStr != "" {
    62  		if n, err := strconv.ParseUint(refreshRetainStr, 10, 8); err != nil || (n < 2 || n > 20) {
    63  			return fmt.Errorf("retain must be a number between 2 and 20, not %q", refreshRetainStr)
    64  		}
    65  	}
    66  
    67  	refreshHoldStr, err := coreCfg(tr, "refresh.hold")
    68  	if err != nil {
    69  		return err
    70  	}
    71  	if refreshHoldStr != "" {
    72  		if _, err := time.Parse(time.RFC3339, refreshHoldStr); err != nil {
    73  			return fmt.Errorf("refresh.hold cannot be parsed: %v", err)
    74  		}
    75  	}
    76  
    77  	refreshOnMeteredStr, err := coreCfg(tr, "refresh.metered")
    78  	if err != nil {
    79  		return err
    80  	}
    81  	switch refreshOnMeteredStr {
    82  	case "", "hold":
    83  		// noop
    84  	default:
    85  		return fmt.Errorf("refresh.metered value %q is invalid", refreshOnMeteredStr)
    86  	}
    87  
    88  	// check (new) refresh.timer
    89  	refreshTimerStr, err := coreCfg(tr, "refresh.timer")
    90  	if err != nil {
    91  		return err
    92  	}
    93  	if refreshTimerStr == "managed" {
    94  		st := tr.State()
    95  		st.Lock()
    96  		defer st.Unlock()
    97  
    98  		if !devicestate.CanManageRefreshes(st) {
    99  			return reportOrIgnoreInvalidManageRefreshes(tr, "refresh.timer")
   100  		}
   101  		return nil
   102  	}
   103  	if refreshTimerStr != "" {
   104  		// try legacy refresh.schedule setting if new-style
   105  		// refresh.timer is not set
   106  		if _, err = timeutil.ParseSchedule(refreshTimerStr); err != nil {
   107  			return err
   108  		}
   109  	}
   110  
   111  	// check (legacy) refresh.schedule
   112  	refreshScheduleStr, err := coreCfg(tr, "refresh.schedule")
   113  	if err != nil {
   114  		return err
   115  	}
   116  	if refreshScheduleStr == "" {
   117  		return nil
   118  	}
   119  
   120  	if refreshScheduleStr == "managed" {
   121  		st := tr.State()
   122  		st.Lock()
   123  		defer st.Unlock()
   124  
   125  		if !devicestate.CanManageRefreshes(st) {
   126  			return reportOrIgnoreInvalidManageRefreshes(tr, "refresh.schedule")
   127  		}
   128  		return nil
   129  	}
   130  
   131  	_, err = timeutil.ParseLegacySchedule(refreshScheduleStr)
   132  	return err
   133  }
   134  
   135  func validateRefreshRateLimit(tr config.Conf) error {
   136  	refreshRateLimit, err := coreCfg(tr, "refresh.rate-limit")
   137  	if err != nil {
   138  		return err
   139  	}
   140  	// reset is fine
   141  	if len(refreshRateLimit) == 0 {
   142  		return nil
   143  	}
   144  	if _, err := strutil.ParseByteSize(refreshRateLimit); err != nil {
   145  		return err
   146  	}
   147  	return nil
   148  }