github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/overlord/snapstate/dbus_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2020 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 snapstate_test
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  
    26  	. "gopkg.in/check.v1"
    27  
    28  	"github.com/snapcore/snapd/overlord/configstate/config"
    29  	"github.com/snapcore/snapd/overlord/snapstate"
    30  	"github.com/snapcore/snapd/snap"
    31  )
    32  
    33  const (
    34  	dbusSessionYamlTemplate = `name: %s
    35  slots:
    36    dbus-slot:
    37      interface: dbus
    38      bus: session
    39      name: org.example.Foo
    40  apps:
    41    daemon:
    42      daemon: simple
    43      daemon-scope: user
    44      activates-on: [dbus-slot]
    45  `
    46  	dbusSystemYamlTemplate = `name: %s
    47  slots:
    48    dbus-slot:
    49      interface: dbus
    50      bus: system
    51      name: org.example.Foo
    52  apps:
    53    daemon:
    54      daemon: simple
    55      activates-on: [dbus-slot]
    56  `
    57  )
    58  
    59  func (s *snapmgrTestSuite) TestCheckDBusServiceConflictsSystem(c *C) {
    60  	someSnap, err := snap.InfoFromSnapYaml([]byte(fmt.Sprintf(dbusSystemYamlTemplate, "some-snap")))
    61  	c.Assert(err, IsNil)
    62  	otherSnap, err := snap.InfoFromSnapYaml([]byte(fmt.Sprintf(dbusSystemYamlTemplate, "other-snap")))
    63  	c.Assert(err, IsNil)
    64  
    65  	restore := snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) {
    66  		switch name {
    67  		case "some-snap":
    68  			return someSnap, nil
    69  		case "other-snap":
    70  			return otherSnap, nil
    71  		default:
    72  			return s.fakeBackend.ReadInfo(name, si)
    73  		}
    74  	})
    75  	defer restore()
    76  
    77  	s.state.Lock()
    78  	defer s.state.Unlock()
    79  	si := &snap.SideInfo{
    80  		RealName: "other-snap",
    81  		Revision: snap.R(-42),
    82  	}
    83  	snapstate.Set(s.state, "other-snap", &snapstate.SnapState{
    84  		Active:   true,
    85  		Sequence: []*snap.SideInfo{si},
    86  		Current:  si.Revision,
    87  		SnapType: "app",
    88  	})
    89  
    90  	err = snapstate.CheckDBusServiceConflicts(s.state, someSnap)
    91  	c.Assert(err, ErrorMatches, `snap "some-snap" requesting to activate on system bus name "org.example.Foo" conflicts with snap "other-snap" use`)
    92  }
    93  
    94  func (s *snapmgrTestSuite) TestCheckDBusServiceConflictsSession(c *C) {
    95  	someSnap, err := snap.InfoFromSnapYaml([]byte(fmt.Sprintf(dbusSessionYamlTemplate, "some-snap")))
    96  	c.Assert(err, IsNil)
    97  	otherSnap, err := snap.InfoFromSnapYaml([]byte(fmt.Sprintf(dbusSessionYamlTemplate, "other-snap")))
    98  	c.Assert(err, IsNil)
    99  
   100  	restore := snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) {
   101  		switch name {
   102  		case "some-snap":
   103  			return someSnap, nil
   104  		case "other-snap":
   105  			return otherSnap, nil
   106  		default:
   107  			return s.fakeBackend.ReadInfo(name, si)
   108  		}
   109  	})
   110  	defer restore()
   111  
   112  	s.state.Lock()
   113  	defer s.state.Unlock()
   114  	si := &snap.SideInfo{
   115  		RealName: "other-snap",
   116  		Revision: snap.R(-42),
   117  	}
   118  	snapstate.Set(s.state, "other-snap", &snapstate.SnapState{
   119  		Active:   true,
   120  		Sequence: []*snap.SideInfo{si},
   121  		Current:  si.Revision,
   122  		SnapType: "app",
   123  	})
   124  
   125  	err = snapstate.CheckDBusServiceConflicts(s.state, someSnap)
   126  	c.Assert(err, ErrorMatches, `snap "some-snap" requesting to activate on session bus name "org.example.Foo" conflicts with snap "other-snap" use`)
   127  }
   128  
   129  func (s *snapmgrTestSuite) TestCheckDBusServiceConflictsDifferentBuses(c *C) {
   130  	sessionSnap, err := snap.InfoFromSnapYaml([]byte(fmt.Sprintf(dbusSessionYamlTemplate, "session-snap")))
   131  	c.Assert(err, IsNil)
   132  	systemSnap, err := snap.InfoFromSnapYaml([]byte(fmt.Sprintf(dbusSystemYamlTemplate, "system-snap")))
   133  	c.Assert(err, IsNil)
   134  
   135  	restore := snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) {
   136  		switch name {
   137  		case "session-snap":
   138  			return sessionSnap, nil
   139  		case "system-snap":
   140  			return systemSnap, nil
   141  		default:
   142  			return s.fakeBackend.ReadInfo(name, si)
   143  		}
   144  	})
   145  	defer restore()
   146  
   147  	s.state.Lock()
   148  	defer s.state.Unlock()
   149  
   150  	// A snap claiming a name on the system bus does not conflict
   151  	// with a snap providing the same name on the session bus.
   152  	si := &snap.SideInfo{
   153  		RealName: "system-snap",
   154  		Revision: snap.R(-42),
   155  	}
   156  	snapstate.Set(s.state, "system-snap", &snapstate.SnapState{
   157  		Active:   true,
   158  		Sequence: []*snap.SideInfo{si},
   159  		Current:  si.Revision,
   160  		SnapType: "app",
   161  	})
   162  	err = snapstate.CheckDBusServiceConflicts(s.state, sessionSnap)
   163  	c.Check(err, IsNil)
   164  
   165  	// ... and the reverse
   166  	snapstate.Set(s.state, "system-snap", nil)
   167  	si = &snap.SideInfo{
   168  		RealName: "session-snap",
   169  		Revision: snap.R(-42),
   170  	}
   171  	snapstate.Set(s.state, "session-snap", &snapstate.SnapState{
   172  		Active:   true,
   173  		Sequence: []*snap.SideInfo{si},
   174  		Current:  si.Revision,
   175  		SnapType: "app",
   176  	})
   177  	err = snapstate.CheckDBusServiceConflicts(s.state, systemSnap)
   178  	c.Check(err, IsNil)
   179  }
   180  
   181  func (s *snapmgrTestSuite) TestCheckDBusServiceConflictsNoConflictWithSelf(c *C) {
   182  	info, err := snap.InfoFromSnapYaml([]byte(fmt.Sprintf(dbusSessionYamlTemplate, "some-snap")))
   183  	c.Assert(err, IsNil)
   184  	restore := snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) {
   185  		switch name {
   186  		case "some-snap":
   187  			return info, nil
   188  		default:
   189  			return s.fakeBackend.ReadInfo(name, si)
   190  		}
   191  	})
   192  	defer restore()
   193  
   194  	s.state.Lock()
   195  	defer s.state.Unlock()
   196  
   197  	// No conflicts on first installation
   198  	err = snapstate.CheckDBusServiceConflicts(s.state, info)
   199  	c.Assert(err, IsNil)
   200  
   201  	// Snap does not conflict against itself
   202  	si := &snap.SideInfo{
   203  		RealName: "some-snap",
   204  		Revision: snap.R(-42),
   205  	}
   206  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   207  		Active:   true,
   208  		Sequence: []*snap.SideInfo{si},
   209  		Current:  si.Revision,
   210  		SnapType: "app",
   211  	})
   212  	err = snapstate.CheckDBusServiceConflicts(s.state, info)
   213  	c.Assert(err, IsNil)
   214  }
   215  
   216  func (s *snapmgrTestSuite) TestInstallDBusActivationConflicts(c *C) {
   217  	someSnap, err := snap.InfoFromSnapYaml([]byte(fmt.Sprintf(dbusSystemYamlTemplate, "some-snap")))
   218  	c.Assert(err, IsNil)
   219  	otherSnap, err := snap.InfoFromSnapYaml([]byte(fmt.Sprintf(dbusSystemYamlTemplate, "other-snap")))
   220  	c.Assert(err, IsNil)
   221  
   222  	restore := snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) {
   223  		switch name {
   224  		case "some-snap":
   225  			return someSnap, nil
   226  		case "other-snap":
   227  			return otherSnap, nil
   228  		default:
   229  			return s.fakeBackend.ReadInfo(name, si)
   230  		}
   231  	})
   232  	defer restore()
   233  
   234  	s.state.Lock()
   235  	defer s.state.Unlock()
   236  
   237  	si := &snap.SideInfo{
   238  		RealName: "other-snap",
   239  		Revision: snap.R(-42),
   240  	}
   241  	snapstate.Set(s.state, "other-snap", &snapstate.SnapState{
   242  		Active:   true,
   243  		Sequence: []*snap.SideInfo{si},
   244  		Current:  si.Revision,
   245  		SnapType: "app",
   246  	})
   247  
   248  	tr := config.NewTransaction(s.state)
   249  	tr.Set("core", "experimental.dbus-activation", true)
   250  	tr.Commit()
   251  
   252  	opts := &snapstate.RevisionOptions{Channel: "channel-for-dbus-activation"}
   253  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
   254  	c.Check(err, ErrorMatches, `snap "some-snap" requesting to activate on system bus name "org.example.Foo" conflicts with snap "other-snap" use`)
   255  }
   256  
   257  func (s *snapmgrTestSuite) TestInstallManyDBusActivationConflicts(c *C) {
   258  	someSnap, err := snap.InfoFromSnapYaml([]byte(fmt.Sprintf(dbusSystemYamlTemplate, "some-snap")))
   259  	c.Assert(err, IsNil)
   260  	otherSnap, err := snap.InfoFromSnapYaml([]byte(fmt.Sprintf(dbusSystemYamlTemplate, "other-snap")))
   261  	c.Assert(err, IsNil)
   262  
   263  	restore := snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) {
   264  		switch name {
   265  		case "some-snap":
   266  			return someSnap, nil
   267  		case "other-snap":
   268  			return otherSnap, nil
   269  		default:
   270  			return s.fakeBackend.ReadInfo(name, si)
   271  		}
   272  	})
   273  	defer restore()
   274  
   275  	s.state.Lock()
   276  	defer s.state.Unlock()
   277  
   278  	tr := config.NewTransaction(s.state)
   279  	tr.Set("core", "experimental.dbus-activation", true)
   280  	tr.Commit()
   281  
   282  	snapNames := []string{"some-snap", "other-snap"}
   283  	_, tss, err := snapstate.InstallMany(s.state, snapNames, s.user.ID)
   284  	c.Assert(err, IsNil)
   285  
   286  	chg := s.state.NewChange("install", "install two snaps")
   287  	for _, ts := range tss {
   288  		chg.AddAll(ts)
   289  	}
   290  
   291  	s.state.Unlock()
   292  	s.settle(c)
   293  	s.state.Lock()
   294  
   295  	// The order of installation is indeterminant, but one will fail
   296  	c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n- Make snap "(some|other)-snap" \(11\) available to the system \(snap "(some|other)-snap" requesting to activate on system bus name "org.example.Foo" conflicts with snap "(some|other)-snap" use\)`)
   297  }