libcni: up-convert a Config to a ConfigList when no other configs are found.

This commit is contained in:
Casey Callendrello 2017-02-17 12:19:38 +01:00
parent 6f3b0abcc7
commit 6e6ad53ea8
3 changed files with 132 additions and 14 deletions

View File

@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc. // Copyright 2015 CNI authors
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -42,7 +42,7 @@ func main() {
if netdir == "" { if netdir == "" {
netdir = DefaultNetDir netdir = DefaultNetDir
} }
netconf, err := libcni.LoadConf(netdir, os.Args[2]) netconf, err := libcni.LoadConfList(netdir, os.Args[2])
if err != nil { if err != nil {
exit(err) exit(err)
} }
@ -61,10 +61,13 @@ func main() {
switch os.Args[1] { switch os.Args[1] {
case CmdAdd: case CmdAdd:
_, err := cninet.AddNetwork(netconf, rt) result, err := cninet.AddNetworkList(netconf, rt)
if result != nil {
_ = result.Print()
}
exit(err) exit(err)
case CmdDel: case CmdDel:
exit(cninet.DelNetwork(netconf, rt)) exit(cninet.DelNetworkList(netconf, rt))
} }
} }

View File

@ -16,6 +16,7 @@ package libcni
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@ -23,6 +24,17 @@ import (
"sort" "sort"
) )
type NotFoundError struct {
Dir string
Name string
}
func (e NotFoundError) Error() string {
return fmt.Sprintf(`no net configuration with name "%s" in %s`, e.Name, e.Dir)
}
var NoConfigsFoundError = errors.New("no net configurations found")
func ConfFromBytes(bytes []byte) (*NetworkConfig, error) { func ConfFromBytes(bytes []byte) (*NetworkConfig, error) {
conf := &NetworkConfig{Bytes: bytes} conf := &NetworkConfig{Bytes: bytes}
if err := json.Unmarshal(bytes, &conf.Network); err != nil { if err := json.Unmarshal(bytes, &conf.Network); err != nil {
@ -137,7 +149,7 @@ func LoadConf(dir, name string) (*NetworkConfig, error) {
case err != nil: case err != nil:
return nil, err return nil, err
case len(files) == 0: case len(files) == 0:
return nil, fmt.Errorf("no net configurations found") return nil, NoConfigsFoundError
} }
sort.Strings(files) sort.Strings(files)
@ -150,16 +162,13 @@ func LoadConf(dir, name string) (*NetworkConfig, error) {
return conf, nil return conf, nil
} }
} }
return nil, fmt.Errorf(`no net configuration with name "%s" in %s`, name, dir) return nil, NotFoundError{dir, name}
} }
func LoadConfList(dir, name string) (*NetworkConfigList, error) { func LoadConfList(dir, name string) (*NetworkConfigList, error) {
files, err := ConfFiles(dir, []string{".conflist"}) files, err := ConfFiles(dir, []string{".conflist"})
switch { if err != nil {
case err != nil:
return nil, err return nil, err
case len(files) == 0:
return nil, fmt.Errorf("no net configuration lists found")
} }
sort.Strings(files) sort.Strings(files)
@ -172,7 +181,24 @@ func LoadConfList(dir, name string) (*NetworkConfigList, error) {
return conf, nil return conf, nil
} }
} }
return nil, fmt.Errorf(`no net configuration list with name "%s" in %s`, name, dir)
// Try and load a network configuration file (instead of list)
// from the same name, then upconvert.
singleConf, err := LoadConf(dir, name)
if err != nil {
// A little extra logic so the error makes sense
switch {
// neither configlists nor config files found
case len(files) == 0 && err == NoConfigsFoundError:
return nil, err
// config lists found but no config files found
case len(files) != 0 && err == NoConfigsFoundError:
return nil, NotFoundError{dir, name}
default: // either not found or parse error
return nil, err
}
}
return ConfListFromConf(singleConf)
} }
func InjectConf(original *NetworkConfig, key string, newValue interface{}) (*NetworkConfig, error) { func InjectConf(original *NetworkConfig, key string, newValue interface{}) (*NetworkConfig, error) {
@ -199,3 +225,29 @@ func InjectConf(original *NetworkConfig, key string, newValue interface{}) (*Net
return ConfFromBytes(newBytes) return ConfFromBytes(newBytes)
} }
// ConfListFromConf "upconverts" a network config in to a NetworkConfigList,
// with the single network as the only entry in the list.
func ConfListFromConf(original *NetworkConfig) (*NetworkConfigList, error) {
// Re-deserialize the config's json, then make a raw map configlist.
// This may seem a bit strange, but it's to make the Bytes fields
// actually make sense. Otherwise, the generated json is littered with
// golang default values.
rawConfig := make(map[string]interface{})
if err := json.Unmarshal(original.Bytes, &rawConfig); err != nil {
return nil, err
}
rawConfigList := map[string]interface{}{
"name": original.Network.Name,
"cniVersion": original.Network.CNIVersion,
"plugins": []interface{}{rawConfig},
}
b, err := json.Marshal(rawConfigList)
if err != nil {
return nil, err
}
return ConfListFromBytes(b)
}

View File

@ -184,6 +184,32 @@ var _ = Describe("Loading configuration from disk", func() {
})) }))
}) })
Context("when there is a config file with the same name as the list", func() {
BeforeEach(func() {
configFile := []byte(`{
"name": "some-list",
"cniVersion": "0.2.0",
"type": "bridge"
}`)
Expect(ioutil.WriteFile(filepath.Join(configDir, "49-whatever.conf"), configFile, 0600)).To(Succeed())
})
It("Loads the config list first", func() {
netConfigList, err := libcni.LoadConfList(configDir, "some-list")
Expect(err).NotTo(HaveOccurred())
Expect(len(netConfigList.Plugins)).To(Equal(3))
})
It("falls back to the config file", func() {
Expect(os.Remove(filepath.Join(configDir, "50-whatever.conflist"))).To(Succeed())
netConfigList, err := libcni.LoadConfList(configDir, "some-list")
Expect(err).NotTo(HaveOccurred())
Expect(len(netConfigList.Plugins)).To(Equal(1))
Expect(netConfigList.Plugins[0].Network.Type).To(Equal("bridge"))
})
})
Context("when the config directory does not exist", func() { Context("when the config directory does not exist", func() {
BeforeEach(func() { BeforeEach(func() {
Expect(os.RemoveAll(configDir)).To(Succeed()) Expect(os.RemoveAll(configDir)).To(Succeed())
@ -191,14 +217,14 @@ var _ = Describe("Loading configuration from disk", func() {
It("returns a useful error", func() { It("returns a useful error", func() {
_, err := libcni.LoadConfList(configDir, "some-plugin") _, err := libcni.LoadConfList(configDir, "some-plugin")
Expect(err).To(MatchError("no net configuration lists found")) Expect(err).To(MatchError("no net configurations found"))
}) })
}) })
Context("when there is no config for the desired plugin list", func() { Context("when there is no config for the desired plugin list", func() {
It("returns a useful error", func() { It("returns a useful error", func() {
_, err := libcni.LoadConfList(configDir, "some-other-plugin") _, err := libcni.LoadConfList(configDir, "some-other-plugin")
Expect(err).To(MatchError(ContainSubstring(`no net configuration list with name "some-other-plugin" in`))) Expect(err).To(MatchError(libcni.NotFoundError{configDir, "some-other-plugin"}))
}) })
}) })
@ -233,7 +259,7 @@ var _ = Describe("Loading configuration from disk", func() {
It("will not find the config", func() { It("will not find the config", func() {
_, err := libcni.LoadConfList(configDir, "deep") _, err := libcni.LoadConfList(configDir, "deep")
Expect(err).To(MatchError(HavePrefix("no net configuration list with name"))) Expect(err).To(MatchError(HavePrefix("no net configuration with name")))
}) })
}) })
}) })
@ -339,3 +365,40 @@ var _ = Describe("Loading configuration from disk", func() {
}) })
}) })
}) })
var _ = Describe("ConfListFromConf", func() {
var testNetConfig *libcni.NetworkConfig
BeforeEach(func() {
pb := []byte(`{"name":"some-plugin","cniVersion":"0.3.0" }`)
tc, err := libcni.ConfFromBytes(pb)
Expect(err).NotTo(HaveOccurred())
testNetConfig = tc
})
It("correctly upconverts a NetworkConfig to a NetworkConfigList", func() {
ncl, err := libcni.ConfListFromConf(testNetConfig)
Expect(err).NotTo(HaveOccurred())
bytes := ncl.Bytes
// null out the json - we don't care about the exact marshalling
ncl.Bytes = nil
ncl.Plugins[0].Bytes = nil
testNetConfig.Bytes = nil
Expect(ncl).To(Equal(&libcni.NetworkConfigList{
Name: "some-plugin",
CNIVersion: "0.3.0",
Plugins: []*libcni.NetworkConfig{testNetConfig},
}))
//Test that the json unmarshals to the same data
ncl2, err := libcni.ConfListFromBytes(bytes)
Expect(err).NotTo(HaveOccurred())
ncl2.Bytes = nil
ncl2.Plugins[0].Bytes = nil
Expect(ncl2).To(Equal(ncl))
})
})