Skip to content

Commit

Permalink
libcni: up-convert a Config to a ConfigList when no other configs are…
Browse files Browse the repository at this point in the history
… found.
  • Loading branch information
Casey Callendrello authored and Casey Callendrello committed Feb 17, 2017
1 parent 6f3b0ab commit 2719207
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 14 deletions.
11 changes: 7 additions & 4 deletions cnitool/cni.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// 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.
Expand Down Expand Up @@ -42,7 +42,7 @@ func main() {
if netdir == "" {
netdir = DefaultNetDir
}
netconf, err := libcni.LoadConf(netdir, os.Args[2])
netconf, err := libcni.LoadConfList(netdir, os.Args[2])
if err != nil {
exit(err)
}
Expand All @@ -61,10 +61,13 @@ func main() {

switch os.Args[1] {
case CmdAdd:
_, err := cninet.AddNetwork(netconf, rt)
result, err := cninet.AddNetworkList(netconf, rt)
if result != nil {
_ = result.Print()
}
exit(err)
case CmdDel:
exit(cninet.DelNetwork(netconf, rt))
exit(cninet.DelNetworkList(netconf, rt))
}
}

Expand Down
65 changes: 58 additions & 7 deletions libcni/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,25 @@ package libcni

import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"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) {
conf := &NetworkConfig{Bytes: bytes}
if err := json.Unmarshal(bytes, &conf.Network); err != nil {
Expand Down Expand Up @@ -137,7 +149,7 @@ func LoadConf(dir, name string) (*NetworkConfig, error) {
case err != nil:
return nil, err
case len(files) == 0:
return nil, fmt.Errorf("no net configurations found")
return nil, NoConfigsFoundError
}
sort.Strings(files)

Expand All @@ -150,16 +162,13 @@ func LoadConf(dir, name string) (*NetworkConfig, error) {
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) {
files, err := ConfFiles(dir, []string{".conflist"})
switch {
case err != nil:
if err != nil {
return nil, err
case len(files) == 0:
return nil, fmt.Errorf("no net configuration lists found")
}
sort.Strings(files)

Expand All @@ -172,7 +181,23 @@ func LoadConfList(dir, name string) (*NetworkConfigList, error) {
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 {
switch {
// no files at all
case len(files) == 0 && err == NoConfigsFoundError:
return nil, err
// some 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) {
Expand All @@ -199,3 +224,29 @@ func InjectConf(original *NetworkConfig, key string, newValue interface{}) (*Net

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{})
err := json.Unmarshal(original.Bytes, &rawConfig)
if err != nil {
return nil, err
}

rawConfigList := make(map[string]interface{})
rawConfigList["name"] = original.Network.Name
rawConfigList["cniVersion"] = original.Network.CNIVersion
rawConfigList["plugins"] = []interface{}{rawConfig}

b, err := json.Marshal(rawConfigList)
if err != nil {
return nil, err
}
return ConfListFromBytes(b)
}
69 changes: 66 additions & 3 deletions libcni/conf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,21 +184,47 @@ 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() {
BeforeEach(func() {
Expect(os.RemoveAll(configDir)).To(Succeed())
})

It("returns a useful error", func() {
_, 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() {
It("returns a useful error", func() {
_, 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"}))
})
})

Expand Down Expand Up @@ -233,7 +259,7 @@ var _ = Describe("Loading configuration from disk", func() {

It("will not find the config", func() {
_, 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")))
})
})
})
Expand Down Expand Up @@ -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))
})

})

0 comments on commit 2719207

Please sign in to comment.