Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix ambiguous networks #1831

Merged
merged 9 commits into from
Sep 14, 2020
46 changes: 26 additions & 20 deletions pkg/cluster/internal/providers/docker/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,51 +154,57 @@ func createNetwork(name, ipv6Subnet string) error {

func sortedNetworksWithName(name string) ([]string, error) {
// list all networks by this name
out, err := exec.Output(exec.Command(
lsOut, err := exec.Output(exec.Command(
"docker", "network", "ls",
"--filter=name=^"+regexp.QuoteMeta(name)+"$",
"--format={{json .}}",
"--format={{.ID}}", // output as unambiguous IDs
))
if err != nil {
return nil, err
}

// now inspect each
ids := strings.Split(strings.TrimSuffix(string(lsOut), "\n"), "\n")
inspectOut, err := exec.Output(exec.Command("docker", append([]string{"network", "inspect"}, ids...)...))
BenTheElder marked this conversation as resolved.
Show resolved Hide resolved
// NOTE: a network could be deleted between the ls and inspect calls, that's fine
if err != nil && !isOnlyErrorNoSuchNetwork(err) {
return nil, err
}

// parse
type networkLSEntry struct {
CreatedAt goDefaultTime `json:"CreatedAt"`
ID string `json:"ID"`
type networkInspectEntry struct {
Created rfc3339NanoTime `json:"Created"`
ID string `json:"Id"`
// NOTE: we don't care about the contents here but we need to parse
// how many entries exist in the containers map
Containers map[string]map[string]string `json:"Containers"`
}

networks := []networkLSEntry{}
decoder := json.NewDecoder(bytes.NewReader(out))
for {
var network networkLSEntry
err := decoder.Decode(&network)
if err == io.EOF {
break
} else if err != nil {
return nil, errors.Wrap(err, "failed to decode networks list")
}
networks = append(networks, network)
networks := []networkInspectEntry{}
if err := json.Unmarshal(inspectOut, &networks); err != nil {
return nil, errors.Wrap(err, "failed to decode networks list")
}

// deterministically sort networks
// NOTE: THIS PART IS IMPORTANT!
// TODO(fixme): we should be sorting on active usage first!
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this TODO is the one thing keeping this WIP.
I will come back and rework this later. the overall code will be more or less the same otherwise.

// unfortunately this is only available in docker network inspect
sort.Slice(networks, func(i, j int) bool {
if time.Time(networks[i].CreatedAt).Before(time.Time(networks[j].CreatedAt)) {
if len(networks[i].Containers) < len(networks[j].Containers) {
BenTheElder marked this conversation as resolved.
Show resolved Hide resolved
return true
}
if time.Time(networks[i].Created).Before(time.Time(networks[j].Created)) {
return true
}
return networks[i].ID < networks[j].ID
})

// return network IDs
ids := make([]string, 0, len(networks))
sortedIDs := make([]string, 0, len(networks))
for i := range networks {
ids = append(ids, networks[i].ID)
sortedIDs = append(sortedIDs, networks[i].ID)
}
return ids, nil
return sortedIDs, nil
}

func checkIfNetworkExists(name string) (bool, error) {
Expand Down
15 changes: 14 additions & 1 deletion pkg/cluster/internal/providers/docker/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,21 @@ import (
"time"
)

// rfce3339NanoTime allows us to parse time.Time objects formatted
// as JSON strings in RFC3339Nano
type rfc3339NanoTime time.Time

func (r *rfc3339NanoTime) UnmarshalJSON(p []byte) error {
t, err := time.Parse(`"`+time.RFC3339Nano+`"`, string(p))
if err != nil {
return err
}
*r = rfc3339NanoTime(t)
return nil
}

/*
Docker CLI outputs time.Time objects with the default string format
Docker sometimes CLI outputs time.Time objects with the default string format
This is going to be a huge pain if go actually makes good on their threat
that this format is not stable

Expand Down