mirror of
https://github.com/containers/podman.git
synced 2025-10-20 04:34:01 +08:00
Merge pull request #16773 from ygalblum/network_ignore
Network Create: Add --ignore flag to support idempotent script
This commit is contained in:
@ -78,6 +78,8 @@ func networkCreateFlags(cmd *cobra.Command) {
|
|||||||
_ = cmd.RegisterFlagCompletionFunc(subnetFlagName, completion.AutocompleteNone)
|
_ = cmd.RegisterFlagCompletionFunc(subnetFlagName, completion.AutocompleteNone)
|
||||||
|
|
||||||
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")
|
||||||
}
|
}
|
||||||
func init() {
|
func init() {
|
||||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
@ -165,7 +167,11 @@ func networkCreate(cmd *cobra.Command, args []string) error {
|
|||||||
return errors.New("cannot set gateway or range without subnet")
|
return errors.New("cannot set gateway or range without subnet")
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := registry.ContainerEngine().NetworkCreate(registry.Context(), network)
|
extraCreateOptions := types.NetworkCreateOptions{
|
||||||
|
IgnoreIfExists: networkCreateOptions.IgnoreIfExists,
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := registry.ContainerEngine().NetworkCreate(registry.Context(), network, &extraCreateOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,10 @@ Define a gateway for the subnet. To provide a gateway address, a
|
|||||||
*subnet* option is required. Can be specified multiple times.
|
*subnet* option is required. Can be specified multiple times.
|
||||||
The argument order of the **--subnet**, **--gateway** and **--ip-range** options must match.
|
The argument order of the **--subnet**, **--gateway** and **--ip-range** options must match.
|
||||||
|
|
||||||
|
#### **--ignore**
|
||||||
|
Ignore the create request if a network with the same name already exists instead of failing.
|
||||||
|
Note, trying to create a network with an existing name and different parameters, will not change the configuration of the existing one
|
||||||
|
|
||||||
#### **--internal**
|
#### **--internal**
|
||||||
|
|
||||||
Restrict external access of this network. Note when using this option, the dnsname plugin will be
|
Restrict external access of this network. Note when using this option, the dnsname plugin will be
|
||||||
|
@ -278,7 +278,7 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ic := abi.ContainerEngine{Libpod: runtime}
|
ic := abi.ContainerEngine{Libpod: runtime}
|
||||||
newNetwork, err := ic.NetworkCreate(r.Context(), network)
|
newNetwork, err := ic.NetworkCreate(r.Context(), network, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.InternalServerError(w, err)
|
utils.InternalServerError(w, err)
|
||||||
return
|
return
|
||||||
|
@ -31,8 +31,17 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query := struct {
|
||||||
|
IgnoreIfExists bool `schema:"ignoreIfExists"`
|
||||||
|
}{}
|
||||||
|
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
|
||||||
|
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||||
|
utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ic := abi.ContainerEngine{Libpod: runtime}
|
ic := abi.ContainerEngine{Libpod: runtime}
|
||||||
report, err := ic.NetworkCreate(r.Context(), network)
|
report, err := ic.NetworkCreate(r.Context(), network, &types.NetworkCreateOptions{IgnoreIfExists: query.IgnoreIfExists})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, types.ErrNetworkExists) {
|
if errors.Is(err, types.ErrNetworkExists) {
|
||||||
utils.Error(w, http.StatusConflict, err)
|
utils.Error(w, http.StatusConflict, err)
|
||||||
|
@ -3,6 +3,7 @@ package network
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/common/libnetwork/types"
|
"github.com/containers/common/libnetwork/types"
|
||||||
@ -13,11 +14,24 @@ import (
|
|||||||
|
|
||||||
// Create makes a new CNI network configuration
|
// Create makes a new CNI network configuration
|
||||||
func Create(ctx context.Context, network *types.Network) (types.Network, error) {
|
func Create(ctx context.Context, network *types.Network) (types.Network, error) {
|
||||||
|
return CreateWithOptions(ctx, network, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateWithOptions(ctx context.Context, network *types.Network, extraCreateOptions *ExtraCreateOptions) (types.Network, error) {
|
||||||
var report types.Network
|
var report types.Network
|
||||||
conn, err := bindings.GetClient(ctx)
|
conn, err := bindings.GetClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return report, err
|
return report, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var params url.Values
|
||||||
|
if extraCreateOptions != nil {
|
||||||
|
params, err = extraCreateOptions.ToParams()
|
||||||
|
if err != nil {
|
||||||
|
return report, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// create empty network if the caller did not provide one
|
// create empty network if the caller did not provide one
|
||||||
if network == nil {
|
if network == nil {
|
||||||
network = &types.Network{}
|
network = &types.Network{}
|
||||||
@ -27,7 +41,7 @@ func Create(ctx context.Context, network *types.Network) (types.Network, error)
|
|||||||
return report, err
|
return report, err
|
||||||
}
|
}
|
||||||
reader := strings.NewReader(networkConfig)
|
reader := strings.NewReader(networkConfig)
|
||||||
response, err := conn.DoRequest(ctx, reader, http.MethodPost, "/networks/create", nil, nil)
|
response, err := conn.DoRequest(ctx, reader, http.MethodPost, "/networks/create", params, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return report, err
|
return report, err
|
||||||
}
|
}
|
||||||
|
@ -84,3 +84,12 @@ type PruneOptions struct {
|
|||||||
// specific on choosing
|
// specific on choosing
|
||||||
Filters map[string][]string
|
Filters map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExtraCreateOptions are optional additional configuration flags for creating Networks
|
||||||
|
// that are not part of the network configuration
|
||||||
|
//
|
||||||
|
//go:generate go run ../generator/generator.go ExtraCreateOptions
|
||||||
|
type ExtraCreateOptions struct {
|
||||||
|
// IgnoreIfExists if true, do not fail if the network already exists
|
||||||
|
IgnoreIfExists *bool `schema:"ignoreIfExists"`
|
||||||
|
}
|
||||||
|
33
pkg/bindings/network/types_extracreate_options.go
Normal file
33
pkg/bindings/network/types_extracreate_options.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// 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 *ExtraCreateOptions) Changed(fieldName string) bool {
|
||||||
|
return util.Changed(o, fieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToParams formats struct fields to be passed to API service
|
||||||
|
func (o *ExtraCreateOptions) ToParams() (url.Values, error) {
|
||||||
|
return util.ToParams(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithIgnoreIfExists set field IgnoreIfExists to given value
|
||||||
|
func (o *ExtraCreateOptions) WithIgnoreIfExists(value bool) *ExtraCreateOptions {
|
||||||
|
o.IgnoreIfExists = &value
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIgnoreIfExists returns value of field IgnoreIfExists
|
||||||
|
func (o *ExtraCreateOptions) GetIgnoreIfExists() bool {
|
||||||
|
if o.IgnoreIfExists == nil {
|
||||||
|
var z bool
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
return *o.IgnoreIfExists
|
||||||
|
}
|
@ -101,11 +101,24 @@ var _ = Describe("Podman networks", func() {
|
|||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(report.Name).To(Equal(name))
|
Expect(report.Name).To(Equal(name))
|
||||||
|
|
||||||
// create network with same name should 500
|
// create network with same name should 409
|
||||||
_, err = network.Create(connText, &net)
|
_, err = network.Create(connText, &net)
|
||||||
Expect(err).To(HaveOccurred())
|
Expect(err).To(HaveOccurred())
|
||||||
code, _ := bindings.CheckResponseCode(err)
|
code, _ := bindings.CheckResponseCode(err)
|
||||||
Expect(code).To(BeNumerically("==", http.StatusConflict))
|
Expect(code).To(BeNumerically("==", http.StatusConflict))
|
||||||
|
|
||||||
|
// create network with same name and ignore false should 409
|
||||||
|
options := new(network.ExtraCreateOptions).WithIgnoreIfExists(false)
|
||||||
|
_, err = network.CreateWithOptions(connText, &net, options)
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
code, _ = bindings.CheckResponseCode(err)
|
||||||
|
Expect(code).To(BeNumerically("==", http.StatusConflict))
|
||||||
|
|
||||||
|
// create network with same name and ignore true succeed
|
||||||
|
options = new(network.ExtraCreateOptions).WithIgnoreIfExists(true)
|
||||||
|
report, err = network.CreateWithOptions(connText, &net, options)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(report.Name).To(Equal(name))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("inspect network", func() {
|
It("inspect network", func() {
|
||||||
|
@ -63,7 +63,7 @@ type ContainerEngine interface { //nolint:interfacebloat
|
|||||||
Info(ctx context.Context) (*define.Info, error)
|
Info(ctx context.Context) (*define.Info, error)
|
||||||
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) (*types.Network, error)
|
NetworkCreate(ctx context.Context, network types.Network, createOptions *types.NetworkCreateOptions) (*types.Network, 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)
|
||||||
|
@ -52,6 +52,8 @@ type NetworkCreateOptions struct {
|
|||||||
IPv6 bool
|
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 bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkCreateReport describes a created network for the cli
|
// NetworkCreateReport describes a created network for the cli
|
||||||
|
@ -138,12 +138,12 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o
|
|||||||
return reports, nil
|
return reports, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) NetworkCreate(ctx context.Context, network types.Network) (*types.Network, error) {
|
func (ic *ContainerEngine) NetworkCreate(ctx context.Context, network types.Network, createOptions *types.NetworkCreateOptions) (*types.Network, error) {
|
||||||
// TODO (5.0): Stop accepting "pasta" as value here
|
// TODO (5.0): Stop accepting "pasta" as value here
|
||||||
if util.StringInSlice(network.Name, []string{"none", "host", "bridge", "private", "slirp4netns", "container", "ns"}) {
|
if util.StringInSlice(network.Name, []string{"none", "host", "bridge", "private", "slirp4netns", "container", "ns"}) {
|
||||||
return nil, fmt.Errorf("cannot create network with name %q because it conflicts with a valid network mode", network.Name)
|
return nil, fmt.Errorf("cannot create network with name %q because it conflicts with a valid network mode", network.Name)
|
||||||
}
|
}
|
||||||
network, err := ic.Libpod.Network().NetworkCreate(network, nil)
|
network, err := ic.Libpod.Network().NetworkCreate(network, createOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -122,12 +122,16 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options
|
|||||||
// when no network options are specified, create a common network for all the pods
|
// when no network options are specified, create a common network for all the pods
|
||||||
if len(options.Networks) == 0 {
|
if len(options.Networks) == 0 {
|
||||||
_, err := ic.NetworkCreate(
|
_, err := ic.NetworkCreate(
|
||||||
ctx, nettypes.Network{
|
ctx,
|
||||||
|
nettypes.Network{
|
||||||
Name: kubeDefaultNetwork,
|
Name: kubeDefaultNetwork,
|
||||||
DNSEnabled: true,
|
DNSEnabled: true,
|
||||||
},
|
},
|
||||||
|
&nettypes.NetworkCreateOptions{
|
||||||
|
IgnoreIfExists: true,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
if err != nil && !errors.Is(err, nettypes.ErrNetworkExists) {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,8 +66,12 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o
|
|||||||
return reports, nil
|
return reports, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) NetworkCreate(ctx context.Context, net types.Network) (*types.Network, error) {
|
func (ic *ContainerEngine) NetworkCreate(ctx context.Context, net types.Network, createOptions *types.NetworkCreateOptions) (*types.Network, error) {
|
||||||
net, err := network.Create(ic.ClientCtx, &net)
|
options := new(network.ExtraCreateOptions)
|
||||||
|
if createOptions != nil {
|
||||||
|
options = options.WithIgnoreIfExists(createOptions.IgnoreIfExists)
|
||||||
|
}
|
||||||
|
net, err := network.CreateWithOptions(ic.ClientCtx, &net, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -452,4 +452,33 @@ var _ = Describe("Podman network create", func() {
|
|||||||
Expect(nc).To(Exit(125))
|
Expect(nc).To(Exit(125))
|
||||||
Expect(nc.ErrorToString()).To(Equal("Error: cannot set more ranges than subnets"))
|
Expect(nc.ErrorToString()).To(Equal("Error: cannot set more ranges than subnets"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("podman network create same name - fail", func() {
|
||||||
|
name := "same-name-" + stringid.GenerateRandomID()
|
||||||
|
networkCreateCommand := []string{"network", "create", name}
|
||||||
|
nc := podmanTest.Podman(networkCreateCommand)
|
||||||
|
nc.WaitWithDefaultTimeout()
|
||||||
|
defer podmanTest.removeNetwork(name)
|
||||||
|
Expect(nc).To(Exit(0))
|
||||||
|
Expect(nc.OutputToString()).To(Equal(name))
|
||||||
|
|
||||||
|
nc = podmanTest.Podman(networkCreateCommand)
|
||||||
|
nc.WaitWithDefaultTimeout()
|
||||||
|
Expect(nc).To(Exit(125))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman network create same name - succeed with ignore", func() {
|
||||||
|
name := "same-name-" + stringid.GenerateRandomID()
|
||||||
|
networkCreateCommand := []string{"network", "create", "--ignore", name}
|
||||||
|
nc := podmanTest.Podman(networkCreateCommand)
|
||||||
|
nc.WaitWithDefaultTimeout()
|
||||||
|
defer podmanTest.removeNetwork(name)
|
||||||
|
Expect(nc).To(Exit(0))
|
||||||
|
Expect(nc.OutputToString()).To(Equal(name))
|
||||||
|
|
||||||
|
nc = podmanTest.Podman(networkCreateCommand)
|
||||||
|
nc.WaitWithDefaultTimeout()
|
||||||
|
Expect(nc).To(Exit(0))
|
||||||
|
Expect(nc.OutputToString()).To(Equal(name))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user