mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-02-06 19:39:29 +00:00
Merge pull request #197 from red-hat-storage/sync_us--devel
Syncing latest changes from devel for ceph-csi
This commit is contained in:
commit
cb3f4e33f1
@ -197,6 +197,11 @@ func (fs *Driver) setupCSIAddonsServer(conf *util.Config) error {
|
|||||||
is := casceph.NewIdentityServer(conf)
|
is := casceph.NewIdentityServer(conf)
|
||||||
fs.cas.RegisterService(is)
|
fs.cas.RegisterService(is)
|
||||||
|
|
||||||
|
if conf.IsControllerServer {
|
||||||
|
fcs := casceph.NewFenceControllerServer()
|
||||||
|
fs.cas.RegisterService(fcs)
|
||||||
|
}
|
||||||
|
|
||||||
// start the server, this does not block, it runs a new go-routine
|
// start the server, this does not block, it runs a new go-routine
|
||||||
err = fs.cas.Start()
|
err = fs.cas.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -77,6 +77,12 @@ func (is *IdentityServer) GetCapabilities(
|
|||||||
Type: identity.Capability_Service_CONTROLLER_SERVICE,
|
Type: identity.Capability_Service_CONTROLLER_SERVICE,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
}, &identity.Capability{
|
||||||
|
Type: &identity.Capability_NetworkFence_{
|
||||||
|
NetworkFence: &identity.Capability_NetworkFence{
|
||||||
|
Type: identity.Capability_NetworkFence_NETWORK_FENCE,
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
120
internal/csi-addons/cephfs/network_fence.go
Normal file
120
internal/csi-addons/cephfs/network_fence.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 The Ceph-CSI Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cephfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
nf "github.com/ceph/ceph-csi/internal/csi-addons/networkfence"
|
||||||
|
"github.com/ceph/ceph-csi/internal/util"
|
||||||
|
|
||||||
|
"github.com/csi-addons/spec/lib/go/fence"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FenceControllerServer struct of cephFS CSI driver with supported methods
|
||||||
|
// of CSI-Addons networkfence controller service spec.
|
||||||
|
type FenceControllerServer struct {
|
||||||
|
*fence.UnimplementedFenceControllerServer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFenceControllerServer creates a new FenceControllerServer which handles
|
||||||
|
// the FenceController Service requests from the CSI-Addons specification.
|
||||||
|
func NewFenceControllerServer() *FenceControllerServer {
|
||||||
|
return &FenceControllerServer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterService registers the FenceControllerServer's service
|
||||||
|
// with the gRPC server.
|
||||||
|
func (fcs *FenceControllerServer) RegisterService(server grpc.ServiceRegistrar) {
|
||||||
|
fence.RegisterFenceControllerServer(server, fcs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateFenceClusterNetworkReq checks the sanity of FenceClusterNetworkRequest.
|
||||||
|
func validateNetworkFenceReq(fenceClients []*fence.CIDR, options map[string]string) error {
|
||||||
|
if len(fenceClients) == 0 {
|
||||||
|
return errors.New("CIDR block cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if value, ok := options["clusterID"]; !ok || value == "" {
|
||||||
|
return errors.New("missing or empty clusterID")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FenceClusterNetwork blocks access to a CIDR block by creating a network fence.
|
||||||
|
// It evicts the IP addresses of clients, which are in CIDR block.
|
||||||
|
func (fcs *FenceControllerServer) FenceClusterNetwork(
|
||||||
|
ctx context.Context,
|
||||||
|
req *fence.FenceClusterNetworkRequest,
|
||||||
|
) (*fence.FenceClusterNetworkResponse, error) {
|
||||||
|
err := validateNetworkFenceReq(req.GetCidrs(), req.Parameters)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
cr, err := util.NewUserCredentials(req.GetSecrets())
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||||
|
}
|
||||||
|
defer cr.DeleteCredentials()
|
||||||
|
|
||||||
|
nwFence, err := nf.NewNetworkFence(ctx, cr, req.Cidrs, req.GetParameters())
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Error(codes.Internal, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = nwFence.AddClientEviction(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to fence CIDR block %q: %s", nwFence.Cidr, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return &fence.FenceClusterNetworkResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnfenceClusterNetwork unblocks the access to a CIDR block by removing the network fence.
|
||||||
|
func (fcs *FenceControllerServer) UnfenceClusterNetwork(
|
||||||
|
ctx context.Context,
|
||||||
|
req *fence.UnfenceClusterNetworkRequest,
|
||||||
|
) (*fence.UnfenceClusterNetworkResponse, error) {
|
||||||
|
err := validateNetworkFenceReq(req.GetCidrs(), req.Parameters)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
cr, err := util.NewUserCredentials(req.GetSecrets())
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||||
|
}
|
||||||
|
defer cr.DeleteCredentials()
|
||||||
|
|
||||||
|
nwFence, err := nf.NewNetworkFence(ctx, cr, req.Cidrs, req.GetParameters())
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Error(codes.Internal, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = nwFence.RemoveNetworkFence(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to unfence CIDR block %q: %s", nwFence.Cidr, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return &fence.UnfenceClusterNetworkResponse{}, nil
|
||||||
|
}
|
59
internal/csi-addons/cephfs/network_fence_test.go
Normal file
59
internal/csi-addons/cephfs/network_fence_test.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 The Ceph-CSI Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cephfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/csi-addons/spec/lib/go/fence"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestFenceClusterNetwork is a minimal test for the FenceClusterNetwork()
|
||||||
|
// procedure. During unit-testing, there is no Ceph cluster available, so
|
||||||
|
// actual operations can not be performed.
|
||||||
|
func TestFenceClusterNetwork(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
controller := NewFenceControllerServer()
|
||||||
|
|
||||||
|
req := &fence.FenceClusterNetworkRequest{
|
||||||
|
Parameters: map[string]string{},
|
||||||
|
Secrets: nil,
|
||||||
|
Cidrs: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := controller.FenceClusterNetwork(context.TODO(), req)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestUnfenceClusterNetwork is a minimal test for the UnfenceClusterNetwork()
|
||||||
|
// procedure. During unit-testing, there is no Ceph cluster available, so actual
|
||||||
|
// operations can not be performed.
|
||||||
|
func TestUnfenceClusterNetwork(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
controller := NewFenceControllerServer()
|
||||||
|
|
||||||
|
req := &fence.UnfenceClusterNetworkRequest{
|
||||||
|
Parameters: map[string]string{},
|
||||||
|
Secrets: nil,
|
||||||
|
Cidrs: nil,
|
||||||
|
}
|
||||||
|
_, err := controller.UnfenceClusterNetwork(context.TODO(), req)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
@ -1,9 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2022 The Ceph-CSI Authors.
|
Copyright 2023 The Ceph-CSI Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -15,10 +18,13 @@ package networkfence
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ceph/ceph-csi/internal/util"
|
"github.com/ceph/ceph-csi/internal/util"
|
||||||
"github.com/ceph/ceph-csi/internal/util/log"
|
"github.com/ceph/ceph-csi/internal/util/log"
|
||||||
@ -29,6 +35,8 @@ import (
|
|||||||
const (
|
const (
|
||||||
blocklistTime = "157784760"
|
blocklistTime = "157784760"
|
||||||
invalidCommandStr = "invalid command"
|
invalidCommandStr = "invalid command"
|
||||||
|
// we can always use mds rank 0, since all the clients have a session with rank-0.
|
||||||
|
mdsRank = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
// NetworkFence contains the CIDR blocks to be blocked.
|
// NetworkFence contains the CIDR blocks to be blocked.
|
||||||
@ -38,6 +46,11 @@ type NetworkFence struct {
|
|||||||
cr *util.Credentials
|
cr *util.Credentials
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// activeClient represents the structure of an active client.
|
||||||
|
type activeClient struct {
|
||||||
|
Inst string `json:"inst"`
|
||||||
|
}
|
||||||
|
|
||||||
// NewNetworkFence returns a networkFence struct object from the Network fence/unfence request.
|
// NewNetworkFence returns a networkFence struct object from the Network fence/unfence request.
|
||||||
func NewNetworkFence(
|
func NewNetworkFence(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
@ -132,6 +145,159 @@ func (nf *NetworkFence) AddNetworkFence(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (nf *NetworkFence) listActiveClients(ctx context.Context) ([]activeClient, error) {
|
||||||
|
arg := []string{
|
||||||
|
"--id", nf.cr.ID,
|
||||||
|
"--keyfile=" + nf.cr.KeyFile,
|
||||||
|
"-m", nf.Monitors,
|
||||||
|
}
|
||||||
|
// FIXME: replace the ceph command with go-ceph API in future
|
||||||
|
cmd := []string{"tell", fmt.Sprintf("mds.%d", mdsRank), "client", "ls"}
|
||||||
|
cmd = append(cmd, arg...)
|
||||||
|
stdout, stdErr, err := util.ExecCommandWithTimeout(ctx, 2*time.Minute, "ceph", cmd...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to list active clients: %w, stderr: %q", err, stdErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var activeClients []activeClient
|
||||||
|
if err := json.Unmarshal([]byte(stdout), &activeClients); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return activeClients, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nf *NetworkFence) evictCephFSClient(ctx context.Context, clientID int) error {
|
||||||
|
arg := []string{
|
||||||
|
"--id", nf.cr.ID,
|
||||||
|
"--keyfile=" + nf.cr.KeyFile,
|
||||||
|
"-m", nf.Monitors,
|
||||||
|
}
|
||||||
|
// FIXME: replace the ceph command with go-ceph API in future
|
||||||
|
cmd := []string{"tell", fmt.Sprintf("mds.%d", mdsRank), "client", "evict", fmt.Sprintf("id=%d", clientID)}
|
||||||
|
cmd = append(cmd, arg...)
|
||||||
|
_, stdErr, err := util.ExecCommandWithTimeout(ctx, 2*time.Minute, "ceph", cmd...)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to evict client %d: %w, stderr: %q", clientID, err, stdErr)
|
||||||
|
}
|
||||||
|
log.DebugLog(ctx, "client %s has been evicted from CephFS\n", clientID)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIPInCIDR(ctx context.Context, ip, cidr string) bool {
|
||||||
|
// Parse the CIDR block
|
||||||
|
_, ipCidr, err := net.ParseCIDR(cidr)
|
||||||
|
if err != nil {
|
||||||
|
log.ErrorLog(ctx, "error parsing CIDR block %s: %w\n", cidr, err)
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the IP address
|
||||||
|
ipAddress := net.ParseIP(ip)
|
||||||
|
if ipAddress == nil {
|
||||||
|
log.ErrorLog(ctx, "error parsing IP address %s\n", ip)
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the IP address is within the CIDR block
|
||||||
|
return ipCidr.Contains(ipAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *activeClient) fetchIP() (string, error) {
|
||||||
|
// example: "inst": "client.4305 172.21.9.34:0/422650892",
|
||||||
|
// then returning value will be 172.21.9.34
|
||||||
|
clientInfo := ac.Inst
|
||||||
|
parts := strings.Fields(clientInfo)
|
||||||
|
if len(parts) >= 2 {
|
||||||
|
ip := strings.Split(parts[1], ":")[0]
|
||||||
|
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("failed to extract IP address, incorrect format: %s", clientInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ac *activeClient) fetchID() (int, error) {
|
||||||
|
// example: "inst": "client.4305 172.21.9.34:0/422650892",
|
||||||
|
// then returning value will be 4305
|
||||||
|
clientInfo := ac.Inst
|
||||||
|
parts := strings.Fields(clientInfo)
|
||||||
|
if len(parts) >= 1 {
|
||||||
|
clientIDStr := strings.TrimPrefix(parts[0], "client.")
|
||||||
|
clientID, err := strconv.Atoi(clientIDStr)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to convert client ID to int: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, fmt.Errorf("failed to extract client ID, incorrect format: %s", clientInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddClientEviction blocks access for all the IPs in the CIDR block
|
||||||
|
// using client eviction.
|
||||||
|
// blocks the active clients listed in cidr, and the IPs
|
||||||
|
// for whom there is no active client present too.
|
||||||
|
func (nf *NetworkFence) AddClientEviction(ctx context.Context) error {
|
||||||
|
evictedIPs := make(map[string]bool)
|
||||||
|
// fetch active clients
|
||||||
|
activeClients, err := nf.listActiveClients(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// iterate through CIDR blocks and check if any active client matches
|
||||||
|
for _, cidr := range nf.Cidr {
|
||||||
|
for _, client := range activeClients {
|
||||||
|
clientIP, err := client.fetchIP()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error fetching client IP: %w", err)
|
||||||
|
}
|
||||||
|
// check if the clientIP is in the CIDR block
|
||||||
|
if isIPInCIDR(ctx, clientIP, cidr) {
|
||||||
|
clientID, err := client.fetchID()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error fetching client ID: %w", err)
|
||||||
|
}
|
||||||
|
// evict the client
|
||||||
|
err = nf.evictCephFSClient(ctx, clientID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error evicting client %d: %w", clientID, err)
|
||||||
|
}
|
||||||
|
log.DebugLog(ctx, "client %d has been evicted\n", clientID)
|
||||||
|
// add the CIDR to the list of blocklisted IPs
|
||||||
|
evictedIPs[clientIP] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// blocklist the IPs in CIDR without any active clients
|
||||||
|
for _, cidr := range nf.Cidr {
|
||||||
|
// check if the CIDR is evicted
|
||||||
|
// fetch the list of IPs from a CIDR block
|
||||||
|
hosts, err := getIPRange(cidr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to convert CIDR block %s to corresponding IP range: %w", cidr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add ceph blocklist for each IP in the range mentioned by the CIDR
|
||||||
|
for _, host := range hosts {
|
||||||
|
if evictedIPs[host] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = nf.addCephBlocklist(ctx, host, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// getIPRange returns a list of IPs from the IP range
|
// getIPRange returns a list of IPs from the IP range
|
||||||
// corresponding to a CIDR block.
|
// corresponding to a CIDR block.
|
||||||
func getIPRange(cidr string) ([]string, error) {
|
func getIPRange(cidr string) ([]string, error) {
|
||||||
@ -199,6 +365,13 @@ func (nf *NetworkFence) removeCephBlocklist(ctx context.Context, ip string, useR
|
|||||||
|
|
||||||
// RemoveNetworkFence unblocks access for all the IPs in the IP range mentioned via the CIDR block
|
// RemoveNetworkFence unblocks access for all the IPs in the IP range mentioned via the CIDR block
|
||||||
// using a network fence.
|
// using a network fence.
|
||||||
|
// Unfencing one of the protocols(CephFS or RBD) suggests the node is expected to be recovered, so
|
||||||
|
// both CephFS and RBD are expected to work again too.
|
||||||
|
// example:
|
||||||
|
// Create RBD NetworkFence CR for one IP 10.10.10.10
|
||||||
|
// Created CephFS NetworkFence CR for IP range but above IP comes in the Range
|
||||||
|
// Delete the CephFS Network Fence CR to unblocklist the IP
|
||||||
|
// So now the IP (10.10.10.10) is (un)blocklisted and can be used by both protocols.
|
||||||
func (nf *NetworkFence) RemoveNetworkFence(ctx context.Context) error {
|
func (nf *NetworkFence) RemoveNetworkFence(ctx context.Context) error {
|
||||||
hasBlocklistRangeSupport := true
|
hasBlocklistRangeSupport := true
|
||||||
// for each CIDR block, convert it into a range of IPs so as to undo blocklisting operation.
|
// for each CIDR block, convert it into a range of IPs so as to undo blocklisting operation.
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2022 The Ceph-CSI Authors.
|
Copyright 2023 The Ceph-CSI Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -51,3 +54,81 @@ func TestGetIPRange(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFetchIP(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
clientInfo string
|
||||||
|
expectedIP string
|
||||||
|
expectedErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
clientInfo: "client.4305 172.21.9.34:0/422650892",
|
||||||
|
expectedIP: "172.21.9.34",
|
||||||
|
expectedErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
clientInfo: "",
|
||||||
|
expectedIP: "",
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
ts := tt
|
||||||
|
|
||||||
|
t.Run(ts.clientInfo, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
client := activeClient{Inst: ts.clientInfo}
|
||||||
|
ip, actualErr := client.fetchIP()
|
||||||
|
|
||||||
|
if (actualErr != nil) != ts.expectedErr {
|
||||||
|
t.Errorf("expected error %v but got %v", ts.expectedErr, actualErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip != ts.expectedIP {
|
||||||
|
t.Errorf("expected IP %s but got %s", ts.expectedIP, ip)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFetchID(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
clientInfo string
|
||||||
|
expectedID int
|
||||||
|
expectedErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
clientInfo: "client.4305 172.21.9.34:0/422650892",
|
||||||
|
expectedID: 4305,
|
||||||
|
expectedErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
clientInfo: "",
|
||||||
|
expectedID: 0,
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
ts := tt
|
||||||
|
t.Run(ts.clientInfo, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ac := &activeClient{Inst: ts.clientInfo}
|
||||||
|
actualID, actualErr := ac.fetchID()
|
||||||
|
|
||||||
|
if (actualErr != nil) != ts.expectedErr {
|
||||||
|
t.Errorf("expected error %v but got %v", ts.expectedErr, actualErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if actualID != ts.expectedID {
|
||||||
|
t.Errorf("expected ID %d but got %d", ts.expectedID, actualID)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user