diff --git a/cmd/show.go b/cmd/show.go index db9484d..34fe9bd 100644 --- a/cmd/show.go +++ b/cmd/show.go @@ -23,11 +23,8 @@ package cmd import ( "fmt" - "os" "gitea.elkins.co/Networking/ccl/internal/pkg/config" - "gitea.elkins.co/Networking/ccl/internal/pkg/container" - toml "github.com/pelletier/go-toml" "github.com/spf13/cobra" ) @@ -40,16 +37,7 @@ var showCmd = &cobra.Command{ Long: `Output a toml format for the listed containers, which should be adequate to use as a ccl config file.`, Run: func(cmd *cobra.Command, args []string) { - type parse struct { - Containers []container.Container `toml:"containers,omitempty"` - } - - conts := config.Union(args, contMask) - output, err := toml.Marshal(parse{conts}) - if err != nil { - fmt.Println("could not marshal containers:", err) - os.Exit(1) - } + output := config.Show(args, contMask) fmt.Fprintln(cmd.OutOrStdout(), string(output)) }, } diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 179702a..dfb436c 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -1,8 +1,12 @@ package config import ( + "bytes" "context" + "fmt" + "net" "os" + "sort" "gitea.elkins.co/Networking/ccl/internal/pkg/container" "gitea.elkins.co/Networking/ccl/internal/pkg/network" @@ -10,6 +14,7 @@ import ( toml "github.com/pelletier/go-toml" log "github.com/sirupsen/logrus" "golang.org/x/exp/slices" + "gopkg.in/guregu/null.v4" ) const ( @@ -60,7 +65,8 @@ func Union(ids []string, catMask []string) (conts []container.Container) { } if len(conts) == 0 { log.WithFields(log.Fields{ - "ids": ids, + "ids": ids, + "catMask": catMask, }).Warnln("No matching containers. If disabled, try adding -a") } slices.SortFunc(conts, func(a, b container.Container) bool { @@ -75,6 +81,26 @@ func Union(ids []string, catMask []string) (conts []container.Container) { return } +func UnionNetworks(ids []string) []network.Network { + nets := make([]network.Network, len(Networks)) + rejects := []int{} + copy(nets, Networks) + for i := range nets { + if !slices.Contains(ids, nets[i].Name) { + rejects = append(rejects, i) + } + } + // reverse the rejects list, so we can delete from the end backward + // and thereby not fuck up sequential indicies + sort.SliceStable(rejects, func(i, j int) bool { + return true + }) + for _, j := range rejects { + nets = slices.Delete(nets, j, j+1) + } + return nets +} + func Init(conn context.Context) error { // A parsing convenience type parse struct { @@ -100,3 +126,66 @@ func Init(conn context.Context) error { }) return nil } + +func Show(ids []string, contMask []string) string { + type parse struct { + Containers []container.Container `toml:"containers,omitempty"` + Networks []network.Network `toml:"networks,omitempty"` + } + + getNet := func(name string) *network.Network { + for i := range Networks { + if Networks[i].Name == name { + return &Networks[i] + } + } + return nil + } + + ipSliceEq := func(a, b []net.IP) bool { + if len(a) != len(b) { + return false + } + for i := range a { + if !bytes.Equal(a[i], b[i]) { + return false + } + } + return true + } + + conts := Union(ids, contMask) + // clear out values equal to the defined networks + for c := range conts { + for n := range conts[c].Networks { + d := getNet(conts[c].Networks[n].Name) + if d == nil { + conts[c].LogEntry().WithField("network", conts[c].Networks[n].Name).Warnln("Network defaults not defined") + continue + } + if conts[c].Networks[n].IPv6 == d.IPv6 { + conts[c].Networks[n].IPv6 = null.Bool{} + } + if ipSliceEq(conts[c].Networks[n].DNS, d.DNS) { + conts[c].Networks[n].DNS = nil + } + } + } + usednets := hashset.New() + for _, c := range conts { + for _, n := range c.Networks { + usednets.Add(n.Name) + } + } + for _, un := range usednets.Values() { + ids = append(ids, un.(string)) + } + nets := UnionNetworks(ids) + + output, err := toml.Marshal(parse{conts, nets}) + if err != nil { + fmt.Println("could not marshal containers:", err) + os.Exit(1) + } + return string(output) +}