Roman Mohr cc71426592 Don't let DHCP delete fail on missing lease
There are at least two reasons why a lease is not present:

 * The dhcp ipam daemon was restarted
 * On add the IPAM plugin failed

Don't fail the IPAM invocation when the lease is not present, to allow
proper device cleanup on CNI delete invocations.
2017-10-11 14:29:16 +02:00

169 lines
3.9 KiB
Go

// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/rpc"
"os"
"path/filepath"
"runtime"
"sync"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/coreos/go-systemd/activation"
)
const listenFdsStart = 3
const resendCount = 3
var errNoMoreTries = errors.New("no more tries")
type DHCP struct {
mux sync.Mutex
leases map[string]*DHCPLease
}
func newDHCP() *DHCP {
return &DHCP{
leases: make(map[string]*DHCPLease),
}
}
// Allocate acquires an IP from a DHCP server for a specified container.
// The acquired lease will be maintained until Release() is called.
func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
conf := types.NetConf{}
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
return fmt.Errorf("error parsing netconf: %v", err)
}
clientID := args.ContainerID + "/" + conf.Name
l, err := AcquireLease(clientID, args.Netns, args.IfName)
if err != nil {
return err
}
ipn, err := l.IPNet()
if err != nil {
l.Stop()
return err
}
d.setLease(args.ContainerID, conf.Name, l)
result.IPs = []*current.IPConfig{{
Version: "4",
Address: *ipn,
Gateway: l.Gateway(),
}}
result.Routes = l.Routes()
return nil
}
// Release stops maintenance of the lease acquired in Allocate()
// and sends a release msg to the DHCP server.
func (d *DHCP) Release(args *skel.CmdArgs, reply *struct{}) error {
conf := types.NetConf{}
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
return fmt.Errorf("error parsing netconf: %v", err)
}
if l := d.getLease(args.ContainerID, conf.Name); l != nil {
l.Stop()
}
return nil
}
func (d *DHCP) getLease(contID, netName string) *DHCPLease {
d.mux.Lock()
defer d.mux.Unlock()
// TODO(eyakubovich): hash it to avoid collisions
l, ok := d.leases[contID+netName]
if !ok {
return nil
}
return l
}
func (d *DHCP) setLease(contID, netName string, l *DHCPLease) {
d.mux.Lock()
defer d.mux.Unlock()
// TODO(eyakubovich): hash it to avoid collisions
d.leases[contID+netName] = l
}
func getListener() (net.Listener, error) {
l, err := activation.Listeners(true)
if err != nil {
return nil, err
}
switch {
case len(l) == 0:
if err := os.MkdirAll(filepath.Dir(socketPath), 0700); err != nil {
return nil, err
}
return net.Listen("unix", socketPath)
case len(l) == 1:
if l[0] == nil {
return nil, fmt.Errorf("LISTEN_FDS=1 but no FD found")
}
return l[0], nil
default:
return nil, fmt.Errorf("Too many (%v) FDs passed through socket activation", len(l))
}
}
func runDaemon(pidfilePath string) error {
// since other goroutines (on separate threads) will change namespaces,
// ensure the RPC server does not get scheduled onto those
runtime.LockOSThread()
// Write the pidfile
if pidfilePath != "" {
if !filepath.IsAbs(pidfilePath) {
return fmt.Errorf("Error writing pidfile %q: path not absolute", pidfilePath)
}
if err := ioutil.WriteFile(pidfilePath, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil {
return fmt.Errorf("Error writing pidfile %q: %v", pidfilePath, err)
}
}
l, err := getListener()
if err != nil {
return fmt.Errorf("Error getting listener: %v", err)
}
dhcp := newDHCP()
rpc.Register(dhcp)
rpc.HandleHTTP()
http.Serve(l, nil)
return nil
}