使用client-go访问CCI的CRD资源Network
您可以前往开发体验馆Codelabs / Namespace生命周期代码示例(Go)下载相关代码,并在线调试。
初始化项目
创建项目examples.com/cci-examples。
项目依赖k8s.io/client-go、k8s.io/code-generator,以下版本可供参考
- 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
定义CRD资源Network
创建文件夹pkg/apis/networking.cci.io/v1beta1,其中networking.cci.io为CRD资源的group,v1beta1为CRD资源版本
mkdir -p pkg/apis/networking.cci.io/v1beta1
在新建文件夹中新建以下文件:
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 }
创建构建脚本
新建hack文件夹用以存放构建脚本及依赖文件
mkdir hack
在hack文件夹中创建以下文件:
tools.go,建立该文件来依赖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
boilerplate.go.txt构建出来的文件头,可定制
/* 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. */
至此,完成所有准备过程,此时目录的文件结构如下:
├── 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
执行命名生成代码
以下命令在linux环境下执行
# 生成vendor文件夹 go mod vendor # 执行构建脚本 chmod 755 hack/update-codegen.sh # hack/update-codegen.sh会执行vendor/k8s.io/code-generator/generate-groups.sh chmod 755 vendor/k8s.io/code-generator/generate-groups.sh ./hack/update-codegen.sh
执行成功后,将会生成代码,目录结构将为
├── 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
示例代码:创建Network
该示例测试版本:
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 创建Network,并等待其状态变更为Active // 参考《Namespace和Network》 https://support.huaweicloud.com/devg-cci/cci_05_0023.html // API参考:https://support.huaweicloud.com/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 := "<账号ID,可以在我的凭证获取>" domainId := "<项目ID,可以在我的凭证获取>" securityGroupID := "<安全组ID,可以在安全组控制台获取>" availableZone := "<az名称,例如cn-north-1a、cn-north-4a或cn-east-3a>" vpcID := "虚拟私有云的ID,可在VPC控制台获取" cidr := "<子网网段,例如192.168.128.0/18>" networkID := "<子网的网络ID,可在VPC控制台 > 子网中获取>" subnetID := "<子网ID,可在VPC控制台 > 子网获取>" 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 } // 查询Network状态,等待其状态变为"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 }