// AddContainer add a container to the specified scope, optionally specifying an ip address // for the container in the scope func (c *Context) AddContainer(h *exec.Handle, options *AddContainerOptions) error { defer trace.End(trace.Begin("")) c.Lock() defer c.Unlock() if h == nil { return fmt.Errorf("handle is required") } var err error s, err := c.resolveScope(options.Scope) if err != nil { return err } if h.ExecConfig.Networks != nil { if _, ok := h.ExecConfig.Networks[s.Name()]; ok { // already part of this scope return nil } // check if container is already part of an "external" scope; // only one "external" scope per container is allowed if s.Type() == constants.ExternalScopeType { for name := range h.ExecConfig.Networks { sc, _ := c.resolveScope(name) if sc.Type() == constants.ExternalScopeType { return fmt.Errorf("container can only be added to at most one mapped network") } } } } // figure out if we need to add a new NIC // if there is already a NIC connected to a // bridge network and we are adding the container // to a bridge network, we just reuse that // NIC var pciSlot int32 if s.Type() == constants.BridgeScopeType { for _, ne := range h.ExecConfig.Networks { sc, err := c.resolveScope(ne.Network.Name) if err != nil { return err } if sc.Type() != constants.BridgeScopeType { continue } if ne.ID != "" { pciSlot = atoiOrZero(ne.ID) if pciSlot != 0 { break } } } } if pciSlot == 0 { d, err := addEthernetCard(h, s) if err != nil { return err } pciSlot = spec.VirtualDeviceSlotNumber(d) } if h.ExecConfig.Networks == nil { h.ExecConfig.Networks = make(map[string]*executor.NetworkEndpoint) } ne := &executor.NetworkEndpoint{ Common: executor.Common{ ID: strconv.Itoa(int(pciSlot)), // Name: this would cause NIC renaming if uncommented }, Network: executor.ContainerNetwork{ Common: executor.Common{ Name: s.Name(), }, Aliases: options.Aliases, Type: s.Type(), }, Ports: options.Ports, } pools := s.Pools() ne.Network.Pools = make([]ip.Range, len(pools)) for i, p := range pools { ne.Network.Pools[i] = *p } ne.Static = false if options.IP != nil && !ip.IsUnspecifiedIP(*options.IP) { ne.Static = true ne.IP = &net.IPNet{ IP: *options.IP, Mask: s.Subnet().Mask, } } h.ExecConfig.Networks[s.Name()] = ne return nil }
func TestContextRemoveContainer(t *testing.T) { hFoo := exec.NewContainer(uid.New()) ctx, err := NewContext(net.IPNet{IP: net.IPv4(172, 16, 0, 0), Mask: net.CIDRMask(12, 32)}, net.CIDRMask(16, 32)) if err != nil { t.Fatalf("NewContext() => (nil, %s), want (ctx, nil)", err) } scope, err := ctx.NewScope(BridgeScopeType, "scope", nil, nil, nil, nil) if err != nil { t.Fatalf("ctx.NewScope() => (nil, %s), want (scope, nil)", err) } options := &AddContainerOptions{ Scope: scope.Name(), } ctx.AddContainer(hFoo, options) ctx.BindContainer(hFoo) // container that is added to multiple bridge scopes hBar := exec.NewContainer(uid.New()) options.Scope = "default" ctx.AddContainer(hBar, options) options.Scope = scope.Name() ctx.AddContainer(hBar, options) var tests = []struct { h *exec.Handle scope string err error }{ {nil, "", fmt.Errorf("")}, // nil handle {hBar, "bar", fmt.Errorf("")}, // scope not found {hFoo, scope.Name(), fmt.Errorf("")}, // bound container {exec.NewContainer(uid.New()), "default", fmt.Errorf("")}, // container not part of scope {hBar, "default", nil}, {hBar, scope.Name(), nil}, } for i, te := range tests { var ne *executor.NetworkEndpoint if te.h != nil && te.h.ExecConfig.Networks != nil { ne = te.h.ExecConfig.Networks[te.scope] } err = ctx.RemoveContainer(te.h, te.scope) if te.err != nil { // expect error if err == nil { t.Fatalf("%d: ctx.RemoveContainer(%#v, %s) => nil want err", i, te.h, te.scope) } continue } s, err := ctx.resolveScope(te.scope) if err != nil { t.Fatalf(err.Error()) } if s.Container(uid.Parse(te.h.Container.ExecConfig.ID)) != nil { t.Fatalf("container %s is part of scope %s", te.h, s.Name()) } // should have a remove spec for NIC, if container was only part of one bridge scope dcs, err := te.h.Spec.FindNICs(context.TODO(), s.Network()) if err != nil { t.Fatalf(err.Error()) } found := false var d types.BaseVirtualDevice for _, dc := range dcs { if dc.GetVirtualDeviceConfigSpec().Operation != types.VirtualDeviceConfigSpecOperationRemove { continue } d = dc.GetVirtualDeviceConfigSpec().Device found = true break } // if a remove spec for the NIC was found, check if any other // network endpoints are still using it if found { for _, ne := range te.h.ExecConfig.Networks { if atoiOrZero(ne.ID) == spec.VirtualDeviceSlotNumber(d) { t.Fatalf("%d: NIC with pci slot %d is still in use by a network endpoint %#v", i, spec.VirtualDeviceSlotNumber(d), ne) } } } else if ne != nil { // check if remove spec for NIC should have been there for _, ne2 := range te.h.ExecConfig.Networks { if ne.ID == ne2.ID { t.Fatalf("%d: NIC with pci slot %s should have been removed", i, ne.ID) } } } // metadata should be gone if _, ok := te.h.ExecConfig.Networks[te.scope]; ok { t.Fatalf("%d: endpoint metadata for container still present in handle %#v", i, te.h.ExecConfig) } } }
if d == nil { backing, err := s.Network().EthernetCardBackingInfo(ctx) if err != nil { return nil, err } if d, err = devices.CreateEthernetCard("vmxnet3", backing); err != nil { return nil, err } d.GetVirtualDevice().DeviceInfo = &types.Description{ Label: s.name, } } if spec.VirtualDeviceSlotNumber(d) == spec.NilSlot { slots := make(map[int32]bool) for _, e := range h.ExecConfig.Networks { if e.Common.ID != "" { slot, err := strconv.Atoi(e.Common.ID) if err == nil { slots[int32(slot)] = true } } } h.Spec.AssignSlotNumber(d, slots) } if dc == nil { devices = append(devices, d)
func TestContextAddContainer(t *testing.T) { ctx, err := NewContext(net.IPNet{IP: net.IPv4(172, 16, 0, 0), Mask: net.CIDRMask(12, 32)}, net.CIDRMask(16, 32)) if err != nil { t.Fatalf("NewContext() => (nil, %s), want (ctx, nil)", err) return } h := exec.NewContainer("foo") var devices object.VirtualDeviceList backing, _ := ctx.DefaultScope().Network().EthernetCardBackingInfo(context.TODO()) specWithEthCard := &spec.VirtualMachineConfigSpec{ VirtualMachineConfigSpec: &types.VirtualMachineConfigSpec{}, } var d types.BaseVirtualDevice if d, err = devices.CreateEthernetCard("vmxnet3", backing); err == nil { d.GetVirtualDevice().SlotInfo = &types.VirtualDevicePciBusSlotInfo{ PciSlotNumber: 1111, } devices = append(devices, d) var cs []types.BaseVirtualDeviceConfigSpec if cs, err = devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd); err == nil { specWithEthCard.DeviceChange = cs } } if err != nil { t.Fatalf(err.Error()) } aecErr := func(_ *exec.Handle, _ *Scope) (types.BaseVirtualDevice, error) { return nil, fmt.Errorf("error") } otherScope, err := ctx.NewScope(BridgeScopeType, "other", nil, net.IPv4(0, 0, 0, 0), nil, nil) if err != nil { t.Fatalf("failed to add scope") } hBar := exec.NewContainer(uid.New()) var tests = []struct { aec func(h *exec.Handle, s *Scope) (types.BaseVirtualDevice, error) h *exec.Handle s *spec.VirtualMachineConfigSpec scope string ip *net.IP err error }{ // nil handle {nil, nil, nil, "", nil, fmt.Errorf("")}, // scope not found {nil, h, nil, "foo", nil, ResourceNotFoundError{}}, // addEthernetCard returns error {aecErr, h, nil, "default", nil, fmt.Errorf("")}, // add a container {nil, h, nil, "default", nil, nil}, // container already added {nil, h, nil, "default", nil, nil}, {nil, hBar, specWithEthCard, "default", nil, nil}, {nil, hBar, nil, otherScope.Name(), nil, nil}, } origAEC := addEthernetCard defer func() { addEthernetCard = origAEC }() for i, te := range tests { // setup addEthernetCard = origAEC scopy := &spec.VirtualMachineConfigSpec{} if te.h != nil { te.h.SetSpec(te.s) if te.h.Spec != nil { *scopy = *te.h.Spec } } if te.aec != nil { addEthernetCard = te.aec } options := &AddContainerOptions{ Scope: te.scope, IP: te.ip, } err := ctx.AddContainer(te.h, options) if te.err != nil { // expect an error if err == nil { t.Fatalf("case %d: ctx.AddContainer(%v, %s, %s) => nil want err", i, te.h, te.scope, te.ip) } if reflect.TypeOf(err) != reflect.TypeOf(te.err) { t.Fatalf("case %d: ctx.AddContainer(%v, %s, %s) => (%v, %v) want (%v, %v)", i, te.h, te.scope, te.ip, err, te.err, err, te.err) } if _, ok := te.err.(DuplicateResourceError); ok { continue } // verify no device changes in the spec if te.s != nil { if len(scopy.DeviceChange) != len(h.Spec.DeviceChange) { t.Fatalf("case %d: ctx.AddContainer(%v, %s, %s) added device", i, te.h, te.scope, te.ip) } } continue } if err != nil { t.Fatalf("case %d: ctx.AddContainer(%v, %s, %s) => %s want nil", i, te.h, te.scope, te.ip, err) } // verify the container was not added to the scope s, _ := ctx.resolveScope(te.scope) if s != nil && te.h != nil { c := s.Container(uid.Parse(te.h.Container.ExecConfig.ID)) if c != nil { t.Fatalf("case %d: ctx.AddContainer(%v, %s, %s) added container", i, te.h, te.scope, te.ip) } } // spec should have a nic attached to the scope's network var dev types.BaseVirtualDevice dcs, err := te.h.Spec.FindNICs(context.TODO(), s.Network()) if len(dcs) != 1 { t.Fatalf("case %d: ctx.AddContainer(%v, %s, %s) more than one NIC added for scope %s", i, te.h, te.scope, te.ip, s.Network()) } dev = dcs[0].GetVirtualDeviceConfigSpec().Device if spec.VirtualDeviceSlotNumber(dev) == spec.NilSlot { t.Fatalf("case %d: ctx.AddContainer(%v, %s, %s) NIC added has nil pci slot", i, te.h, te.scope, te.ip) } // spec metadata should be updated with endpoint info ne, ok := te.h.ExecConfig.Networks[s.Name()] if !ok { t.Fatalf("case %d: ctx.AddContainer(%v, %s, %s) no network endpoint info added", i, te.h, te.scope, te.ip) } if spec.VirtualDeviceSlotNumber(dev) != atoiOrZero(ne.ID) { t.Fatalf("case %d; ctx.AddContainer(%v, %s, %s) => ne.ID == %d, want %d", i, te.h, te.scope, te.ip, atoiOrZero(ne.ID), spec.VirtualDeviceSlotNumber(dev)) } if ne.Network.Name != s.Name() { t.Fatalf("case %d; ctx.AddContainer(%v, %s, %s) => ne.NetworkName == %s, want %s", i, te.h, te.scope, te.ip, ne.Network.Name, s.Name()) } if te.ip != nil && !te.ip.Equal(ne.Static.IP) { t.Fatalf("case %d; ctx.AddContainer(%v, %s, %s) => ne.Static.IP == %s, want %s", i, te.h, te.scope, te.ip, ne.Static.IP, te.ip) } if te.ip == nil && ne.Static != nil { t.Fatalf("case %d; ctx.AddContainer(%v, %s, %s) => ne.Static.IP == %s, want %s", i, te.h, te.scope, te.ip, ne.Static.IP, net.IPv4zero) } } }