github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/controlapi/network.go (about) 1 package controlapi 2 3 import ( 4 "context" 5 "net" 6 7 "github.com/docker/docker/pkg/plugingetter" 8 "github.com/docker/libnetwork/driverapi" 9 "github.com/docker/libnetwork/ipamapi" 10 "github.com/docker/swarmkit/api" 11 "github.com/docker/swarmkit/identity" 12 "github.com/docker/swarmkit/manager/allocator" 13 "github.com/docker/swarmkit/manager/allocator/networkallocator" 14 "github.com/docker/swarmkit/manager/state/store" 15 "google.golang.org/grpc/codes" 16 "google.golang.org/grpc/status" 17 ) 18 19 func validateIPAMConfiguration(ipamConf *api.IPAMConfig) error { 20 if ipamConf == nil { 21 return status.Errorf(codes.InvalidArgument, "ipam configuration: cannot be empty") 22 } 23 24 _, subnet, err := net.ParseCIDR(ipamConf.Subnet) 25 if err != nil { 26 return status.Errorf(codes.InvalidArgument, "ipam configuration: invalid subnet %s", ipamConf.Subnet) 27 } 28 29 if ipamConf.Range != "" { 30 ip, _, err := net.ParseCIDR(ipamConf.Range) 31 if err != nil { 32 return status.Errorf(codes.InvalidArgument, "ipam configuration: invalid range %s", ipamConf.Range) 33 } 34 35 if !subnet.Contains(ip) { 36 return status.Errorf(codes.InvalidArgument, "ipam configuration: subnet %s does not contain range %s", ipamConf.Subnet, ipamConf.Range) 37 } 38 } 39 40 if ipamConf.Gateway != "" { 41 ip := net.ParseIP(ipamConf.Gateway) 42 if ip == nil { 43 return status.Errorf(codes.InvalidArgument, "ipam configuration: invalid gateway %s", ipamConf.Gateway) 44 } 45 46 if !subnet.Contains(ip) { 47 return status.Errorf(codes.InvalidArgument, "ipam configuration: subnet %s does not contain gateway %s", ipamConf.Subnet, ipamConf.Gateway) 48 } 49 } 50 51 return nil 52 } 53 54 func validateIPAM(ipam *api.IPAMOptions, pg plugingetter.PluginGetter) error { 55 if ipam == nil { 56 // It is ok to not specify any IPAM configurations. We 57 // will choose good defaults. 58 return nil 59 } 60 61 if err := validateDriver(ipam.Driver, pg, ipamapi.PluginEndpointType); err != nil { 62 return err 63 } 64 65 for _, ipamConf := range ipam.Configs { 66 if err := validateIPAMConfiguration(ipamConf); err != nil { 67 return err 68 } 69 } 70 71 return nil 72 } 73 74 func validateNetworkSpec(spec *api.NetworkSpec, pg plugingetter.PluginGetter) error { 75 if spec == nil { 76 return status.Errorf(codes.InvalidArgument, errInvalidArgument.Error()) 77 } 78 79 if spec.Ingress && spec.DriverConfig != nil && spec.DriverConfig.Name != "overlay" { 80 return status.Errorf(codes.Unimplemented, "only overlay driver is currently supported for ingress network") 81 } 82 83 if spec.Attachable && spec.Ingress { 84 return status.Errorf(codes.InvalidArgument, "ingress network cannot be attachable") 85 } 86 87 if err := validateAnnotations(spec.Annotations); err != nil { 88 return err 89 } 90 91 if _, ok := spec.Annotations.Labels[networkallocator.PredefinedLabel]; ok { 92 return status.Errorf(codes.PermissionDenied, "label %s is for internally created predefined networks and cannot be applied by users", 93 networkallocator.PredefinedLabel) 94 } 95 if err := validateDriver(spec.DriverConfig, pg, driverapi.NetworkPluginEndpointType); err != nil { 96 return err 97 } 98 99 return validateIPAM(spec.IPAM, pg) 100 } 101 102 // CreateNetwork creates and returns a Network based on the provided NetworkSpec. 103 // - Returns `InvalidArgument` if the NetworkSpec is malformed. 104 // - Returns an error if the creation fails. 105 func (s *Server) CreateNetwork(ctx context.Context, request *api.CreateNetworkRequest) (*api.CreateNetworkResponse, error) { 106 if err := validateNetworkSpec(request.Spec, s.pg); err != nil { 107 return nil, err 108 } 109 110 // TODO(mrjana): Consider using `Name` as a primary key to handle 111 // duplicate creations. See #65 112 n := &api.Network{ 113 ID: identity.NewID(), 114 Spec: *request.Spec, 115 } 116 117 err := s.store.Update(func(tx store.Tx) error { 118 if request.Spec.Ingress { 119 if n, err := allocator.GetIngressNetwork(s.store); err == nil { 120 return status.Errorf(codes.AlreadyExists, "ingress network (%s) is already present", n.ID) 121 } else if err != allocator.ErrNoIngress { 122 return status.Errorf(codes.Internal, "failed ingress network presence check: %v", err) 123 } 124 } 125 return store.CreateNetwork(tx, n) 126 }) 127 if err != nil { 128 return nil, err 129 } 130 131 return &api.CreateNetworkResponse{ 132 Network: n, 133 }, nil 134 } 135 136 // GetNetwork returns a Network given a NetworkID. 137 // - Returns `InvalidArgument` if NetworkID is not provided. 138 // - Returns `NotFound` if the Network is not found. 139 func (s *Server) GetNetwork(ctx context.Context, request *api.GetNetworkRequest) (*api.GetNetworkResponse, error) { 140 if request.NetworkID == "" { 141 return nil, status.Errorf(codes.InvalidArgument, errInvalidArgument.Error()) 142 } 143 144 var n *api.Network 145 s.store.View(func(tx store.ReadTx) { 146 n = store.GetNetwork(tx, request.NetworkID) 147 }) 148 if n == nil { 149 return nil, status.Errorf(codes.NotFound, "network %s not found", request.NetworkID) 150 } 151 return &api.GetNetworkResponse{ 152 Network: n, 153 }, nil 154 } 155 156 // RemoveNetwork removes a Network referenced by NetworkID. 157 // - Returns `InvalidArgument` if NetworkID is not provided. 158 // - Returns `NotFound` if the Network is not found. 159 // - Returns an error if the deletion fails. 160 func (s *Server) RemoveNetwork(ctx context.Context, request *api.RemoveNetworkRequest) (*api.RemoveNetworkResponse, error) { 161 if request.NetworkID == "" { 162 return nil, status.Errorf(codes.InvalidArgument, errInvalidArgument.Error()) 163 } 164 165 var ( 166 n *api.Network 167 rm = s.removeNetwork 168 ) 169 170 s.store.View(func(tx store.ReadTx) { 171 n = store.GetNetwork(tx, request.NetworkID) 172 }) 173 if n == nil { 174 return nil, status.Errorf(codes.NotFound, "network %s not found", request.NetworkID) 175 } 176 177 if allocator.IsIngressNetwork(n) { 178 rm = s.removeIngressNetwork 179 } 180 181 if v, ok := n.Spec.Annotations.Labels[networkallocator.PredefinedLabel]; ok && v == "true" { 182 return nil, status.Errorf(codes.FailedPrecondition, "network %s (%s) is a swarm predefined network and cannot be removed", 183 request.NetworkID, n.Spec.Annotations.Name) 184 } 185 186 if err := rm(n.ID); err != nil { 187 if err == store.ErrNotExist { 188 return nil, status.Errorf(codes.NotFound, "network %s not found", request.NetworkID) 189 } 190 return nil, err 191 } 192 return &api.RemoveNetworkResponse{}, nil 193 } 194 195 func (s *Server) removeNetwork(id string) error { 196 return s.store.Update(func(tx store.Tx) error { 197 services, err := store.FindServices(tx, store.ByReferencedNetworkID(id)) 198 if err != nil { 199 return status.Errorf(codes.Internal, "could not find services using network %s: %v", id, err) 200 } 201 202 if len(services) != 0 { 203 return status.Errorf(codes.FailedPrecondition, "network %s is in use by service %s", id, services[0].ID) 204 } 205 206 tasks, err := store.FindTasks(tx, store.ByReferencedNetworkID(id)) 207 if err != nil { 208 return status.Errorf(codes.Internal, "could not find tasks using network %s: %v", id, err) 209 } 210 211 for _, t := range tasks { 212 if t.DesiredState <= api.TaskStateRunning && t.Status.State <= api.TaskStateRunning { 213 return status.Errorf(codes.FailedPrecondition, "network %s is in use by task %s", id, t.ID) 214 } 215 } 216 217 return store.DeleteNetwork(tx, id) 218 }) 219 } 220 221 func (s *Server) removeIngressNetwork(id string) error { 222 return s.store.Update(func(tx store.Tx) error { 223 services, err := store.FindServices(tx, store.All) 224 if err != nil { 225 return status.Errorf(codes.Internal, "could not find services using network %s: %v", id, err) 226 } 227 for _, srv := range services { 228 if allocator.IsIngressNetworkNeeded(srv) { 229 return status.Errorf(codes.FailedPrecondition, "ingress network cannot be removed because service %s depends on it", srv.ID) 230 } 231 } 232 return store.DeleteNetwork(tx, id) 233 }) 234 } 235 236 func filterNetworks(candidates []*api.Network, filters ...func(*api.Network) bool) []*api.Network { 237 result := []*api.Network{} 238 239 for _, c := range candidates { 240 match := true 241 for _, f := range filters { 242 if !f(c) { 243 match = false 244 break 245 } 246 } 247 if match { 248 result = append(result, c) 249 } 250 } 251 252 return result 253 } 254 255 // ListNetworks returns a list of all networks. 256 func (s *Server) ListNetworks(ctx context.Context, request *api.ListNetworksRequest) (*api.ListNetworksResponse, error) { 257 var ( 258 networks []*api.Network 259 err error 260 ) 261 262 s.store.View(func(tx store.ReadTx) { 263 switch { 264 case request.Filters != nil && len(request.Filters.Names) > 0: 265 networks, err = store.FindNetworks(tx, buildFilters(store.ByName, request.Filters.Names)) 266 case request.Filters != nil && len(request.Filters.NamePrefixes) > 0: 267 networks, err = store.FindNetworks(tx, buildFilters(store.ByNamePrefix, request.Filters.NamePrefixes)) 268 case request.Filters != nil && len(request.Filters.IDPrefixes) > 0: 269 networks, err = store.FindNetworks(tx, buildFilters(store.ByIDPrefix, request.Filters.IDPrefixes)) 270 default: 271 networks, err = store.FindNetworks(tx, store.All) 272 } 273 }) 274 if err != nil { 275 return nil, err 276 } 277 278 if request.Filters != nil { 279 networks = filterNetworks(networks, 280 func(e *api.Network) bool { 281 return filterContains(e.Spec.Annotations.Name, request.Filters.Names) 282 }, 283 func(e *api.Network) bool { 284 return filterContainsPrefix(e.Spec.Annotations.Name, request.Filters.NamePrefixes) 285 }, 286 func(e *api.Network) bool { 287 return filterContainsPrefix(e.ID, request.Filters.IDPrefixes) 288 }, 289 func(e *api.Network) bool { 290 return filterMatchLabels(e.Spec.Annotations.Labels, request.Filters.Labels) 291 }, 292 ) 293 } 294 295 return &api.ListNetworksResponse{ 296 Networks: networks, 297 }, nil 298 }