diff --git a/docs/networking/network.md b/docs/networking/network.md index 9bfdcbc77..aba7edf78 100644 --- a/docs/networking/network.md +++ b/docs/networking/network.md @@ -8,7 +8,7 @@ network:create # Creates an attachable docker netwo network:destroy # Destroys a docker network network:exists # Checks if a docker network exists network:info [--format text|json] # Outputs information about a docker network -network:list [--format text|json] # Lists all docker networks +network:list [--format text|json] [--dokku-managed] # Lists all docker networks network:report [] [] # Displays a network report for one or more apps network:rebuild # Rebuilds network settings for an app network:rebuildall # Rebuild network settings for all apps @@ -59,13 +59,26 @@ dokku network:list --format json ``` [ - {"CreatedAt":"2024-02-25T01:55:24.275184461Z","Driver":"bridge","ID":"d18df2d21433","Internal":false,"IPv6":false,"Labels":{},"Name":"bridge","Scope":"local"}, - {"CreatedAt":"2024-02-25T01:55:24.275184461Z","Driver":"bridge","ID":"f50fa882e7de","Internal":false,"IPv6":false,"Labels":{},"Name":"test-network","Scope":"local"}, - {"CreatedAt":"2024-02-25T01:55:24.275184461Z","Driver":"host","ID":"ab6a59291443","Internal":false,"IPv6":false,"Labels":{},"Name":"host","Scope":"local"}, - {"CreatedAt":"2024-02-25T01:55:24.275184461Z","Driver":"null","ID":"e2506bc8b7d7","Internal":false,"IPv6":false,"Labels":{},"Name":"none","Scope":"local"} + {"CreatedAt":"2024-02-25T01:55:24.275184461Z","DokkuManaged":false,"Driver":"bridge","ID":"d18df2d21433","Internal":false,"IPv6":false,"Labels":{},"Name":"bridge","Scope":"local"}, + {"CreatedAt":"2024-02-25T01:55:24.275184461Z","DokkuManaged":true,"Driver":"bridge","ID":"f50fa882e7de","Internal":false,"IPv6":false,"Labels":{"com.dokku.network-name":"test-network"},"Name":"test-network","Scope":"local"}, + {"CreatedAt":"2024-02-25T01:55:24.275184461Z","DokkuManaged":false,"Driver":"host","ID":"ab6a59291443","Internal":false,"IPv6":false,"Labels":{},"Name":"host","Scope":"local"}, + {"CreatedAt":"2024-02-25T01:55:24.275184461Z","DokkuManaged":false,"Driver":"null","ID":"e2506bc8b7d7","Internal":false,"IPv6":false,"Labels":{},"Name":"none","Scope":"local"} ] ``` +The `DokkuManaged` field is `true` for networks created by `network:create` and `false` for Docker built-in networks (such as `bridge`, `host`, and `none`) or networks created outside of Dokku (such as compose `*_default` networks). This can be used by automation to determine which networks Dokku is responsible for. + +The `network:list` command also takes a `--dokku-managed` flag, which restricts the output to only those networks created by Dokku. It can be combined with the `--format` flag: + +```shell +dokku network:list --dokku-managed +``` + +``` +=====> Networks +test-network +``` + ### Creating a network > [!IMPORTANT] @@ -142,10 +155,11 @@ dokku network:info bridge ``` =====> bridge network information - ID: d18df2d21433 - Name: bridge - Driver: bridge - Scope: local + ID: d18df2d21433 + Name: bridge + Driver: bridge + Scope: local + Dokku managed: false ``` The `network:info` command also takes a `--format` flag, with the valid options including `text` (default) and `json`. The `json` output format can be used for automation purposes: @@ -155,7 +169,7 @@ dokku network:info bridge --format json ``` ``` -{"CreatedAt":"2024-02-25T01:55:24.275184461Z","Driver":"bridge","ID":"d18df2d21433","Internal":false,"IPv6":false,"Labels":{},"Name":"bridge","Scope":"local"} +{"CreatedAt":"2024-02-25T01:55:24.275184461Z","DokkuManaged":false,"Driver":"bridge","ID":"d18df2d21433","Internal":false,"IPv6":false,"Labels":{},"Name":"bridge","Scope":"local"} ``` ### Routing an app to a known ip:port combination diff --git a/plugins/network/functions.go b/plugins/network/functions.go index f60ac266e..ed3be21f7 100644 --- a/plugins/network/functions.go +++ b/plugins/network/functions.go @@ -11,15 +11,21 @@ import ( "github.com/dokku/dokku/plugins/common" ) +// dokkuNetworkNameLabel is the docker label network:create applies to every +// network it creates. Its presence is the marker used to identify a network as +// dokku-managed. +const dokkuNetworkNameLabel = "com.dokku.network-name" + type DockerNetwork struct { - CreatedAt time.Time - Driver string - ID string - Internal bool - IPv6 bool - Labels map[string]string - Name string - Scope string + CreatedAt time.Time + DokkuManaged bool + Driver string + ID string + Internal bool + IPv6 bool + Labels map[string]string + Name string + Scope string } // attachAppToNetwork attaches a container to a network @@ -206,6 +212,10 @@ func getNetworks() (map[string]DockerNetwork, error) { network.Labels[key] = value } + if _, ok := network.Labels[dokkuNetworkNameLabel]; ok { + network.DokkuManaged = true + } + networks[network.Name] = network } diff --git a/plugins/network/src/commands/commands.go b/plugins/network/src/commands/commands.go index f79bda5d0..ed39b6b95 100644 --- a/plugins/network/src/commands/commands.go +++ b/plugins/network/src/commands/commands.go @@ -22,7 +22,7 @@ Additional commands:` network:destroy , Destroys a docker network network:exists , Checks if a docker network exists network:info [--format text|json], Outputs information about a docker network - network:list [--format text|json], Lists all docker networks + network:list [--format text|json] [--dokku-managed], Lists all docker networks network:rebuild , Rebuilds network settings for an app network:rebuildall, Rebuild network settings for all apps network:report [] [], Displays a network report for one or more apps diff --git a/plugins/network/src/subcommands/subcommands.go b/plugins/network/src/subcommands/subcommands.go index 02e4cc7d2..83fe37f98 100644 --- a/plugins/network/src/subcommands/subcommands.go +++ b/plugins/network/src/subcommands/subcommands.go @@ -43,8 +43,9 @@ func main() { case "list": args := flag.NewFlagSet("network:list", flag.ExitOnError) format := args.String("format", "text", "format: [ text | json ]") + dokkuManaged := args.Bool("dokku-managed", false, "show only dokku-managed networks") args.Parse(os.Args[2:]) - err = network.CommandList(*format) + err = network.CommandList(*format, *dokkuManaged) case "rebuild": args := flag.NewFlagSet("network:rebuild", flag.ExitOnError) args.Parse(os.Args[2:]) diff --git a/plugins/network/subcommands.go b/plugins/network/subcommands.go index 56e4e6bd2..3c28e15dd 100644 --- a/plugins/network/subcommands.go +++ b/plugins/network/subcommands.go @@ -15,7 +15,7 @@ func CommandCreate(networkName string) error { common.LogInfo1Quiet(fmt.Sprintf("Creating network %v", networkName)) result, err := common.CallExecCommand(common.ExecCommandInput{ Command: common.DockerBin(), - Args: []string{"network", "create", "--attachable", "--label", fmt.Sprintf("com.dokku.network-name=%v", networkName), networkName}, + Args: []string{"network", "create", "--attachable", "--label", fmt.Sprintf("%s=%v", dokkuNetworkNameLabel, networkName), networkName}, }) if err != nil { return fmt.Errorf("Unable to create network: %w", err) @@ -103,18 +103,19 @@ func CommandInfo(networkName string, format string) error { return nil } - length := 10 + length := 16 common.LogInfo2Quiet(fmt.Sprintf("%s network information", networkName)) common.LogVerbose(fmt.Sprintf("%s%s", common.RightPad("ID:", length, " "), network.ID)) common.LogVerbose(fmt.Sprintf("%s%s", common.RightPad("Name:", length, " "), network.Name)) common.LogVerbose(fmt.Sprintf("%s%s", common.RightPad("Driver:", length, " "), network.Driver)) common.LogVerbose(fmt.Sprintf("%s%s", common.RightPad("Scope:", length, " "), network.Scope)) + common.LogVerbose(fmt.Sprintf("%s%v", common.RightPad("Dokku managed:", length, " "), network.DokkuManaged)) return nil } // CommandList is an alias for "docker network ls" -func CommandList(format string) error { +func CommandList(format string, dokkuManaged bool) error { networks, err := getNetworks() if err != nil { return err @@ -123,6 +124,9 @@ func CommandList(format string) error { if format == "json" { networkList := []DockerNetwork{} for _, network := range networks { + if dokkuManaged && !network.DokkuManaged { + continue + } networkList = append(networkList, network) } out, err := json.Marshal(networkList) @@ -135,6 +139,9 @@ func CommandList(format string) error { common.LogInfo2Quiet("Networks") for _, network := range networks { + if dokkuManaged && !network.DokkuManaged { + continue + } fmt.Println(network.Name) } diff --git a/tests/unit/network.bats b/tests/unit/network.bats index 3b0652124..449eefacc 100644 --- a/tests/unit/network.bats +++ b/tests/unit/network.bats @@ -14,6 +14,7 @@ teardown() { docker network rm create-network || true docker network rm deploy-network || true docker network rm initial-network || true + docker network rm "$TEST_NETWORK" || true global_teardown } @@ -441,6 +442,63 @@ teardown() { assert_failure } +@test "(network:list) --format json exposes DokkuManaged" { + run /bin/bash -c "dokku network:create $TEST_NETWORK" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku network:list --format json | jq -e ." + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku network:list --format json | jq -r '.[] | select(.Name == \"bridge\") | .DokkuManaged'" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "false" + + run /bin/bash -c "dokku network:list --format json | jq -r '.[] | select(.Name == \"$TEST_NETWORK\") | .DokkuManaged'" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "true" + + run /bin/bash -c "dokku --force network:destroy $TEST_NETWORK" + echo "output: $output" + echo "status: $status" + assert_success +} + +@test "(network:list) --dokku-managed filters to dokku-created networks" { + run /bin/bash -c "dokku network:create $TEST_NETWORK" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku network:list --dokku-managed | grep -w bridge" + echo "output: $output" + echo "status: $status" + assert_failure + + run /bin/bash -c "dokku network:list --dokku-managed | grep $TEST_NETWORK" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku network:list --dokku-managed --format json | jq -r '.[].DokkuManaged' | sort -u" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "true" + + run /bin/bash -c "dokku --force network:destroy $TEST_NETWORK" + echo "output: $output" + echo "status: $status" + assert_success +} + @test "(network) network:set attach" { run deploy_app echo "output: $output"