github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/service/snap/app.go (about)

     1  // Copyright 2021 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package snap
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  )
    11  
    12  // ConfinementPolicy describes the confinement policy for installing a given
    13  // snap application.
    14  type ConfinementPolicy string
    15  
    16  const (
    17  	// StrictPolicy confined snaps run in complete isolation, up to a minimal
    18  	// access level that’s deemed always safe.
    19  	StrictPolicy ConfinementPolicy = "strict"
    20  	// ClassicPolicy allows access to your system’s resources in much the same
    21  	// way traditional packages do
    22  	ClassicPolicy ConfinementPolicy = "classic"
    23  	// DevModePolicy is a special mode for snap creators and developers.
    24  	// A devmode snap runs as a strictly confined snap with full access to
    25  	// system resources, and produces debug output to identify unspecified
    26  	// interfaces.
    27  	DevModePolicy ConfinementPolicy = "devmode"
    28  	// JailModePolicy enforces the confinement model for a snap to ensure that
    29  	// the confinement is enforced.
    30  	JailModePolicy ConfinementPolicy = "jailmode"
    31  )
    32  
    33  // Validate validates a given confinement policy to ensure it matches the ones we
    34  // expect.
    35  func (p ConfinementPolicy) Validate() error {
    36  	switch p {
    37  	case StrictPolicy, ClassicPolicy, DevModePolicy, JailModePolicy:
    38  		return nil
    39  	}
    40  	return errors.NotValidf("%s confinement", p)
    41  }
    42  
    43  // String representation of the confinement policy.
    44  func (p ConfinementPolicy) String() string {
    45  	return string(p)
    46  }
    47  
    48  // App is a wrapper around a single snap
    49  type App struct {
    50  	name               string
    51  	confinementPolicy  ConfinementPolicy
    52  	channel            string
    53  	backgroundServices []BackgroundService
    54  	prerequisites      []Installable
    55  }
    56  
    57  // NewNamedApp creates a new application from a given name.
    58  func NewNamedApp(name string) *App {
    59  	return &App{
    60  		name: name,
    61  	}
    62  }
    63  
    64  // NewApp creates a application along with it's dependencies.
    65  func NewApp(name string, channel string, policy ConfinementPolicy, services []BackgroundService, prerequisites []Installable) *App {
    66  	return &App{
    67  		name:               name,
    68  		channel:            channel,
    69  		confinementPolicy:  policy,
    70  		backgroundServices: services,
    71  		prerequisites:      prerequisites,
    72  	}
    73  }
    74  
    75  // Validate will validate a given application for any potential issues.
    76  func (a *App) Validate() error {
    77  	if !snapNameRe.MatchString(a.name) {
    78  		return errors.NotValidf("application %v", a.name)
    79  	}
    80  
    81  	if a.confinementPolicy != "" {
    82  		if err := a.confinementPolicy.Validate(); err != nil {
    83  			return errors.Trace(err)
    84  		}
    85  	}
    86  
    87  	for _, backgroundService := range a.backgroundServices {
    88  		err := backgroundService.Validate()
    89  		if err != nil {
    90  			return errors.Trace(err)
    91  		}
    92  	}
    93  
    94  	for _, prerequisite := range a.prerequisites {
    95  		err := prerequisite.Validate()
    96  		if err != nil {
    97  			return errors.Trace(err)
    98  		}
    99  	}
   100  
   101  	return nil
   102  }
   103  
   104  // StartCommands returns a list if shell commands that should be executed (in order)
   105  // to start App and its background services. executeable is a path to the snap
   106  // executable. If the app has prerequisite applications defined, then take care to call
   107  // StartCommands on those apps also.
   108  func (a *App) StartCommands(executable string) []string {
   109  	if len(a.backgroundServices) == 0 {
   110  		return []string{fmt.Sprintf("%s start %s", executable, a.name)}
   111  	}
   112  
   113  	commands := make([]string, 0, len(a.backgroundServices))
   114  	for _, backgroundService := range a.backgroundServices {
   115  		enableFlag := ""
   116  		if backgroundService.EnableAtStartup {
   117  			enableFlag = " --enable "
   118  		}
   119  
   120  		command := fmt.Sprintf("%s start %s %s.%s", executable, enableFlag, a.name, backgroundService.Name)
   121  		commands = append(commands, command)
   122  	}
   123  	return commands
   124  }
   125  
   126  // Install returns a way to install one application with all it's settings.
   127  func (a *App) Install() []string {
   128  	args := []string{
   129  		"install",
   130  	}
   131  	if a.channel != "" {
   132  		args = append(args, fmt.Sprintf("--channel=%s", a.channel))
   133  	}
   134  	if a.confinementPolicy != "" {
   135  		args = append(args, fmt.Sprintf("--%s", a.confinementPolicy))
   136  	}
   137  	return append(args, a.name)
   138  }
   139  
   140  // Prerequisites defines a list of all the Prerequisites required before the
   141  // application also needs to be installed.
   142  func (a *App) Prerequisites() []Installable {
   143  	return a.prerequisites
   144  }
   145  
   146  // BackgroundServices returns a list of background services that are
   147  // required to be installed for the main application to run.
   148  func (a *App) BackgroundServices() []BackgroundService {
   149  	return a.backgroundServices
   150  }
   151  
   152  // Name returns the name of the application
   153  func (a *App) Name() string {
   154  	return a.name
   155  }