github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/worker/gclient/container_test.go (about)

     1  package gclient_test
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"net"
     9  	"strings"
    10  	"time"
    11  
    12  	. "github.com/onsi/ginkgo"
    13  	. "github.com/onsi/gomega"
    14  	"github.com/onsi/gomega/gbytes"
    15  
    16  	"code.cloudfoundry.org/garden"
    17  	. "code.cloudfoundry.org/garden/client"
    18  	"code.cloudfoundry.org/garden/client/connection/connectionfakes"
    19  	"code.cloudfoundry.org/garden/gardenfakes"
    20  )
    21  
    22  var _ = Describe("Container", func() {
    23  	var container garden.Container
    24  
    25  	var fakeConnection *connectionfakes.FakeConnection
    26  
    27  	BeforeEach(func() {
    28  		fakeConnection = new(connectionfakes.FakeConnection)
    29  	})
    30  
    31  	JustBeforeEach(func() {
    32  		var err error
    33  
    34  		client := New(fakeConnection)
    35  
    36  		fakeConnection.CreateReturns("some-handle", nil)
    37  
    38  		container, err = client.Create(garden.ContainerSpec{})
    39  		Ω(err).ShouldNot(HaveOccurred())
    40  	})
    41  
    42  	Describe("Handle", func() {
    43  		It("returns the container's handle", func() {
    44  			Ω(container.Handle()).Should(Equal("some-handle"))
    45  		})
    46  	})
    47  
    48  	Describe("Stop", func() {
    49  		It("sends a stop request", func() {
    50  			err := container.Stop(true)
    51  			Ω(err).ShouldNot(HaveOccurred())
    52  
    53  			handle, kill := fakeConnection.StopArgsForCall(0)
    54  			Ω(handle).Should(Equal("some-handle"))
    55  			Ω(kill).Should(BeTrue())
    56  		})
    57  
    58  		Context("when stopping fails", func() {
    59  			disaster := errors.New("oh no!")
    60  
    61  			BeforeEach(func() {
    62  				fakeConnection.StopReturns(disaster)
    63  			})
    64  
    65  			It("returns the error", func() {
    66  				err := container.Stop(true)
    67  				Ω(err).Should(Equal(disaster))
    68  			})
    69  		})
    70  	})
    71  
    72  	Describe("Info", func() {
    73  		It("sends an info request", func() {
    74  			infoToReturn := garden.ContainerInfo{
    75  				State: "chillin",
    76  			}
    77  
    78  			fakeConnection.InfoReturns(infoToReturn, nil)
    79  
    80  			info, err := container.Info()
    81  			Ω(err).ShouldNot(HaveOccurred())
    82  
    83  			Ω(fakeConnection.InfoArgsForCall(0)).Should(Equal("some-handle"))
    84  
    85  			Ω(info).Should(Equal(infoToReturn))
    86  		})
    87  
    88  		Context("when getting info fails", func() {
    89  			disaster := errors.New("oh no!")
    90  
    91  			BeforeEach(func() {
    92  				fakeConnection.InfoReturns(garden.ContainerInfo{}, disaster)
    93  			})
    94  
    95  			It("returns the error", func() {
    96  				_, err := container.Info()
    97  				Ω(err).Should(Equal(disaster))
    98  			})
    99  		})
   100  	})
   101  
   102  	Describe("Properties", func() {
   103  		Context("when getting properties succeeds", func() {
   104  			BeforeEach(func() {
   105  				fakeConnection.PropertiesReturns(garden.Properties{"Foo": "bar"}, nil)
   106  			})
   107  
   108  			It("returns the properties map", func() {
   109  				result, err := container.Properties()
   110  				Ω(err).ShouldNot(HaveOccurred())
   111  				Ω(result).Should(Equal(garden.Properties{"Foo": "bar"}))
   112  			})
   113  		})
   114  
   115  		Context("when getting properties fails", func() {
   116  			disaster := errors.New("oh no!")
   117  
   118  			BeforeEach(func() {
   119  				fakeConnection.PropertiesReturns(nil, disaster)
   120  			})
   121  
   122  			It("returns the error", func() {
   123  				_, err := container.Properties()
   124  				Ω(err).Should(Equal(disaster))
   125  			})
   126  		})
   127  	})
   128  
   129  	Describe("Property", func() {
   130  
   131  		propertyName := "propertyName"
   132  		propertyValue := "propertyValue"
   133  
   134  		Context("when getting property succeeds", func() {
   135  			BeforeEach(func() {
   136  				fakeConnection.PropertyReturns(propertyValue, nil)
   137  			})
   138  
   139  			It("returns the value", func() {
   140  				result, err := container.Property(propertyName)
   141  				Ω(err).ShouldNot(HaveOccurred())
   142  				Ω(result).Should(Equal(propertyValue))
   143  			})
   144  		})
   145  
   146  		Context("when getting property fails", func() {
   147  			disaster := errors.New("oh no!")
   148  
   149  			BeforeEach(func() {
   150  				fakeConnection.PropertyReturns("", disaster)
   151  			})
   152  
   153  			It("returns the error", func() {
   154  				_, err := container.Property(propertyName)
   155  				Ω(err).Should(Equal(disaster))
   156  			})
   157  		})
   158  	})
   159  
   160  	Describe("StreamIn", func() {
   161  		It("sends a stream in request", func() {
   162  			fakeConnection.StreamInStub = func(handle string, spec garden.StreamInSpec) error {
   163  				Ω(spec.Path).Should(Equal("to"))
   164  				Ω(spec.User).Should(Equal("frank"))
   165  
   166  				content, err := ioutil.ReadAll(spec.TarStream)
   167  				Ω(err).ShouldNot(HaveOccurred())
   168  				Ω(string(content)).Should(Equal("stuff"))
   169  
   170  				return nil
   171  			}
   172  
   173  			err := container.StreamIn(garden.StreamInSpec{
   174  				User:      "frank",
   175  				Path:      "to",
   176  				TarStream: bytes.NewBufferString("stuff"),
   177  			})
   178  			Ω(err).ShouldNot(HaveOccurred())
   179  		})
   180  
   181  		Context("when streaming in fails", func() {
   182  			disaster := errors.New("oh no!")
   183  
   184  			BeforeEach(func() {
   185  				fakeConnection.StreamInReturns(
   186  					disaster)
   187  			})
   188  
   189  			It("returns the error", func() {
   190  				err := container.StreamIn(garden.StreamInSpec{
   191  					Path: "to",
   192  				})
   193  				Ω(err).Should(Equal(disaster))
   194  			})
   195  		})
   196  	})
   197  
   198  	Describe("StreamOut", func() {
   199  		It("sends a stream out request", func() {
   200  			fakeConnection.StreamOutReturns(ioutil.NopCloser(strings.NewReader("kewl")), nil)
   201  
   202  			reader, err := container.StreamOut(garden.StreamOutSpec{
   203  				User: "deandra",
   204  				Path: "from",
   205  			})
   206  			bytes, err := ioutil.ReadAll(reader)
   207  			Ω(err).ShouldNot(HaveOccurred())
   208  			Ω(string(bytes)).Should(Equal("kewl"))
   209  
   210  			handle, spec := fakeConnection.StreamOutArgsForCall(0)
   211  			Ω(handle).Should(Equal("some-handle"))
   212  			Ω(spec.Path).Should(Equal("from"))
   213  			Ω(spec.User).Should(Equal("deandra"))
   214  		})
   215  
   216  		Context("when streaming out fails", func() {
   217  			disaster := errors.New("oh no!")
   218  
   219  			BeforeEach(func() {
   220  				fakeConnection.StreamOutReturns(nil, disaster)
   221  			})
   222  
   223  			It("returns the error", func() {
   224  				_, err := container.StreamOut(garden.StreamOutSpec{
   225  					Path: "from",
   226  				})
   227  				Ω(err).Should(Equal(disaster))
   228  			})
   229  		})
   230  	})
   231  
   232  	Describe("CurrentBandwidthLimits", func() {
   233  		It("sends an empty limit request and returns its response", func() {
   234  			limitsToReturn := garden.BandwidthLimits{
   235  				RateInBytesPerSecond:      1,
   236  				BurstRateInBytesPerSecond: 2,
   237  			}
   238  
   239  			fakeConnection.CurrentBandwidthLimitsReturns(limitsToReturn, nil)
   240  
   241  			limits, err := container.CurrentBandwidthLimits()
   242  			Ω(err).ShouldNot(HaveOccurred())
   243  
   244  			Ω(limits).Should(Equal(limitsToReturn))
   245  		})
   246  
   247  		Context("when the request fails", func() {
   248  			disaster := errors.New("oh no!")
   249  
   250  			BeforeEach(func() {
   251  				fakeConnection.CurrentBandwidthLimitsReturns(garden.BandwidthLimits{}, disaster)
   252  			})
   253  
   254  			It("returns the error", func() {
   255  				_, err := container.CurrentBandwidthLimits()
   256  				Ω(err).Should(Equal(disaster))
   257  			})
   258  		})
   259  	})
   260  
   261  	Describe("CurrentCPULimits", func() {
   262  		It("sends an empty limit request and returns its response", func() {
   263  			limitsToReturn := garden.CPULimits{
   264  				LimitInShares: 1,
   265  			}
   266  
   267  			fakeConnection.CurrentCPULimitsReturns(limitsToReturn, nil)
   268  
   269  			limits, err := container.CurrentCPULimits()
   270  			Ω(err).ShouldNot(HaveOccurred())
   271  
   272  			Ω(limits).Should(Equal(limitsToReturn))
   273  		})
   274  
   275  		Context("when the request fails", func() {
   276  			disaster := errors.New("oh no!")
   277  
   278  			BeforeEach(func() {
   279  				fakeConnection.CurrentCPULimitsReturns(garden.CPULimits{}, disaster)
   280  			})
   281  
   282  			It("returns the error", func() {
   283  				_, err := container.CurrentCPULimits()
   284  				Ω(err).Should(Equal(disaster))
   285  			})
   286  		})
   287  	})
   288  
   289  	Describe("CurrentDiskLimits", func() {
   290  		It("sends an empty limit request and returns its response", func() {
   291  			limitsToReturn := garden.DiskLimits{
   292  				InodeSoft: 7,
   293  				InodeHard: 8,
   294  				ByteSoft:  11,
   295  				ByteHard:  12,
   296  				Scope:     garden.DiskLimitScopeExclusive,
   297  			}
   298  
   299  			fakeConnection.CurrentDiskLimitsReturns(limitsToReturn, nil)
   300  
   301  			limits, err := container.CurrentDiskLimits()
   302  			Ω(err).ShouldNot(HaveOccurred())
   303  
   304  			Ω(limits).Should(Equal(limitsToReturn))
   305  		})
   306  
   307  		Context("when the request fails", func() {
   308  			disaster := errors.New("oh no!")
   309  
   310  			BeforeEach(func() {
   311  				fakeConnection.CurrentDiskLimitsReturns(garden.DiskLimits{}, disaster)
   312  			})
   313  
   314  			It("returns the error", func() {
   315  				_, err := container.CurrentDiskLimits()
   316  				Ω(err).Should(Equal(disaster))
   317  			})
   318  		})
   319  	})
   320  
   321  	Describe("CurrentMemoryLimits", func() {
   322  		It("gets the current limits", func() {
   323  			limitsToReturn := garden.MemoryLimits{
   324  				LimitInBytes: 1,
   325  			}
   326  
   327  			fakeConnection.CurrentMemoryLimitsReturns(limitsToReturn, nil)
   328  
   329  			limits, err := container.CurrentMemoryLimits()
   330  			Ω(err).ShouldNot(HaveOccurred())
   331  
   332  			Ω(limits).Should(Equal(limitsToReturn))
   333  		})
   334  
   335  		Context("when the request fails", func() {
   336  			disaster := errors.New("oh no!")
   337  
   338  			BeforeEach(func() {
   339  				fakeConnection.CurrentMemoryLimitsReturns(garden.MemoryLimits{}, disaster)
   340  			})
   341  
   342  			It("returns the error", func() {
   343  				_, err := container.CurrentMemoryLimits()
   344  				Ω(err).Should(Equal(disaster))
   345  			})
   346  		})
   347  	})
   348  
   349  	Describe("Run", func() {
   350  		It("sends a run request and returns the process id and a stream", func() {
   351  			fakeConnection.RunStub = func(handle string, spec garden.ProcessSpec, io garden.ProcessIO) (garden.Process, error) {
   352  				process := new(gardenfakes.FakeProcess)
   353  
   354  				process.IDReturns("process-handle")
   355  				process.WaitReturns(123, nil)
   356  
   357  				go func() {
   358  					defer GinkgoRecover()
   359  
   360  					_, err := fmt.Fprintf(io.Stdout, "stdout data")
   361  					Ω(err).ShouldNot(HaveOccurred())
   362  
   363  					_, err = fmt.Fprintf(io.Stderr, "stderr data")
   364  					Ω(err).ShouldNot(HaveOccurred())
   365  				}()
   366  
   367  				return process, nil
   368  			}
   369  
   370  			spec := garden.ProcessSpec{
   371  				Path: "some-script",
   372  			}
   373  
   374  			stdout := gbytes.NewBuffer()
   375  			stderr := gbytes.NewBuffer()
   376  
   377  			processIO := garden.ProcessIO{
   378  				Stdout: stdout,
   379  				Stderr: stderr,
   380  			}
   381  
   382  			process, err := container.Run(spec, processIO)
   383  			Ω(err).ShouldNot(HaveOccurred())
   384  
   385  			ranHandle, ranSpec, ranIO := fakeConnection.RunArgsForCall(0)
   386  			Ω(ranHandle).Should(Equal("some-handle"))
   387  			Ω(ranSpec).Should(Equal(spec))
   388  			Ω(ranIO).Should(Equal(processIO))
   389  
   390  			Ω(process.ID()).Should(Equal("process-handle"))
   391  
   392  			status, err := process.Wait()
   393  			Ω(err).ShouldNot(HaveOccurred())
   394  			Ω(status).Should(Equal(123))
   395  
   396  			Eventually(stdout).Should(gbytes.Say("stdout data"))
   397  			Eventually(stderr).Should(gbytes.Say("stderr data"))
   398  		})
   399  	})
   400  
   401  	Describe("Attach", func() {
   402  		It("sends an attach request and returns a stream", func() {
   403  			fakeConnection.AttachStub = func(handle string, processID string, io garden.ProcessIO) (garden.Process, error) {
   404  				process := new(gardenfakes.FakeProcess)
   405  
   406  				process.IDReturns("process-handle")
   407  				process.WaitReturns(123, nil)
   408  
   409  				go func() {
   410  					defer GinkgoRecover()
   411  
   412  					_, err := fmt.Fprintf(io.Stdout, "stdout data")
   413  					Ω(err).ShouldNot(HaveOccurred())
   414  
   415  					_, err = fmt.Fprintf(io.Stderr, "stderr data")
   416  					Ω(err).ShouldNot(HaveOccurred())
   417  				}()
   418  
   419  				return process, nil
   420  			}
   421  
   422  			stdout := gbytes.NewBuffer()
   423  			stderr := gbytes.NewBuffer()
   424  
   425  			processIO := garden.ProcessIO{
   426  				Stdout: stdout,
   427  				Stderr: stderr,
   428  			}
   429  
   430  			process, err := container.Attach("process-handle", processIO)
   431  			Ω(err).ShouldNot(HaveOccurred())
   432  
   433  			attachedHandle, attachedID, attachedIO := fakeConnection.AttachArgsForCall(0)
   434  			Ω(attachedHandle).Should(Equal("some-handle"))
   435  			Ω(attachedID).Should(Equal("process-handle"))
   436  			Ω(attachedIO).Should(Equal(processIO))
   437  
   438  			Ω(process.ID()).Should(Equal("process-handle"))
   439  
   440  			status, err := process.Wait()
   441  			Ω(err).ShouldNot(HaveOccurred())
   442  			Ω(status).Should(Equal(123))
   443  
   444  			Eventually(stdout).Should(gbytes.Say("stdout data"))
   445  			Eventually(stderr).Should(gbytes.Say("stderr data"))
   446  		})
   447  
   448  		Context("when the process requested is not found", func() {
   449  			It("returns ProcessNotFoundError", func() {
   450  				fakeConnection.AttachReturns(nil, garden.ProcessNotFoundError{
   451  					ProcessID: "not-existing-process",
   452  				})
   453  
   454  				_, err := container.Attach("notExistingProcess", garden.ProcessIO{})
   455  				Ω(err).Should(Equal(garden.ProcessNotFoundError{
   456  					ProcessID: "not-existing-process",
   457  				}))
   458  			})
   459  		})
   460  	})
   461  
   462  	Describe("NetIn", func() {
   463  		It("sends a net in request", func() {
   464  			fakeConnection.NetInReturns(111, 222, nil)
   465  
   466  			hostPort, containerPort, err := container.NetIn(123, 456)
   467  			Ω(err).ShouldNot(HaveOccurred())
   468  			Ω(hostPort).Should(Equal(uint32(111)))
   469  			Ω(containerPort).Should(Equal(uint32(222)))
   470  
   471  			h, hp, cp := fakeConnection.NetInArgsForCall(0)
   472  			Ω(h).Should(Equal("some-handle"))
   473  			Ω(hp).Should(Equal(uint32(123)))
   474  			Ω(cp).Should(Equal(uint32(456)))
   475  		})
   476  
   477  		Context("when the request fails", func() {
   478  			disaster := errors.New("oh no!")
   479  
   480  			BeforeEach(func() {
   481  				fakeConnection.NetInReturns(0, 0, disaster)
   482  			})
   483  
   484  			It("returns the error", func() {
   485  				_, _, err := container.NetIn(123, 456)
   486  				Ω(err).Should(Equal(disaster))
   487  			})
   488  		})
   489  	})
   490  
   491  	Describe("NetOut", func() {
   492  		It("sends NetOut requests over the connection", func() {
   493  			Ω(container.NetOut(garden.NetOutRule{
   494  				Networks: []garden.IPRange{garden.IPRangeFromIP(net.ParseIP("1.2.3.4"))},
   495  				Ports: []garden.PortRange{
   496  					{Start: 12, End: 24},
   497  				},
   498  				Log: true,
   499  			})).Should(Succeed())
   500  
   501  			h, rule := fakeConnection.NetOutArgsForCall(0)
   502  			Ω(h).Should(Equal("some-handle"))
   503  
   504  			Ω(rule.Networks).Should(HaveLen(1))
   505  			Ω(rule.Networks[0]).Should(Equal(garden.IPRange{Start: net.ParseIP("1.2.3.4"), End: net.ParseIP("1.2.3.4")}))
   506  
   507  			Ω(rule.Ports).Should(HaveLen(1))
   508  			Ω(rule.Ports[0]).Should(Equal(garden.PortRange{Start: 12, End: 24}))
   509  
   510  			Ω(rule.Log).Should(Equal(true))
   511  		})
   512  	})
   513  
   514  	Describe(("GraceTime"), func() {
   515  		It("send the set grace time request", func() {
   516  			graceTime := time.Second * 5
   517  
   518  			Ω(container.SetGraceTime(graceTime)).Should(Succeed())
   519  
   520  			Ω(fakeConnection.SetGraceTimeCallCount()).Should(Equal(1))
   521  			handle, actualGraceTime := fakeConnection.SetGraceTimeArgsForCall(0)
   522  			Ω(handle).Should(Equal("some-handle"))
   523  			Ω(actualGraceTime).Should(Equal(graceTime))
   524  		})
   525  
   526  		Context("when the request fails", func() {
   527  			disaster := errors.New("banana")
   528  
   529  			BeforeEach(func() {
   530  				fakeConnection.SetGraceTimeReturns(disaster)
   531  			})
   532  
   533  			It("returns the error", func() {
   534  				err := container.SetGraceTime(time.Second * 5)
   535  				Ω(err).Should(Equal(disaster))
   536  			})
   537  		})
   538  	})
   539  
   540  	Context("when the request fails", func() {
   541  		disaster := errors.New("oh no!")
   542  
   543  		BeforeEach(func() {
   544  			fakeConnection.NetOutReturns(disaster)
   545  		})
   546  
   547  		It("returns the error", func() {
   548  			err := container.NetOut(garden.NetOutRule{})
   549  			Ω(err).Should(Equal(disaster))
   550  		})
   551  	})
   552  })