github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/container/lxd/image_test.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package lxd_test
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"time"
    10  
    11  	lxdclient "github.com/canonical/lxd/client"
    12  	lxdapi "github.com/canonical/lxd/shared/api"
    13  	jc "github.com/juju/testing/checkers"
    14  	"go.uber.org/mock/gomock"
    15  	gc "gopkg.in/check.v1"
    16  
    17  	"github.com/juju/juju/container/lxd"
    18  	"github.com/juju/juju/container/lxd/mocks"
    19  	lxdtesting "github.com/juju/juju/container/lxd/testing"
    20  	corebase "github.com/juju/juju/core/base"
    21  )
    22  
    23  var _ = gc.Suite(&imageSuite{})
    24  
    25  type imageSuite struct {
    26  	lxdtesting.BaseSuite
    27  }
    28  
    29  func (s *imageSuite) patch(remotes map[string]lxdclient.ImageServer) {
    30  	lxd.PatchConnectRemote(s, remotes)
    31  }
    32  
    33  func (s *imageSuite) TestCopyImageUsesPassedCallback(c *gc.C) {
    34  	ctrl := gomock.NewController(c)
    35  	defer ctrl.Finish()
    36  	iSvr := s.NewMockServer(ctrl)
    37  
    38  	copyOp := lxdtesting.NewMockRemoteOperation(ctrl)
    39  	copyOp.EXPECT().Wait().Return(nil).AnyTimes()
    40  	copyOp.EXPECT().GetTarget().Return(&lxdapi.Operation{StatusCode: lxdapi.Success}, nil)
    41  	copyOp.EXPECT().AddHandler(gomock.Any()).Return(nil, nil)
    42  
    43  	image := lxdapi.Image{Filename: "this-is-our-image"}
    44  	aliases := []lxdapi.ImageAlias{{Name: "local/image/alias"}}
    45  	req := &lxdclient.ImageCopyArgs{Aliases: aliases}
    46  	iSvr.EXPECT().CopyImage(iSvr, image, req).Return(copyOp, nil)
    47  
    48  	jujuSvr, err := lxd.NewServer(iSvr)
    49  	c.Assert(err, jc.ErrorIsNil)
    50  
    51  	sourced := lxd.SourcedImage{
    52  		Image:     &image,
    53  		LXDServer: iSvr,
    54  	}
    55  	err = jujuSvr.CopyRemoteImage(context.Background(), sourced, []string{"local/image/alias"}, lxdtesting.NoOpCallback)
    56  	c.Assert(err, jc.ErrorIsNil)
    57  }
    58  
    59  func (s *imageSuite) TestCopyImageRetries(c *gc.C) {
    60  	ctrl := gomock.NewController(c)
    61  	defer ctrl.Finish()
    62  
    63  	clock := mocks.NewMockClock(ctrl)
    64  	after := make(chan time.Time, 2)
    65  	after <- time.Time{}
    66  	after <- time.Time{}
    67  	clock.EXPECT().After(gomock.Any()).Return(after).AnyTimes()
    68  	clock.EXPECT().Now().Return(time.Now()).AnyTimes()
    69  
    70  	iSvr := s.NewMockServer(ctrl)
    71  	image := lxdapi.Image{Filename: "this-is-our-image"}
    72  	aliases := []lxdapi.ImageAlias{{Name: "local/image/alias"}}
    73  	req := &lxdclient.ImageCopyArgs{Aliases: aliases}
    74  
    75  	copyOp := lxdtesting.NewMockRemoteOperation(ctrl)
    76  	copyOp.EXPECT().AddHandler(gomock.Any()).Return(nil, nil).AnyTimes()
    77  	copyOp.EXPECT().Wait().Return(nil).Return(errors.New("Failed remote image download: boom"))
    78  	copyOp.EXPECT().Wait().Return(nil).Return(errors.New("Failed remote image download: boom"))
    79  	copyOp.EXPECT().Wait().Return(nil).Return(nil)
    80  	copyOp.EXPECT().GetTarget().Return(&lxdapi.Operation{StatusCode: lxdapi.Success}, nil)
    81  
    82  	iSvr.EXPECT().CopyImage(iSvr, image, req).Return(copyOp, nil).Times(3)
    83  
    84  	jujuSvr, err := lxd.NewTestingServer(iSvr, clock)
    85  	c.Assert(err, jc.ErrorIsNil)
    86  
    87  	sourced := lxd.SourcedImage{
    88  		Image:     &image,
    89  		LXDServer: iSvr,
    90  	}
    91  	err = jujuSvr.CopyRemoteImage(context.Background(), sourced, []string{"local/image/alias"}, lxdtesting.NoOpCallback)
    92  	c.Assert(err, jc.ErrorIsNil)
    93  }
    94  
    95  func (s *imageSuite) TestFindImageLocalServer(c *gc.C) {
    96  	ctrl := gomock.NewController(c)
    97  	defer ctrl.Finish()
    98  	iSvr := s.NewMockServer(ctrl)
    99  
   100  	alias := &lxdapi.ImageAliasesEntry{ImageAliasesEntryPut: lxdapi.ImageAliasesEntryPut{Target: "foo-target"}}
   101  	image := lxdapi.Image{Filename: "this-is-our-image"}
   102  	gomock.InOrder(
   103  		iSvr.EXPECT().GetImageAlias("juju/ubuntu@16.04/"+s.Arch()).Return(alias, lxdtesting.ETag, nil),
   104  		iSvr.EXPECT().GetImage("foo-target").Return(&image, lxdtesting.ETag, nil),
   105  	)
   106  
   107  	jujuSvr, err := lxd.NewServer(iSvr)
   108  	c.Assert(err, jc.ErrorIsNil)
   109  
   110  	found, err := jujuSvr.FindImage(context.Background(), corebase.MakeDefaultBase("ubuntu", "16.04"), s.Arch(), lxdapi.InstanceTypeContainer, []lxd.ServerSpec{{}}, false, nil)
   111  	c.Assert(err, jc.ErrorIsNil)
   112  	c.Check(found.LXDServer, gc.Equals, iSvr)
   113  	c.Check(*found.Image, gc.DeepEquals, image)
   114  }
   115  
   116  func (s *imageSuite) TestFindImageLocalServerUnknownSeries(c *gc.C) {
   117  	ctrl := gomock.NewController(c)
   118  	defer ctrl.Finish()
   119  	iSvr := s.NewMockServer(ctrl)
   120  	iSvr.EXPECT().GetImageAlias("juju/pldlinux@18.04/"+s.Arch()).Return(nil, lxdtesting.ETag, errors.New("not found"))
   121  
   122  	jujuSvr, err := lxd.NewServer(iSvr)
   123  	c.Assert(err, jc.ErrorIsNil)
   124  
   125  	_, err = jujuSvr.FindImage(context.Background(), corebase.MakeDefaultBase("pldlinux", "18.04"), s.Arch(), lxdapi.InstanceTypeContainer, []lxd.ServerSpec{{}}, false, nil)
   126  	c.Check(err, gc.ErrorMatches, `base.*pldlinux.*`)
   127  }
   128  
   129  func (s *imageSuite) TestFindImageRemoteServers(c *gc.C) {
   130  	ctrl := gomock.NewController(c)
   131  	defer ctrl.Finish()
   132  	iSvr := s.NewMockServer(ctrl)
   133  
   134  	rSvr1 := lxdtesting.NewMockImageServer(ctrl)
   135  	rSvr2 := lxdtesting.NewMockImageServer(ctrl)
   136  	s.patch(map[string]lxdclient.ImageServer{
   137  		"server-that-wont-work": rSvr1,
   138  		"server-that-has-image": rSvr2,
   139  	})
   140  
   141  	const imageType = "container"
   142  	image := lxdapi.Image{Filename: "this-is-our-image"}
   143  	alias := lxdapi.ImageAliasesEntry{ImageAliasesEntryPut: lxdapi.ImageAliasesEntryPut{Target: "foo-remote-target"}}
   144  	gomock.InOrder(
   145  		iSvr.EXPECT().GetImageAlias("juju/ubuntu@16.04/"+s.Arch()).Return(nil, lxdtesting.ETag, errors.New("not found")),
   146  		rSvr1.EXPECT().GetImageAliasType(imageType, "16.04/"+s.Arch()).Return(nil, lxdtesting.ETag, errors.New("not found")),
   147  		rSvr2.EXPECT().GetImageAliasType(imageType, "16.04/"+s.Arch()).Return(&alias, lxdtesting.ETag, nil),
   148  		rSvr2.EXPECT().GetImage("foo-remote-target").Return(&image, lxdtesting.ETag, nil),
   149  	)
   150  
   151  	jujuSvr, err := lxd.NewServer(iSvr)
   152  	c.Assert(err, jc.ErrorIsNil)
   153  
   154  	remotes := []lxd.ServerSpec{
   155  		{Name: "server-that-wont-work", Protocol: lxd.LXDProtocol},
   156  		{Name: "server-that-has-image", Protocol: lxd.SimpleStreamsProtocol},
   157  		{Name: "server-that-should-not-be-touched", Protocol: lxd.LXDProtocol},
   158  	}
   159  	found, err := jujuSvr.FindImage(context.Background(), corebase.MakeDefaultBase("ubuntu", "16.04"), s.Arch(), lxdapi.InstanceTypeContainer, remotes, false, nil)
   160  	c.Assert(err, jc.ErrorIsNil)
   161  	c.Check(found.LXDServer, gc.Equals, rSvr2)
   162  	c.Check(*found.Image, gc.DeepEquals, image)
   163  }
   164  
   165  func (s *imageSuite) TestFindImageRemoteServersCopyLocalNoCallback(c *gc.C) {
   166  	ctrl := gomock.NewController(c)
   167  	defer ctrl.Finish()
   168  	iSvr := s.NewMockServer(ctrl)
   169  
   170  	rSvr := lxdtesting.NewMockImageServer(ctrl)
   171  	s.patch(map[string]lxdclient.ImageServer{
   172  		"server-that-has-image": rSvr,
   173  	})
   174  
   175  	copyOp := lxdtesting.NewMockRemoteOperation(ctrl)
   176  	copyOp.EXPECT().Wait().Return(nil).AnyTimes()
   177  	copyOp.EXPECT().GetTarget().Return(&lxdapi.Operation{StatusCode: lxdapi.Success}, nil)
   178  
   179  	localAlias := "juju/ubuntu@16.04/" + s.Arch()
   180  	image := lxdapi.Image{Filename: "this-is-our-image"}
   181  	alias := lxdapi.ImageAliasesEntry{ImageAliasesEntryPut: lxdapi.ImageAliasesEntryPut{Target: "foo-remote-target"}}
   182  	copyReq := &lxdclient.ImageCopyArgs{Aliases: []lxdapi.ImageAlias{{Name: localAlias}}}
   183  	gomock.InOrder(
   184  		iSvr.EXPECT().GetImageAlias(localAlias).Return(nil, lxdtesting.ETag, nil),
   185  		rSvr.EXPECT().GetImageAliasType("container", "16.04/"+s.Arch()).Return(&alias, lxdtesting.ETag, nil),
   186  		rSvr.EXPECT().GetImage("foo-remote-target").Return(&image, lxdtesting.ETag, nil),
   187  		iSvr.EXPECT().CopyImage(rSvr, image, copyReq).Return(copyOp, nil),
   188  	)
   189  
   190  	jujuSvr, err := lxd.NewServer(iSvr)
   191  	c.Assert(err, jc.ErrorIsNil)
   192  
   193  	remotes := []lxd.ServerSpec{
   194  		{Name: "server-that-has-image", Protocol: lxd.SimpleStreamsProtocol},
   195  	}
   196  	found, err := jujuSvr.FindImage(context.Background(), corebase.MakeDefaultBase("ubuntu", "16.04"), s.Arch(), lxdapi.InstanceTypeContainer, remotes, true, nil)
   197  	c.Assert(err, jc.ErrorIsNil)
   198  	c.Check(found.LXDServer, gc.Equals, iSvr)
   199  	c.Check(*found.Image, gc.DeepEquals, image)
   200  }
   201  
   202  func (s *imageSuite) TestFindImageRemoteServersNotFound(c *gc.C) {
   203  	ctrl := gomock.NewController(c)
   204  	defer ctrl.Finish()
   205  	iSvr := s.NewMockServer(ctrl)
   206  
   207  	rSvr := lxdtesting.NewMockImageServer(ctrl)
   208  	s.patch(map[string]lxdclient.ImageServer{
   209  		"server-that-has-image": rSvr,
   210  	})
   211  
   212  	alias := lxdapi.ImageAliasesEntry{ImageAliasesEntryPut: lxdapi.ImageAliasesEntryPut{Target: "foo-remote-target"}}
   213  	gomock.InOrder(
   214  		iSvr.EXPECT().GetImageAlias("juju/ubuntu@18.04/"+s.Arch()).Return(nil, lxdtesting.ETag, errors.New("not found")),
   215  		rSvr.EXPECT().GetImageAliasType("container", "18.04/"+s.Arch()).Return(&alias, lxdtesting.ETag, nil),
   216  		rSvr.EXPECT().GetImage("foo-remote-target").Return(
   217  			nil, lxdtesting.ETag, errors.New("failed to retrieve image")),
   218  	)
   219  
   220  	jujuSvr, err := lxd.NewServer(iSvr)
   221  	c.Assert(err, jc.ErrorIsNil)
   222  
   223  	remotes := []lxd.ServerSpec{{Name: "server-that-has-image", Protocol: lxd.SimpleStreamsProtocol}}
   224  	_, err = jujuSvr.FindImage(context.Background(), corebase.MakeDefaultBase("ubuntu", "18.04"), s.Arch(), lxdapi.InstanceTypeContainer, remotes, false, nil)
   225  	c.Assert(err, gc.ErrorMatches, ".*failed to retrieve image.*")
   226  }
   227  
   228  func (s *imageSuite) TestBaseRemoteAliasesNotSupported(c *gc.C) {
   229  	_, err := lxd.BaseRemoteAliases(corebase.MakeDefaultBase("centos", "7"), "arm64")
   230  	c.Assert(err, gc.ErrorMatches, `base "centos@7" not supported`)
   231  
   232  	_, err = lxd.BaseRemoteAliases(corebase.MakeDefaultBase("centos", "8"), "arm64")
   233  	c.Assert(err, gc.ErrorMatches, `base "centos@8" not supported`)
   234  
   235  	_, err = lxd.BaseRemoteAliases(corebase.MakeDefaultBase("opensuse", "opensuse42"), "s390x")
   236  	c.Assert(err, gc.ErrorMatches, `base "opensuse@opensuse42" not supported`)
   237  }