Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 30 additions & 10 deletions cns/NetworkContainerContract.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const (
DetachContainerFromNetwork = "/network/detachcontainerfromnetwork"
RequestIPConfig = "/network/requestipconfig"
ReleaseIPConfig = "/network/releaseipconfig"
GetIPAddresses = "/debug/getipaddresses"
)

// NetworkContainer Prefixes
Expand Down Expand Up @@ -197,16 +198,6 @@ type GetNetworkContainerResponse struct {
AllowNCToHostCommunication bool
}

type GetIPConfigRequest struct {
DesiredIPAddress string
OrchestratorContext json.RawMessage
}

type GetIPConfigResponse struct {
PodIpInfo PodIpInfo
Response Response
}

// DeleteNetworkContainerRequest specifies the details about the request to delete a specifc network container.
type PodIpInfo struct {
PodIPConfig IPSubnet
Expand All @@ -221,6 +212,35 @@ type HostIPInfo struct {
Subnet string
}

type IPConfigRequest struct {
DesiredIPAddress string
OrchestratorContext json.RawMessage
}

// IPConfigResponse is used in CNS IPAM mode as a response to CNI ADD
type IPConfigResponse struct {
PodIpInfo PodIpInfo
Response Response
}

// GetIPAddressesRequest is used in CNS IPAM mode to get the states of IPConfigs
// The IPConfigStateFilter is a slice of IP's to fetch from CNS that match those states
type GetIPAddressesRequest struct {
IPConfigStateFilter []string
}

// GetIPAddressStateResponse is used in CNS IPAM mode as a response to get IP address state
type GetIPAddressStateResponse struct {
IPAddresses []IPAddressState
Response Response
}

// IPAddressState Only used in the GetIPConfig API to return IP's that match a filter
type IPAddressState struct {
IPAddress string
State string
}

// DeleteNetworkContainerRequest specifies the details about the request to delete a specifc network container.
type DeleteNetworkContainerRequest struct {
NetworkContainerid string
Expand Down
89 changes: 89 additions & 0 deletions cns/cnsclient/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package cnsclient

import (
"fmt"
"sort"
"strings"

"github.com/Azure/azure-container-networking/cns"
)

const (
getCmdArg = "get"
getAvailableArg = "Available"
getAllocatedArg = "Allocated"
getAllArg = "All"
getPendingReleaseArg = "PendingRelease"

releaseArg = "release"

eth0InterfaceName = "eth0"
azure0InterfaceName = "azure0"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of these are unused, the rest can be consts


defaultCNSURl = "http://localhost:10090"
)

var (
availableCmds = []string{
getCmdArg,
}

getFlags = []string{
getAvailableArg,
getAllocatedArg,
getAllocatedArg,
}
)

func HandleCNSClientCommands(cmd, arg string) error {
cnsClient, err := InitCnsClient(defaultCNSURl)
if err != nil {
return err
}

switch {
case strings.EqualFold(getCmdArg, cmd):
return getCmd(cnsClient, arg)
default:
return fmt.Errorf("No debug cmd supplied, options are: %v", getCmdArg)
}
}

func getCmd(client *CNSClient, arg string) error {
var states []string

switch arg {
case cns.Available:
states = append(states, cns.Available)

case cns.Allocated:
states = append(states, cns.Allocated)

case cns.PendingRelease:
states = append(states, cns.PendingRelease)

default:
states = append(states, cns.Allocated)
states = append(states, cns.Available)
states = append(states, cns.PendingRelease)
}

addr, err := client.GetIPAddressesMatchingStates(states...)
if err != nil {
return err
}

printIPAddresses(addr)
return nil
}

// Sort the addresses based on IP, then write to stdout
func printIPAddresses(addrSlice []cns.IPAddressState) {
sort.Slice(addrSlice, func(i, j int) bool {
return addrSlice[i].IPAddress < addrSlice[j].IPAddress
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you only need this line, the rest of the block can be removed

})

for _, addr := range addrSlice {
fmt.Printf("%+v\n", addr)
}
}
63 changes: 59 additions & 4 deletions cns/cnsclient/cnsclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,11 @@ func (cnsClient *CNSClient) DeleteHostNCApipaEndpoint(networkContainerID string)
}

// RequestIPAddress calls the requestIPAddress in CNS
func (cnsClient *CNSClient) RequestIPAddress(orchestratorContext []byte) (*cns.GetIPConfigResponse, error) {
func (cnsClient *CNSClient) RequestIPAddress(orchestratorContext []byte) (*cns.IPConfigResponse, error) {
var (
err error
res *http.Response
response *cns.GetIPConfigResponse
response *cns.IPConfigResponse
)

defer func() {
Expand All @@ -227,7 +227,7 @@ func (cnsClient *CNSClient) RequestIPAddress(orchestratorContext []byte) (*cns.G
httpc := &http.Client{}
url := cnsClient.connectionURL + cns.RequestIPConfig

payload := &cns.GetIPConfigRequest{
payload := &cns.IPConfigRequest{
OrchestratorContext: orchestratorContext,
}

Expand Down Expand Up @@ -277,7 +277,7 @@ func (cnsClient *CNSClient) ReleaseIPAddress(orchestratorContext []byte) error {
url := cnsClient.connectionURL + cns.ReleaseIPConfig
log.Printf("ReleaseIPAddress url %v", url)

payload := &cns.GetIPConfigRequest{
payload := &cns.IPConfigRequest{
OrchestratorContext: orchestratorContext,
}

Expand Down Expand Up @@ -316,3 +316,58 @@ func (cnsClient *CNSClient) ReleaseIPAddress(orchestratorContext []byte) error {

return err
}

// GetIPAddressesWithStates takes a variadic number of string parameters, to get all IP Addresses matching a number of states
// usage GetIPAddressesWithStates(cns.Available, cns.Allocated)
func (cnsClient *CNSClient) GetIPAddressesMatchingStates(StateFilter ...string) ([]cns.IPAddressState, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing about variadic funcs is that it's possible for clients to pass no args. I'm curious if the behavior then should be to just no-op.

var (
resp cns.GetIPAddressStateResponse
err error
res *http.Response
body bytes.Buffer
)

if len(StateFilter) == 0 {
return []cns.IPAddressState{}, nil
}

url := cnsClient.connectionURL + cns.GetIPAddresses
log.Printf("GetIPAddressesMatchingStates url %v", url)

payload := &cns.GetIPAddressesRequest{
IPConfigStateFilter: StateFilter,
}

err = json.NewEncoder(&body).Encode(payload)
if err != nil {
log.Errorf("encoding json failed with %v", err)
return resp.IPAddresses, err
}

res, err = http.Post(url, contentTypeJSON, &body)
if err != nil {
log.Errorf("[Azure CNSClient] HTTP Post returned error %v", err.Error())
return resp.IPAddresses, err
}

defer res.Body.Close()

if res.StatusCode != http.StatusOK {
errMsg := fmt.Sprintf("[Azure CNSClient] GetIPAddressesMatchingStates invalid http status code: %v", res.StatusCode)
log.Errorf(errMsg)
return resp.IPAddresses, fmt.Errorf(errMsg)
}

err = json.NewDecoder(res.Body).Decode(&resp)
if err != nil {
log.Errorf("[Azure CNSClient] Error received while parsing GetIPAddressesMatchingStates response resp:%v err:%v", res.Body, err.Error())
return resp.IPAddresses, err
}

if resp.Response.ReturnCode != 0 {
log.Errorf("[Azure CNSClient] GetIPAddressesMatchingStates received error response :%v", resp.Response.Message)
return resp.IPAddresses, fmt.Errorf(resp.Response.Message)
}

return resp.IPAddresses, err
}
15 changes: 14 additions & 1 deletion cns/cnsclient/cnsclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func addTestStateToRestServer(t *testing.T, secondaryIps []string) {
}
}

func getIPNetFromResponse(resp *cns.GetIPConfigResponse) (net.IPNet, error) {
func getIPNetFromResponse(resp *cns.IPConfigResponse) (net.IPNet, error) {
var (
resultIPnet net.IPNet
err error
Expand Down Expand Up @@ -222,4 +222,17 @@ func TestCNSClientRequestAndRelease(t *testing.T) {
if err != nil {
t.Fatalf("Expected to not fail when releasing IP reservation found with context: %+v", err)
}

ipaddresses, err := cnsClient.GetIPAddressesMatchingStates(cns.Available)
if err != nil {
t.Fatalf("Get allocated IP addresses failed %+v", err)
}

if len(ipaddresses) != 1 {
t.Fatalf("Number of available IP addresses expected to be 1, actual %+v", ipaddresses)
}

if ipaddresses[0].IPAddress != desiredIpAddress && ipaddresses[0].State != cns.Available {
t.Fatalf("Available IP address does not match expected, address state: %+v", ipaddresses)
}
}
2 changes: 1 addition & 1 deletion cns/restserver/internalapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func (service *HTTPRestService) ReconcileNCState(ncRequest *cns.CreateNetworkCon
}
jsonContext, _ := json.Marshal(kubernetesPodInfo)

ipconfigRequest := cns.GetIPConfigRequest{
ipconfigRequest := cns.IPConfigRequest{
DesiredIPAddress: secIpConfig.IPAddress,
OrchestratorContext: jsonContext,
}
Expand Down
Loading