Merge pull request #16732 from flouthoc/network-update

network: add support for `podman network update` and `--network-dns-server`
This commit is contained in:
OpenShift Merge Robot
2023-01-12 12:18:14 -05:00
committed by GitHub
16 changed files with 320 additions and 16 deletions

View File

@ -80,6 +80,9 @@ func networkCreateFlags(cmd *cobra.Command) {
flags.BoolVar(&networkCreateOptions.DisableDNS, "disable-dns", false, "disable dns plugin") flags.BoolVar(&networkCreateOptions.DisableDNS, "disable-dns", false, "disable dns plugin")
flags.BoolVar(&networkCreateOptions.IgnoreIfExists, "ignore", false, "Don't fail if network already exists") flags.BoolVar(&networkCreateOptions.IgnoreIfExists, "ignore", false, "Don't fail if network already exists")
dnsserverFlagName := "dns"
flags.StringArrayVar(&networkCreateOptions.NetworkDNSServers, dnsserverFlagName, nil, "DNS servers this network will use")
_ = cmd.RegisterFlagCompletionFunc(dnsserverFlagName, completion.AutocompleteNone)
} }
func init() { func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{ registry.Commands = append(registry.Commands, registry.CliCommand{
@ -107,13 +110,14 @@ func networkCreate(cmd *cobra.Command, args []string) error {
} }
network := types.Network{ network := types.Network{
Name: name, Name: name,
Driver: networkCreateOptions.Driver, Driver: networkCreateOptions.Driver,
Options: networkCreateOptions.Options, Options: networkCreateOptions.Options,
Labels: networkCreateOptions.Labels, Labels: networkCreateOptions.Labels,
IPv6Enabled: networkCreateOptions.IPv6, IPv6Enabled: networkCreateOptions.IPv6,
DNSEnabled: !networkCreateOptions.DisableDNS, DNSEnabled: !networkCreateOptions.DisableDNS,
Internal: networkCreateOptions.Internal, NetworkDNSServers: networkCreateOptions.NetworkDNSServers,
Internal: networkCreateOptions.Internal,
} }
if cmd.Flags().Changed(ipamDriverFlagName) { if cmd.Flags().Changed(ipamDriverFlagName) {

View File

@ -0,0 +1,57 @@
package network
import (
"fmt"
"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v4/cmd/podman/common"
"github.com/containers/podman/v4/cmd/podman/registry"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/spf13/cobra"
)
var (
networkUpdateDescription = `Update an existing podman network`
networkUpdateCommand = &cobra.Command{
Use: "update [options] NETWORK",
Short: "update an existing podman network",
Long: networkUpdateDescription,
RunE: networkUpdate,
Args: cobra.ExactArgs(1),
ValidArgsFunction: common.AutocompleteNetworks,
Example: `podman network update podman1`,
}
)
var (
networkUpdateOptions entities.NetworkUpdateOptions
)
func networkUpdateFlags(cmd *cobra.Command) {
flags := cmd.Flags()
addDNSServerFlagName := "dns-add"
flags.StringArrayVar(&networkUpdateOptions.AddDNSServers, addDNSServerFlagName, nil, "add network level nameservers")
removeDNSServerFlagName := "dns-drop"
flags.StringArrayVar(&networkUpdateOptions.RemoveDNSServers, removeDNSServerFlagName, nil, "remove network level nameservers")
_ = cmd.RegisterFlagCompletionFunc(addDNSServerFlagName, completion.AutocompleteNone)
_ = cmd.RegisterFlagCompletionFunc(removeDNSServerFlagName, completion.AutocompleteNone)
}
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: networkUpdateCommand,
Parent: networkCmd,
})
networkUpdateFlags(networkUpdateCommand)
}
func networkUpdate(cmd *cobra.Command, args []string) error {
name := args[0]
err := registry.ContainerEngine().NetworkUpdate(registry.Context(), name, networkUpdateOptions)
if err != nil {
return err
}
fmt.Println(name)
return nil
}

View File

@ -24,6 +24,10 @@ release because it is used as a special network mode in **podman run/create --ne
Disables the DNS plugin for this network which if enabled, can perform container to container name Disables the DNS plugin for this network which if enabled, can perform container to container name
resolution. resolution.
#### **--dns**=*ip*
Set network-scoped DNS resolver/nameserver for containers in this network. If not set, the host servers from `/etc/resolv.conf` will be used. It can be overwritten on the container level with the `podman run/create --dns` option. This option can be specified multiple times to set more than one IP.
#### **--driver**, **-d** #### **--driver**, **-d**
Driver to manage the network. Currently `bridge`, `macvlan` and `ipvlan` are supported. Defaults to `bridge`. Driver to manage the network. Currently `bridge`, `macvlan` and `ipvlan` are supported. Defaults to `bridge`.

View File

@ -0,0 +1,36 @@
% podman-network-update 1
## NAME
podman\-network\-update - Update an existing Podman network
## SYNOPSIS
**podman network update** [*options*] *network*
## DESCRIPTION
Allow changes to existing container networks. At present, only changes to the DNS servers in use by a network is supported.
NOTE: Only supported with the netavark network backend.
## OPTIONS
#### **--dns-add**
Accepts array of DNS resolvers and add it to the existing list of resolvers configured for a network.
#### **--dns-drop**
Accepts array of DNS resolvers and removes them from the existing list of resolvers configured for a network.
## EXAMPLE
Update a network
```
$ podman network update network1 --dns-add 8.8.8.8,1.1.1.1
```
Update a network and add/remove dns servers
```
$ podman network update network1 --dns-drop 8.8.8.8 --dns-add 3.3.3.3
```
## SEE ALSO
**[podman(1)](podman.1.md)**, **[podman-network(1)](podman-network.1.md)**, **[podman-network-inspect(1)](podman-network-inspect.1.md)**, **[podman-network-ls(1)](podman-network-ls.1.md)**

View File

@ -32,6 +32,7 @@ so networks have to be created again after a backend change.
| prune | [podman-network-prune(1)](podman-network-prune.1.md) | Remove all unused networks | | prune | [podman-network-prune(1)](podman-network-prune.1.md) | Remove all unused networks |
| reload | [podman-network-reload(1)](podman-network-reload.1.md) | Reload network configuration for containers | | reload | [podman-network-reload(1)](podman-network-reload.1.md) | Reload network configuration for containers |
| rm | [podman-network-rm(1)](podman-network-rm.1.md) | Remove one or more networks | | rm | [podman-network-rm(1)](podman-network-rm.1.md) | Remove one or more networks |
| update | [podman-network-upate(1)](podman-network-update.1.md) | Update an existing Podman network |
## SEE ALSO ## SEE ALSO
**[podman(1)](podman.1.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)** **[podman(1)](podman.1.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**

View File

@ -53,6 +53,26 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, report) utils.WriteResponse(w, http.StatusOK, report)
} }
func UpdateNetwork(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
ic := abi.ContainerEngine{Libpod: runtime}
networkUpdateOptions := entities.NetworkUpdateOptions{}
if err := json.NewDecoder(r.Body).Decode(&networkUpdateOptions); err != nil {
utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to decode request JSON payload: %w", err))
return
}
name := utils.GetName(r)
err := ic.NetworkUpdate(r.Context(), name, networkUpdateOptions)
if err != nil {
utils.Error(w, http.StatusInternalServerError, err)
}
utils.WriteResponse(w, http.StatusNoContent, nil)
}
func ListNetworks(w http.ResponseWriter, r *http.Request) { func ListNetworks(w http.ResponseWriter, r *http.Request) {
if v, err := utils.SupportedVersion(r, ">=4.0.0"); err != nil { if v, err := utils.SupportedVersion(r, ">=4.0.0"); err != nil {
utils.BadRequest(w, "version", v.String(), err) utils.BadRequest(w, "version", v.String(), err)

View File

@ -44,3 +44,7 @@ type networkDisconnectRequest types.NetworkDisconnect
// Network connect // Network connect
// swagger:model // swagger:model
type networkConnectRequestLibpod entities.NetworkConnectOptions type networkConnectRequestLibpod entities.NetworkConnectOptions
// Network update
// swagger:model
type networkUpdateRequestLibpod entities.NetworkUpdateOptions

View File

@ -234,6 +234,33 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// 500: // 500:
// $ref: "#/responses/internalError" // $ref: "#/responses/internalError"
r.HandleFunc(VersionedPath("/libpod/networks/{name}"), s.APIHandler(libpod.RemoveNetwork)).Methods(http.MethodDelete) r.HandleFunc(VersionedPath("/libpod/networks/{name}"), s.APIHandler(libpod.RemoveNetwork)).Methods(http.MethodDelete)
// swagger:operation POST /libpod/networks/{name}/update libpod NetworkUpdateLibpod
// ---
// tags:
// - networks
// summary: Update exisiting podman network
// description: Update exisiting podman network
// produces:
// - application/json
// parameters:
// - in: path
// name: name
// type: string
// required: true
// description: the name or ID of the network
// - in: body
// name: update
// description: attributes for updating a netavark network
// schema:
// $ref: "#/definitions/networkUpdateRequestLibpod"
// responses:
// 200:
// description: OK
// 400:
// $ref: "#/responses/badParamError"
// 500:
// $ref: "#/responses/internalError"
r.HandleFunc(VersionedPath("/libpod/networks/{name}/update"), s.APIHandler(libpod.UpdateNetwork)).Methods(http.MethodPost)
// swagger:operation GET /libpod/networks/{name}/exists libpod NetworkExistsLibpod // swagger:operation GET /libpod/networks/{name}/exists libpod NetworkExistsLibpod
// --- // ---
// tags: // tags:

View File

@ -50,6 +50,25 @@ func CreateWithOptions(ctx context.Context, network *types.Network, extraCreateO
return report, response.Process(&report) return report, response.Process(&report)
} }
// Updates an existing netavark network config
func Update(ctx context.Context, netNameOrID string, options *UpdateOptions) error {
conn, err := bindings.GetClient(ctx)
if err != nil {
return err
}
networkConfig, err := jsoniter.MarshalToString(options)
if err != nil {
return err
}
reader := strings.NewReader(networkConfig)
response, err := conn.DoRequest(ctx, reader, http.MethodPost, "/networks/%s/update", nil, nil, netNameOrID)
if err != nil {
return err
}
defer response.Body.Close()
return response.Process(nil)
}
// Inspect returns information about a network configuration // Inspect returns information about a network configuration
func Inspect(ctx context.Context, nameOrID string, _ *InspectOptions) (types.Network, error) { func Inspect(ctx context.Context, nameOrID string, _ *InspectOptions) (types.Network, error) {
var net types.Network var net types.Network

View File

@ -58,6 +58,14 @@ type ListOptions struct {
Filters map[string][]string Filters map[string][]string
} }
// NetworkUpdateOptions describes options to update a network
//
//go:generate go run ../generator/generator.go UpdateOptions
type UpdateOptions struct {
AddDNSServers []string `json:"adddnsservers"`
RemoveDNSServers []string `json:"removednsservers"`
}
// DisconnectOptions are optional options for disconnecting // DisconnectOptions are optional options for disconnecting
// containers from a network // containers from a network
// //

View File

@ -0,0 +1,48 @@
// Code generated by go generate; DO NOT EDIT.
package network
import (
"net/url"
"github.com/containers/podman/v4/pkg/bindings/internal/util"
)
// Changed returns true if named field has been set
func (o *UpdateOptions) Changed(fieldName string) bool {
return util.Changed(o, fieldName)
}
// ToParams formats struct fields to be passed to API service
func (o *UpdateOptions) ToParams() (url.Values, error) {
return util.ToParams(o)
}
// WithAddDNSServers set field AddDNSServers to given value
func (o *UpdateOptions) WithAddDNSServers(value []string) *UpdateOptions {
o.AddDNSServers = value
return o
}
// GetAddDNSServers returns value of field AddDNSServers
func (o *UpdateOptions) GetAddDNSServers() []string {
if o.AddDNSServers == nil {
var z []string
return z
}
return o.AddDNSServers
}
// WithRemoveDNSServers set field RemoveDNSServers to given value
func (o *UpdateOptions) WithRemoveDNSServers(value []string) *UpdateOptions {
o.RemoveDNSServers = value
return o
}
// GetRemoveDNSServers returns value of field RemoveDNSServers
func (o *UpdateOptions) GetRemoveDNSServers() []string {
if o.RemoveDNSServers == nil {
var z []string
return z
}
return o.RemoveDNSServers
}

View File

@ -64,6 +64,7 @@ type ContainerEngine interface { //nolint:interfacebloat
KubeApply(ctx context.Context, body io.Reader, opts ApplyOptions) error KubeApply(ctx context.Context, body io.Reader, opts ApplyOptions) error
NetworkConnect(ctx context.Context, networkname string, options NetworkConnectOptions) error NetworkConnect(ctx context.Context, networkname string, options NetworkConnectOptions) error
NetworkCreate(ctx context.Context, network types.Network, createOptions *types.NetworkCreateOptions) (*types.Network, error) NetworkCreate(ctx context.Context, network types.Network, createOptions *types.NetworkCreateOptions) (*types.Network, error)
NetworkUpdate(ctx context.Context, networkname string, options NetworkUpdateOptions) error
NetworkDisconnect(ctx context.Context, networkname string, options NetworkDisconnectOptions) error NetworkDisconnect(ctx context.Context, networkname string, options NetworkDisconnectOptions) error
NetworkExists(ctx context.Context, networkname string) (*BoolReport, error) NetworkExists(ctx context.Context, networkname string) (*BoolReport, error)
NetworkInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]types.Network, []error, error) NetworkInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]types.Network, []error, error)

View File

@ -41,21 +41,28 @@ type NetworkRmReport struct {
// NetworkCreateOptions describes options to create a network // NetworkCreateOptions describes options to create a network
type NetworkCreateOptions struct { type NetworkCreateOptions struct {
DisableDNS bool DisableDNS bool
Driver string Driver string
Gateways []net.IP Gateways []net.IP
Internal bool Internal bool
Labels map[string]string Labels map[string]string
MacVLAN string MacVLAN string
Ranges []string NetworkDNSServers []string
Subnets []string Ranges []string
IPv6 bool Subnets []string
IPv6 bool
// Mapping of driver options and values. // Mapping of driver options and values.
Options map[string]string Options map[string]string
// IgnoreIfExists if true, do not fail if the network already exists // IgnoreIfExists if true, do not fail if the network already exists
IgnoreIfExists bool IgnoreIfExists bool
} }
// NetworkUpdateOptions describes options to update a network
type NetworkUpdateOptions struct {
AddDNSServers []string `json:"adddnsservers"`
RemoveDNSServers []string `json:"removednsservers"`
}
// NetworkCreateReport describes a created network for the cli // NetworkCreateReport describes a created network for the cli
type NetworkCreateReport struct { type NetworkCreateReport struct {
Name string Name string

View File

@ -13,6 +13,17 @@ import (
"github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/entities"
) )
func (ic *ContainerEngine) NetworkUpdate(ctx context.Context, netName string, options entities.NetworkUpdateOptions) error {
var networkUpdateOptions types.NetworkUpdateOptions
networkUpdateOptions.AddDNSServers = options.AddDNSServers
networkUpdateOptions.RemoveDNSServers = options.RemoveDNSServers
err := ic.Libpod.Network().NetworkUpdate(netName, networkUpdateOptions)
if err != nil {
return err
}
return nil
}
func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]types.Network, error) { func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]types.Network, error) {
// dangling filter is not provided by netutil // dangling filter is not provided by netutil
var wantDangling bool var wantDangling bool

View File

@ -12,6 +12,11 @@ import (
"github.com/containers/podman/v4/pkg/errorhandling" "github.com/containers/podman/v4/pkg/errorhandling"
) )
func (ic *ContainerEngine) NetworkUpdate(ctx context.Context, netName string, opts entities.NetworkUpdateOptions) error {
options := new(network.UpdateOptions).WithAddDNSServers(opts.AddDNSServers).WithRemoveDNSServers(opts.RemoveDNSServers)
return network.Update(ic.ClientCtx, netName, options)
}
func (ic *ContainerEngine) NetworkList(ctx context.Context, opts entities.NetworkListOptions) ([]types.Network, error) { func (ic *ContainerEngine) NetworkList(ctx context.Context, opts entities.NetworkListOptions) ([]types.Network, error) {
options := new(network.ListOptions).WithFilters(opts.Filters) options := new(network.ListOptions).WithFilters(opts.Filters)
return network.List(ic.ClientCtx, options) return network.List(ic.ClientCtx, options)

View File

@ -1,6 +1,7 @@
package integration package integration
import ( import (
"encoding/json"
"fmt" "fmt"
"net" "net"
"os" "os"
@ -8,6 +9,7 @@ import (
"syscall" "syscall"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containers/common/libnetwork/types"
. "github.com/containers/podman/v4/test/utils" . "github.com/containers/podman/v4/test/utils"
"github.com/containers/storage/pkg/stringid" "github.com/containers/storage/pkg/stringid"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
@ -41,6 +43,56 @@ var _ = Describe("Podman run networking", func() {
}) })
It("podman verify network scoped DNS server and also verify updating network dns server", func() {
// TODO: Unskip after https://github.com/containers/podman/pull/16525
Skip("TODO: unskip after https://github.com/containers/podman/pull/16525")
// Following test is only functional with netavark and aardvark
SkipIfCNI(podmanTest)
net := createNetworkName("IntTest")
session := podmanTest.Podman([]string{"network", "create", net, "--dns", "1.1.1.1"})
session.WaitWithDefaultTimeout()
defer podmanTest.removeNetwork(net)
Expect(session).Should(Exit(0))
session = podmanTest.Podman([]string{"network", "inspect", net})
session.WaitWithDefaultTimeout()
defer podmanTest.removeNetwork(net)
var results []types.Network
err := json.Unmarshal([]byte(session.OutputToString()), &results)
Expect(err).ToNot(HaveOccurred())
Expect(results).To(HaveLen(1))
result := results[0]
Expect(result.Subnets).To(HaveLen(1))
aardvarkDNSGateway := result.Subnets[0].Gateway.String()
Expect(session.OutputToString()).To(ContainSubstring("1.1.1.1"))
Expect(session).Should(Exit(0))
session = podmanTest.Podman([]string{"run", "-d", "--name", "con1", "--network", net, "busybox", "top"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
session = podmanTest.Podman([]string{"exec", "-i", "con1", "nslookup", "google.com", aardvarkDNSGateway})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(ContainSubstring("Non-authoritative answer: Name: google.com Address:"))
// Update to a bad DNS Server
session = podmanTest.Podman([]string{"network", "update", net, "--dns-add", "7.7.7.7"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
// Remove good DNS server
session = podmanTest.Podman([]string{"network", "update", net, "--dns-drop=1.1.1.1"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
session = podmanTest.Podman([]string{"exec", "-i", "con1", "nslookup", "google.com", aardvarkDNSGateway})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(1))
Expect(session.OutputToString()).To(ContainSubstring(";; connection timed out; no servers could be reached"))
})
It("podman run network connection with default bridge", func() { It("podman run network connection with default bridge", func() {
session := podmanTest.RunContainerWithNetworkTest("") session := podmanTest.RunContainerWithNetworkTest("")
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()