Help Center> Cloud Container Instance> SDK Reference> Using client-go to Access the CRD Resource Network of CCI
Updated on 2023-02-09 GMT+08:00

Using client-go to Access the CRD Resource Network of CCI

Initializing a Project

Create a project named examples.com/cci-examples.

The project depends on k8s.io/client-go and k8s.io/code-generator of the following versions (for reference only):

  • k8s.io/client-go@kubernetes-1.15.0, k8s.io/code-generator@kubernetes-1.15.0
  • k8s.io/client-go@kubernetes-1.16.0, k8s.io/code-generator@kubernetes-1.16.0
  • k8s.io/client-go@kubernetes-1.17.0, k8s.io/code-generator@kubernetes-1.17.0
  • k8s.io/client-go@kubernetes-1.18.0, k8s.io/code-generator@kubernetes-1.18.0
  • k8s.io/client-go@kubernetes-1.19.0, k8s.io/code-generator@kubernetes-1.19.0
  • k8s.io/client-go@kubernetes-1.20.0, k8s.io/code-generator@kubernetes-1.20.0
mkdir -p examples.com/cci-examples
cd examples.com/cci-examples/
go mod init examples.com/cci-examples
go get k8s.io/client-go@kubernetes-1.15.0
go get k8s.io/code-generator@kubernetes-1.15.0

Defining the CRD Resource Network

Create the pkg/apis/networking.cci.io/v1beta1 folder, where networking.cci.io indicates the group of the CRD resource and v1beta1 indicates the version of the CRD resource.

mkdir -p pkg/apis/networking.cci.io/v1beta1

Create the following files in the new folder:

doc.go

// +k8s:deepcopy-gen=package
// +groupName=networking.cci.io
// +groupGoName=NetworkingCCI
package v1beta1

types.go

package v1beta1

import (
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// NetworkList is a list of network resource in container.
type NetworkList struct {
	metav1.TypeMeta `json:",inline"`
	// Standard list metadata.
	// More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
	// +optional
	metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
	Items           []Network `json:"items" protobuf:"bytes,2,rep,name=items"`
}

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// Network is a network resource in container.
type Network struct {
	metav1.TypeMeta `json:",inline"`
	// +optional
	metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
	// Spec defines the attributes on a network
	// +optional
	Spec NetworkSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
	// Status describes the network status
	// +optional
	Status NetworkStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}

// NetworkSpec describes the attributes on a network resource.
type NetworkSpec struct {
	// network type
	NetworkType string `json:"networkType,omitempty" protobuf:"bytes,5,opt,name=networkType"`
	// ID of the VPC to attach
	AttachedVPC string `json:"attachedVPC,omitempty" protobuf:"bytes,4,opt,name=attachedVPC"`
	// network ID
	NetworkID string `json:"networkID,omitempty" protobuf:"bytes,7,opt,name=networkID"`
	// Subnet ID
	SubnetID string `json:"subnetID,omitempty" protobuf:"bytes,8,opt,name=subnetID"`
	// available zone
	AvailableZone string `json:"availableZone,omitempty" protobuf:"bytes,9,opt,name=availableZone"`
	// The CIDR of the network
	CIDR string `json:"cidr,omitempty" protobuf:"bytes,3,opt,name=cidr"`
}

// NetworkStatus describes the status of a network
type NetworkStatus struct {
	// State describes the network state
	// +optional
	State string `json:"state" protobuf:"bytes,1,opt,name=state"`
	// Message describes why network is in current state
	// +optional
	Message string `json:"message,omitempty" protobuf:"bytes,2,opt,name=message"`
}

const (
	// NetworkInitializing means the network is initializing
	NetworkInitializing = "Initializing"
	// NetworkPending means the network is processing
	NetworkPending = "Pending"
	// NetworkActive means the network is available
	NetworkActive = "Active"
	// NetworkFailed means the network is not available
	NetworkFailed = "Failed"
	// NetworkTerminating means the network is undergoing graceful termination
	NetworkTerminating = "Terminating"
)

register.go

package v1beta1

import (
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

// GroupName is the group name use in this package
const GroupName = "networking.cci.io"

// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"}

// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
	return SchemeGroupVersion.WithResource(resource).GroupResource()
}

var (
	// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
	SchemeBuilder      = runtime.NewSchemeBuilder(addKnownTypes)
	localSchemeBuilder = &SchemeBuilder
	AddToScheme        = localSchemeBuilder.AddToScheme
)

// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
	scheme.AddKnownTypes(SchemeGroupVersion,
		&Network{},
		&NetworkList{},
	)
	// Add the watch version that applies
	metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
	return nil
}

Creating a Build Script

Create the hack folder for storing the build script and dependency files.

mkdir hack

Create the following files in the hack folder:

tools.go, which is created to depend on code-generator.

// +build tools
 
/*
Copyright 2019 The Kubernetes 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.
*/
 
// This package imports things required by build scripts, to force `go mod` to see them as dependencies
package tools
 
import _ "k8s.io/code-generator"

update-codegen.sh

#!/usr/bin/env bash
 
# Copyright 2017 The Kubernetes 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.
 
set -o errexit
set -o nounset
set -o pipefail
 
SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${SCRIPT_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
 
# generate the code with:
# --output-base    because this script should also be able to run inside the vendor dir of
#                  k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir
#                  instead of the $GOPATH directly. For normal projects this can be dropped.
# generators deepcopy,client,informer,lister
export CLIENTSET_PKG_NAME=networking.cci.io
export CLIENTSET_NAME_VERSIONED=v1beta1
"${CODEGEN_PKG}"/generate-groups.sh "deepcopy,client" \
  examples.com/cci-examples/pkg/client examples.com/cci-examples/pkg/apis \
  networking.cci.io:v1beta1 \
  --output-base "$(dirname "${BASH_SOURCE[0]}")/../../.." \
  --go-header-file "${SCRIPT_ROOT}"/hack/boilerplate.go.txt
 
# To use your own boilerplate text append:
#   --go-header-file "${SCRIPT_ROOT}"/hack/custom-boilerplate.go.txt

Header of the file built by boilerplate.go.txt. You can customize it.

/*
Copyright The Kubernetes 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.
*/

All preparations are complete. The file structure of the directory is as follows:

├── go.mod
├── go.sum
├── hack
│   ├── boilerplate.go.txt
│   ├── tools.go
│   └── update-codegen.sh
└── pkg
    └── apis
        └── networking.cci.io
            └── v1beta1
                ├── doc.go
                ├── register.go
                └── types.go
 
5 directories, 8 files

Executing the Name Generation Code

Run the following commands in the Linux environment:

# Generate the vendor folder.
go mod vendor
# Run the build script.
chmod 755 hack/update-codegen.sh
# hack/update-codegen.sh will execute vendor/k8s.io/code-generator/generate-groups.sh.
chmod 755 vendor/k8s.io/code-generator/generate-groups.sh
./hack/update-codegen.sh

After the commands are executed successfully, code is generated. The directory structure is as follows:

├── go.mod
├── go.sum
├── hack
│   ├── boilerplate.go.txt
│   ├── tools.go
│   └── update-codegen.sh
└── pkg
    ├── apis
    │   └── networking.cci.io
    │       └── v1beta1
    │           ├── doc.go
    │           ├── register.go
    │           ├── types.go
    │           └── zz_generated.deepcopy.go
    └── client
        └── networking.cci.io
            └── v1beta1
                ├── clientset.go
                ├── doc.go
                ├── fake
                │   ├── clientset_generated.go
                │   ├── doc.go
                │   └── register.go
                ├── scheme
                │   ├── doc.go
                │   └── register.go
                └── typed
                    └── networking.cci.io
                        └── v1beta1
                            ├── doc.go
                            ├── fake
                            │   ├── doc.go
                            │   ├── fake_network.go
                            │   └── fake_networking.cci.io_client.go
                            ├── generated_expansion.go
                            ├── network.go
                            └── networking.cci.io_client.go

Example Code: Creating a Network

The example has been tested for the following versions:

k8s.io/client-go@kubernetes-1.15.0

k8s.io/code-generator@kubernetes-1.15.0

import (
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/tools/clientcmd"

	"examples.com/cci-examples/pkg/apis/networking.cci.io/v1beta1"
	clientset "examples.com/cci-examples/pkg/client/networking.cci.io/v1beta1"
)

const (
	name      = "test-k8s-client-namespace-cn-north-1-default-network"
	namespace = "test-k8s-client-namespace"
)

// CreateNetwork indicates creating a network and waiting until its status changes to Active.
// For details, see "Namespace and Network" at https://support.huaweicloud.com/intl/en-us/devg-cci/cci_05_0023.html.
// API Reference: https://support.huaweicloud.com/intl/en-us/api-cci/createNetworkingCciIoV1beta1NamespacedNetwork.html
func CreateNetwork() (*v1beta1.Network, error) {
	config, _ := clientcmd.BuildConfigFromFlags("", "{path to kubeconfig}")
	cs, err := clientset.NewForConfig(config)
	if err != nil {
		return nil, err
	}

	projectId := "<Account ID, which can be obtained from My Credentials>"
	domainId := "<Project ID, which can be obtained from My Credentials>"
	securityGroupID := "<Security group ID, which can be obtained from the Security Groups page of the Network Console>"
	availableZone := "<AZ name, for example, cn-north-1a, cn-north-4a, or cn-east-3a>"
	vpcID := "VPC ID, which can be obtained from the VPC console"
	cidr := "<Subnet CIDR block, for example, 192.168.128.0/18>"
	networkID := "<Subnet network ID, which can be obtained from the Subnets page of the VPC console>"
	subnetID := "<Subnet ID, which can be obtained from the Subnets page of the VPC console>"

	network := &v1beta1.Network{
		ObjectMeta: metav1.ObjectMeta{
			Annotations: map[string]string{
				"network.alpha.kubernetes.io/default-security-group": securityGroupID,
				"network.alpha.kubernetes.io/domain-id":              domainId,
				"network.alpha.kubernetes.io/project-id":             projectId,
			},
			Name: name,
		},
		Spec: v1beta1.NetworkSpec{
			AvailableZone: availableZone,
			CIDR:          cidr,
			AttachedVPC:   vpcID,
			NetworkID:     networkID,
			NetworkType:   "underlay_neutron",
			SubnetID:      subnetID,
		},
	}
	network, err = cs.NetworkingCCIV1beta1().Networks(namespace).Create(network)
	if err != nil {
		return nil, err
	}

	// Query the network status and wait until the status changes to Active.
	err = wait.Poll(time.Second*5, time.Second*30, func() (done bool, err error) {
		network, err = cs.NetworkingCCIV1beta1().Networks(namespace).Get(name, metav1.GetOptions{})
		if err != nil {
			return false, err
		}
		if network.Status.State == v1beta1.NetworkActive {
			return true, nil
		}
		return false, nil
	})
	return network, err
}