ICN开发指导

 

ICN  

 ICN (github)addresses the overall challenges of edge deployments. 

更多了解请参看srini的blogs 

ICN all project repo wiew, you can git clone all-projects 

only clone icn repo

only clone sd-ewan

SD-EWAN  

SD-EWAN(GitHub) means software define edge WAN, it is used to address the network between multi edge clusters or between edge and internet.

SD-EWAN  是ICN的核心组件。 

SD-EWAN Scenarios

overview:

对于SD-EWAN的整体理解,请参看 srini的两篇blog

Software Defined Edge WAN : SD-EWAN CNF & Cloud Native configuration   

Software Defined Edge WAN : Central Control and Traffic Hub  

SDEWAN CNF API

hands up: 

org example  

对于 SD-EWAN 的实践,我们可以从官方的github的test用例来理解。https://github.com/akraino-edge-stack/icn-sdwan/tree/master/platform/test/e2e-test-crd

git clone https://github.com/akraino-edge-stack/icn-sdwan
cd icn-sdwan/platform/test/e2e-test-crd
./test.sh

test.sh 的 进入到 edge-a edge-b sdewan-hub三个目录,通过vagrant建立了两个edge cluster a(vagrant脚本)和b的虚拟主机,同时建立了sdewan-hub的一个虚拟主机。

具体开发过程,可以参考 Li Cheng的github https://github.com/cheng1li/sdewan/tree/e6d9c67f342cc1703697b4859d4b242b600684c3

 

每个edge的目录下面,会启动两个虚拟主机,一个是k8s edge cluster, 另一个是installer, edge cluseter通过installer的虚拟主机来部署的。参考,vagrant启动多虚拟主机

installer的provision过程,会调用installer.sh, 通过ansible部署每个edge cluser, 后面会通过kud(emco)来部署。 

kub其实也是通过ansible, 主要是通过kubernetes-sigs/kubespray 定义cluster.yaml 来部署。 

在部署的过程中,kubespray/roles/kubernetes/client/tasks/main.yml 会将admin kubeconfig拷贝到ansible主机,同时也会将kubectl binary拷贝到ansible主机。

有关kubespray部署k8s请参考 https://kubernetes.feisky.xyz/setup/cluster/kubespray 

CRD开发

请参看Kubebuilder (github)的这篇文章,这篇文章系统的介绍了K8s 的Job开发过程中,涉及到的各种知识。 k8s, 1.6以下,采用kubebuilder V1版本,以上为V2版本,需要迁移

Vagrant:

Vagrant tutorial   

Vagrant Documentation 

licheng同学为k8s的vagrant已经准备了一些脚本,请参考GitHub 

ansible:

使用篇

各种实例 

非常好的Ansible入门教程(超简单)    

python脚本化ansible篇:

ansible作为python模块库使用的方法实例  

python 插件扩展篇: 

关于ansible自定义lookup_plugins插件实现playbook扩展  

Ansible 插件开发  

Ansible插件扩展

step one by one:

  • setup host  

# install vagrant on ubuntu18.04 on Host
sudo apt update
sudo apt install -y libvirt-bin qemu dnsmasq
sudo apt install vagrant
vagrant plugin install vagrant-libvirt
  • set up icn VM by vagrant 

git clone https://gerrit.akraino.org/r/icn
git clone https://gerrit.akraino.org/r/icn/sdwan
cd icn
git grep "vagrant ssh"
vagrant up
vagrant ssh
sudo su
make bm_verify_nestedk8s
  •  insteall docker

# install docker, ref: https://kubernetes.io/docs/setup/production-environment/container-runtimes/#docker

wget -O- https://get.docker.com/ |bash
sudo usermod -aG docker  `whoami`

# Set up the Docker daemon
sudo bash -c "cat > /etc/docker/daemon.json" <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF

sudo mkdir -p /etc/systemd/system/docker.service.d

# Restart Docker
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl enable docker
  • install kubeadm   

# install kubeadm
# ref: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#installing-kubeadm-kubelet-and-kubectl
sudo apt-get update && sudo apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

# [ERROR Swap]: running with swap on is not supported. Please disable swap
# ref: https://stackoverflow.com/questions/56287494/why-does-kubeadm-not-start-even-after-disabling-swap
sudo swapoff -a

API_SERVER_IP=`ip route get 1 | awk '{match($0, /.+src\s([.0-9]+)/, a);print a[1];exit}'`
  • install k8s  

# https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init/
# for flannel --pod-network-cidr 10.244.0.0/16 ,  for calico --pod-network-cidr 192.168.64.0/20
# install k8s
sudo kubeadm init --pod-network-cidr 10.244.0.0/16
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

TOKEN=`kubeadm token create`
kubeadm token list

TOKEN_INFO=`kubeadm token create --print-join-command`
TOKEN_CA_HASH=`echo $TOKEN_INFO |awk '{match($0, /.+discovery-token-ca-cert-hash\s(.+)/, a);print a[1];exit}'`
TOKEN=`echo $TOKEN_INFO | awk '{match($0, /.+--token\s([^ ]+)/, a);print a[1];exit}'`


# join any number of worker nodes by running the following on each as root:
# kubeadm join $API_SERVER_IP:6443 --token $TOKEN  --discovery-token-ca-cert-hash $TOKEN_CA_HASH
  • deploy a pod network to the cluster

# choose one of CNI plugin from https://kubernetes.io/docs/concepts/cluster-administration/addons/
# here we choose flannel, ref https://github.com/coreos/flannel
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

kubectl get daemonset -n kube-system
kubectl get pod -n kube-system
  • build OVN4NFV controller

sudo apt install make
sudo apt install make-guile
sudo apt  install jq
# https://gist.github.com/Zate/b3c8e18cbb2bbac2976d79525d95f893
# GOURLREGEX='https://dl.google.com/go/go[0-9\.]+\.linux-amd64.tar.gz'
url="$(wget -qO- https://golang.org/dl/ | grep -oP '\/dl\/go([0-9\.]+)\.linux-amd64\.tar\.gz' | head -n 1 )"
latest="$(echo $url | grep -oP 'go[0-9\.]+' | grep -oP '[0-9\.]+' | head -c -2 )"
wget --quiet --continue --show-progress "https://golang.org/dl/go${latest}.linux-amd64.tar.gz"
sudo tar -C /usr/local -xzf go"${latest}".linux-amd64.tar.gz
mkdir -p ~/go/{bin,pkg,src}
echo "export GOPATH=~/go" >> ~/.profile && source ~/.profile
echo "export PATH='$PATH':/usr/local/go/bin:$GOPATH/bin" >> ~/.profile && source ~/.profile
go get -u github.com/golang/dep/cmd/dep


# https://github.com/opnfv/ovn4nfv-k8s-plugin
mkdir -p k8s/plugin && cd k8s/plugin  # work around for makefile GOPARH var
git clone https://github.com/opnfv/ovn4nfv-k8s-plugin.git
cd ovn4nfv-k8s-plugin
ln -s $GOPATH "$PWD/../../go"
make

tips: "export variable …"  export specific variables to a sub-make

https://www.gnu.org/software/make/manual/make.html 

  • install OVN

# install by k8s, https://github.com/ovn-org/ovn-kubernetes
sudo apt-get install apt-transport-https
echo "deb http://3.19.28.122/openvswitch/stable /" |  sudo tee /etc/apt/sources.list.d/openvswitch.list
wget -O - http://3.19.28.122/openvswitch/keyFile |  sudo apt-key add -
sudo apt-get update
sudo apt-get build-dep dkms
sudo apt-get install openvswitch-datapath-dkms -y

# ---------------------------------------------------------------------------------

 

# https://github.com/onap/multicloud-k8s/blob/master/kud/deployment_infra/playbooks/configure-ovn.yml

# DISTRIB_CODENAME=`grep "DISTRIB_CODENAME" /etc/lsb-release |cut -d "=" -f 2`
# https://packages.wand.net.nz/ 
DISTRIB_CODENAME=$(lsb_release -sc)
sudo apt-get install apt-transport-https
echo "deb https://packages.wand.net.nz $DISTRIB_CODENAME ovs-2.10" |  sudo tee /etc/apt/sources.list.d/openvswitch.list
sudo curl https://packages.wand.net.nz/keyring.gpg -o /etc/apt/trusted.gpg.d/wand.gpg
sudo apt-get update

sudo apt-get install -y openvswitch-common openvswitch-switch
sudo apt-get install -y ovn-common ovn-host
sudo apt-get install -y ovn-central

echo "OVN_CTL_OPTS=\" --db-sb-create-insecure-remote=yes --db-nb-create-insecure-remote=yes\"" | sudo tee -a /etc/default/ovn-central
sudo systemctl restart ovn-central

sudo systemctl stop ovn-host
# https://www.openvswitch.org/support/dist-docs-2.5/IntegrationGuide.md.txt
# http://www.openvswitch.org/support/dist-docs/ovs-vswitchd.conf.db.5.html
# http://www.openvswitch.org/support/dist-docs/ovs-vsctl.8.txt
sudo ovs-vsctl set open_vswitch . external_ids:ovn-remote="\"tcp:$API_SERVER_IP:6642\""
sudo ovs-vsctl get open_vswitch . external_ids:ovn-remote

sudo ovs-vsctl set open_vswitch . external_ids:ovn-encap-type="\"geneve\""
# https://stackoverflow.com/questions/39819378/ansible-get-current-target-hosts-ip-address
sudo ovs-vsctl set open_vswitch . external_ids:ovn-encap-ip="\"$API_SERVER_IP\""

sudo ovs-vsctl get open_vswitch . external_ids

# https://docs.openvswitch.org/en/latest/tutorials/ovs-advanced/#setup
sudo ovs-vsctl add-br br-int -- set Bridge br-int fail-mode=secure
sudo systemctl start ovn-host
  • install multus 

kubectl apply -f https://raw.githubusercontent.com/onap/multicloud-k8s/master/kud/deployment_infra/images/multus-daemonset.yml

kubectl describe CustomResourceDefinition network-attachment-definitions.k8s.cni.cncf.io
kubectl describe ClusterRole multus
kubectl describe ClusterRoleBinding multus
kubectl -n kube-system describe ServiceAccount multus
kubectl -n kube-system describe ConfigMap multus
kubectl -n kube-system get DaemonSet
  • deploy OVN4NFV

# ref:  https://github.com/onap/multicloud-k8s

# follow the step from:  https://github.com/onap/multicloud-k8s/blob/master/kud/deployment_infra/playbooks/configure-ovn4nfv.yml

# setp 1: define a CRD network object specification
cat > /tmp/ovn4nfvnetwork.yml << EOF
apiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
  name: ovn-networkobj
spec:
  config: '{
     "cniVersion": "0.3.1",
     "name": "ovn4nfv-k8s-plugin",
     "type": "ovn4nfvk8s-cni"
  }'
EOF

kubectl apply -f /tmp/ovn4nfvnetwork.yml
kubectl describe net-attach-def

kubectl create namespace operator
kubectl label node `hostname` nfnType=operator --overwrite
kubectl apply -f https://raw.githubusercontent.com/onap/multicloud-k8s/master/kud/deployment_infra/images/nfn.yml

# check status
sudo kubectl describe CustomResourceDefinition networks.k8s.plugin.opnfv.org
sudo kubectl describe CustomResourceDefinition providernetworks.k8s.plugin.opnfv.org
sudo kubectl -n operator describe ServiceAccount k8s-nfn-sa
sudo kubectl describe ClusterRole k8s-nfn-cr
sudo kubectl describe ClusterRoleBinding k8s-nfn-crb
sudo kubectl -n operator describe Service nfn-operator
sudo kubectl -n operator describe Deployment nfn-operator
sudo kubectl -n operator describe ConfigMap ovn4nfv-cni-config
sudo kubectl -n operator describe DaemonSet ovn4nfv-cni
sudo kubectl -n operator describe DaemonSet nfn-agent
  • deploy openwrt  

# https://github.com/akraino-edge-stack/icn/blob/master/sdwan/test/sdwan-openwrt-ovn.yml
# https://github.com/akraino-edge-stack/icn-sdwan/blob/master/platform/test/e2e-test/edge-scripts/setup-cnf.sh
mkdir ~/sdwan_setup
cd ~/sdwan_setup
# https://github.com/akraino-edge-stack/icn-sdwan/blob/master/platform/test/e2e-test/edge-scripts/setup-cnf.sh
curl https://raw.githubusercontent.com/akraino-edge-stack/icn-sdwan/master/platform/test/e2e-test/edge-a/scripts/variables -o  ./variables
source ./variables
curl https://raw.githubusercontent.com/akraino-edge-stack/icn-sdwan/master/platform/test/e2e-test/edge-scripts/sdwan_verifier.sh -o sdwan_verifier.sh
curl https://raw.githubusercontent.com/akraino-edge-stack/icn-sdwan/master/platform/test/e2e-test/edge-scripts/setup-cnf.sh -o setup-cnf.sh
sed -i -e 's/openwrt:test/openwrt:${OPENWRT_V:-test}/' setup-cnf.sh
kubectl taint nodes --all node-role.kubernetes.io/master-

OPENWRT_V=0.3.0 bash setup-cnf.sh
  • Add a new API(used for demo)

# https://kubernetes.io/zh/docs/tasks/debug-application-cluster/get-shell-running-container/
kubectl exec -it $sdewan_cnf_name sh
cd /usr/lib/lua/luci/controller/rest_v1/
ps |grep http
# https://oldwiki.archive.openwrt.org/doc/techref/ubus
ubus list
ubus -v list network.interface
ubus call network.interface.wan0 status

PODIP
=`kubectl get pod $sdewan_cnf_name -o template='{{ .status.podIP }}'` BASE_URL="http://$PODIP/cgi-bin/luci/" TOKEN=`wget -S --spider --post-data "luci_username=root&luci_password=" $BASE_URL 2>&1 | grep -o "sysauth=\([0-9a-fA-F]\+\)" |cut -d "=" -f2` cat > demo.lua <<<'-- Licensed to the public under the GNU General Public License v2. module("luci.controller.demo", package.seeall) sys = require "luci.sys" ut = require "luci.util" io = require "io" ip = "ip -4 " function index() entry({"admin", "config", "command"}, call("execute")).dependent = false end function trim(s) return s:match("^%s*(.-)%s*$") end function split_and_trim(str, sep) local array = {} local reg = string.format("([^%s]+)", sep) for item in string.gmatch(str, reg) do item_trimed = trim(item) if string.len(item_trimed) > 0 then table.insert(array, item_trimed) end end return array end function execute() local commands = luci.http.formvalue("command") io.stderr:write("Execute command: %s\n" % commands) local command_array = split_and_trim(commands, ";") for index, command in ipairs(command_array) do -- io.stderr:write("%d: [%s]\n" % {index, command}) sys.exec(command) -- sys.exec("uci commit sample") -- sys.exec("/etc/init.d/firewall restart") end luci.http.prepare_content("application/json") luci.http.write_json("{'status':'ok'}") end' scp demo.lua root@$PODIP:/usr/lib/lua/luci/controller wget -S --header="Cookie:sysauth=$TOKEN" --post-data "command=fw3 reload" $BASE_URL/admin/config/command curl -s -X "POST" -H "Cookie:sysauth=$TOKEN" -d "command=fw3 reload" $BASE_URL/admin/config/demo # login pod kubectl exec -it $sdewan_cnf_name sh logread sed -i -e 's/"config", "command"/"config", "demo"/' /usr/lib/lua/luci/controller/demo.lua exit wget -S --header="Cookie:sysauth=$TOKEN" --post-data "command=fw3 reload" $BASE_URL/admin/config/demo curl -s -X "POST" -H "Cookie:sysauth=$TOKEN" -d "command=fw3 reload" $BASE_URL/admin/config/demo
  • add a really interface API:   

  1 cat > interface.lua <<EOF
  2 -- Licensed to the public under the GNU General Public License v2.
  3 
  4 module("luci.controller.rest_v1.interface", package.seeall)
  5 
  6 NX = require("nixio")
  7 json = require "luci.jsonc"
  8 io = require "io"
  9 sys = require "luci.sys"
 10 utils = require "luci.controller.rest_v1.utils"
 11 util = require "luci.util"
 12 
 13 fields_table = {
 14     {field="ip_address", key="inet addr", type="array"},
 15     {field="ip6_address", key="inet6 addr", type="array"},
 16     {field="mac_address", key="HWaddr"},
 17     {field="received_packets", key="RX packets"},
 18     {field="send_packets", key="TX packets"},
 19     {field="status", key=function(data) if string.match(data, "[%s]UP[%s]") ~= nil then return "UP" else return "DOWN" end end},
 20 }
 21 
 22 interface_validator = {
 23     {name="action", required=true, validator=function(value) return utils.in_array(value, {"up", "down"}) end, message="wrong action"}
 24 }
 25 
 26 function index()
 27     ver = "v1"
 28     entry({"sdewan", ver, "interfaces"}, call("handle_request")).leaf = true
 29 end
 30 
 31 function get_field(data, key, field_type)
 32     if type(key) == "function" then
 33         return key(data)
 34     end
 35 
 36     local reg = {
 37         key .. ":[^%s]+[%s]",
 38         key .. " [^%s]+[%s]",
 39         key .. ": [^%s]+[%s]",
 40     }
 41 
 42     local ret = nil
 43     for index=1, #reg do
 44         for item in string.gmatch(data, reg[index]) do
 45             local value = nil
 46             local i,j = string.find(item, key .. ": ")
 47             if i ~= nil then
 48                 value = string.sub(item, j+1, string.len(item)-1)
 49             else
 50                 i,j = string.find(item, key .. ":")
 51                 if i ~= nil then
 52                     value = string.sub(item, j+1, string.len(item)-1)
 53                 else
 54                     i,j = string.find(item, key .. " ")
 55                     if i ~= nil then
 56                         value = string.sub(item, j+1, string.len(item)-1)
 57                     end
 58                 end
 59             end
 60             if value ~= nil then
 61                 if field_type == "array" then
 62                     if ret == nil then
 63                         ret = {value}
 64                     else
 65                         ret[#ret+1] = value
 66                     end
 67                 else
 68                     ret = value
 69                     break
 70                 end
 71             end
 72         end
 73     end
 74     return ret
 75 end
 76 
 77 function is_interface_available(ifname)
 78     for interface in util.execi("ifconfig | awk '/^[^ \t]+/{print $1}'") do
 79         if interface ~= "lo" and interface == ifname then
 80            return true
 81         end
 82     end
 83 
 84     return false
 85 end
 86 
 87 function get_interface(interface, bypasscheck)
 88     if bypasscheck ~= true then
 89         if is_interface_available(interface) == false then
 90             return nil
 91         end
 92     end
 93 
 94     local ret = {}
 95     local data = util.exec("ifconfig " .. interface)
 96     if data == nil then
 97         for j=1, 3 do
 98             utils.log("ifconfig failed, retrying ... ")
 99             NX.nanosleep(1)
100             data = util.exec("ifconfig " .. interface)
101             if data ~= nil then
102                 break
103             end
104         end
105     end
106     ret["name"] = interface
107     for i,v in pairs(fields_table) do
108         local value = get_field(data, v["key"], v["type"])
109         if value ~= nil then
110             ret[v["field"]] = value
111         end
112     end
113     return ret
114 end
115 
116 function get_interfaces()
117     local ret = {}
118     ret["interfaces"] = {}
119     local index = 1
120     for interface in util.execi("ifconfig | awk '/^[^ \t]+/{print $1}'") do
121         if interface ~= "lo" then
122            ret["interfaces"][index] = get_interface(interface, true)
123            index = index + 1
124         end
125     end
126     return ret
127 end
128 
129 -- Request Handler
130 function handle_request()
131     local method = utils.get_req_method()
132     if method == "GET" then
133         return getRequest()
134     elseif method == "PUT" then
135         return setRequest()
136     else
137         utils.response_error(405, "Method Not Allowed")
138     end
139 end
140 
141 function getRequest()
142     local uri_list = utils.get_URI_list()
143     if uri_list == nil then
144         return
145     end
146 
147     if #uri_list == 5 then
148         utils.response_object(get_interfaces())
149     elseif #uri_list == 6 then
150         local ifname = uri_list[#uri_list]
151         local obj = get_interface(ifname)
152 
153         if obj == nil then
154             utils.response_error(404, "Cannot find interface[" .. ifname .. "]" )
155             return
156         end
157         utils.response_object(obj)
158     else
159         utils.response_error(400, "Bad request URI")
160         return
161     end
162 end
163 
164 function setRequest()
165     local uri_list = utils.get_URI_list()
166     if uri_list == nil then
167         return
168     end
169 
170     if #uri_list == 6 then
171         local ifname = uri_list[#uri_list]
172         -- local obj = get_interface(ifname)
173         -- if obj == nil then
174         --    utils.response_error(404, "Cannot find interface[" .. ifname .. "]" )
175         --    return
176         -- end
177         -- check content
178         local body_obj = utils.get_and_validate_body_object(interface_validator)
179         if body_obj == nil then
180             return
181         end
182 
183         local action = body_obj["action"]
184         local exec_command = "ifconfig " .. ifname .. " " .. action
185         -- execute command
186         utils.log("Execute Command: %s" % exec_command)
187         sys.exec(exec_command)
188         NX.nanosleep(1)
189         utils.response_success()
190     else
191         utils.response_error(400, "Bad request URI")
192         return
193     end
194 end
195 EOF
View Code

 

  • test the interface API Manual

# copy to CNF POD
scp interface.lua root@$PODIP:/usr/lib/lua/luci/controller/rest_v1
# get  json respon
curl -s -H "Cookie:sysauth=$TOKEN" -H "Content-Type: application/json"  $BASE_URL/sdewan/v1/interfaces  | jq

# debug:
kubectl exec -it $sdewan_cnf_name logread
  • Add a label for CNF pod
 kubectl label pod $sdewan_cnf_name sdewanPurpose=cnf
  • create interface empty CRD

os=$(go env GOOS)
arch=$(go env GOARCH)
curl -L https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH) | tar -xz -C /tmp/
VURL=`curl -Ls -o /dev/null -w %{url_effective} https://go.kubebuilder.io/dl/latest/$(go env GOOS)/$(go env GOARCH)`
VERSION=${VURL##*/}
sudo mv /tmp/${VERSION%%.*} /usr/local/kubebuilder
echo "export PATH=$PATH:/usr/local/kubebuilder/bin" >> ~/.profile  && source ~/.profile

# https://kubernetes-sigs.github.io/kustomize/installation/source/
GOBIN=$(go env GOPATH)/bin GO111MODULE=on go get sigs.k8s.io/kustomize/kustomize/v3
sudo apt-get install gcc

mkdir sdewan && cd sdewan 
go mod init sdewan.akraino.org/sdewan

kubebuilder init --domain sdewan.akraino.org
kubebuilder create api --group batch --version  v1alpha1  --kind  CniInterface

make install
make run
# it will run, see Makefile
# /home/vagrant/go/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
# go fmt ./...
# go vet ./...
# /home/vagrant/go/bin/controller-gen "crd:trivialVersions=true" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
# go run ./main.go

kubectl describe CustomResourceDefinition cniinterfaces.batch.sdewan.akraino.org
kubectl apply -f config/samples/
kubectl describe CniInterface
kubectl delete -f config/samples/

make uninstall

 

  • Add base openwrt client and test it.

# currently the CNF is manager openwrt by rust API

# we need to implementation openwrt client, ref https://github.com/cheng1li/sdewan/

mkdir openwrt/
curl https://raw.githubusercontent.com/cheng1li/sdewan/master/openwrt/openwrtclient.go -o openwrt/openwrtclient.go

curl https://raw.githubusercontent.com/cheng1li/sdewan/8c34d5f2d1da238daf20272b57c4e9b72943c405/openwrt/service.go  -o openwrt/service.go

curl https://raw.githubusercontent.com/cheng1li/sdewan/8c34d5f2d1da238daf20272b57c4e9b72943c405/openwrt/utils.go openwrt/utils.go 


mkdir cmd
cat > cmd/client.go <<EOF
package main

import (
    "fmt"
    "flag"
    "sdewan.akraino.org/sdewan/openwrt"
)

func main() {
    fmt.Println("hello world")
    podip := flag.String("ip", "127.0.0.1", "A string for pod ip.")
    flag.Parse()
    fmt.Println("OpenWrt IP:", *podip)
    openwrtClient := openwrt.NewOpenwrtClient(*podip, "root", "")
    fmt.Println("openwrtClient:", openwrtClient)
    service := openwrt.ServiceClient{OpenwrtClient: openwrtClient}
    sc, _ := service.GetAvailableServices()
    fmt.Println("AvailableServices:", sc)
}
EOF

go run cmd/client.go -ip=$(kubectl get pod $sdewan_cnf_name -o template='{{ .status.podIP }}')

 

  • Add base interfaces client and test it.

cat > openwrt/interfaces.go <<EOF
package openwrt

import (
        "encoding/json"
)

const (
        interfaceBaseURL = "sdewan/v1/"
)


type InterfaceClient struct {
        OpenwrtClient *openwrtClient
}

// Interface API struct
type Interface struct {
        Name            string   `json:"name,omitempty"`
        ReceivedPackets string   `json:"received_packets,omitempty"`
        SendPackets     string   `json:"send_packets,omitempty"`
        Status          string   `json:"status,omitempty"`
        MacAddress      string   `json:"mac_address,omitempty"`
        IpAddress       []string `json:"ip_address,omitempty"`
}

type AvailableInterfaces struct {
        Interfaces []Interface `json:"interfaces"`
}

// get available interfaces
func (s *InterfaceClient) GetAvailableInterfaces() (*AvailableInterfaces, error) {
        response, err := s.OpenwrtClient.Get(interfaceBaseURL + "interfaces")
        if err != nil {
                return nil, err
        }

        var servs AvailableInterfaces
        err2 := json.Unmarshal([]byte(response), &servs)
        if err2 != nil {
                return nil, err2
        }

        return &servs, nil
}
EOF

cat > cmd/client.go <<EOF
package main

import (
    "fmt"
    "flag"
    "sdewan.akraino.org/sdewan/openwrt"
)

func main() {
    fmt.Println("hello world")
    podip := flag.String("ip", "127.0.0.1", "A string for pod ip.")
    flag.Parse()
    fmt.Println("OpenWrt IP:", *podip)
    openwrtClient := openwrt.NewOpenwrtClient(*podip, "root", "")
    fmt.Println("openwrtClient:", openwrtClient)
    service := openwrt.InterfaceClient{OpenwrtClient: openwrtClient}
    fcs, _ := service.GetAvailableInterfaces()
    fmt.Println("AvailableInterfaces:", fcs)
    fmt.Println("Interface 0, name:", fcs.Interfaces[0].Name)
}
EOF

go run cmd/client.go -ip=$(kubectl get pod $sdewan_cnf_name -o template='{{ .status.podIP }}')

 

  • fill the interface CRD

patch 

  1 cat > /tmp/sdwan_interface.patch << EOF
  2 commit fdeff619eb0e4270d25c3f656f72a3378f922e9b
  3 Author: vagrant <vagrant@ubuntu>
  4 Date:   Fri Sep 11 15:57:25 2020 +0000
  5 
  6     add more field for CniInterfaceSpec
  7 
  8 diff --git a/api/v1alpha1/cniinterface_types.go b/api/v1alpha1/cniinterface_types.go
  9 index 03623c0..963771f 100644
 10 --- a/api/v1alpha1/cniinterface_types.go
 11 +++ b/api/v1alpha1/cniinterface_types.go
 12 @@ -29,13 +29,22 @@ type CniInterfaceSpec struct {
 13         // Important: Run "make" to regenerate code after modifying this file
 14 
 15         // Foo is an example field of CniInterface. Edit CniInterface_types.go to remove/update
 16 -       Foo string `json:"foo,omitempty"`
 17 +       Name        string               `json:"name,omitempty"`
 18 +       Status      string               `json:"status,omitempty"`
 19 +       MacAddress  string               `json:"mac_address,omitempty"`
 20 +       IpAddress   []string             `json:"ip_address,omitempty"`
 21 +       CnfSelector metav1.LabelSelector `json:"cnfSelector,omitempty"`
 22  }
 23 
 24  // CniInterfaceStatus defines the observed state of CniInterface
 25  type CniInterfaceStatus struct {
 26         // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
 27         // Important: Run "make" to regenerate code after modifying this file
 28 +       ReceivedPackets string   `json:"received_packets,omitempty"`
 29 +       SendPackets     string   `json:"send_packets,omitempty"`
 30 +       Status          string   `json:"status,omitempty"`
 31 +       MacAddress      string   `json:"mac_address,omitempty"`
 32 +       IpAddress       []string `json:"ip_address,omitempty"`
 33  }
 34 
 35  // +kubebuilder:object:root=true
 36 diff --git a/config/samples/batch_v1alpha1_cniinterface.yaml b/config/samples/batch_v1alpha1_cniinterface.yaml
 37 index 0d9ff78..4171ced 100644
 38 --- a/config/samples/batch_v1alpha1_cniinterface.yaml
 39 +++ b/config/samples/batch_v1alpha1_cniinterface.yaml
 40 @@ -4,4 +4,7 @@ metadata:
 41    name: cniinterface-sample
 42  spec:
 43    # Add fields here
 44 -  foo: bar
 45 +  cnfSelector:
 46 +    matchLabels:
 47 +      sdewanPurpose: cnf
 48 +  name: eth0
 49 diff --git a/controllers/cniinterface_controller.go b/controllers/cniinterface_controller.go
 50 index dfd4008..416f89d 100644
 51 --- a/controllers/cniinterface_controller.go
 52 +++ b/controllers/cniinterface_controller.go
 53 @@ -20,11 +20,13 @@ import (
 54         "context"
 55 
 56         "github.com/go-logr/logr"
 57 +       corev1 "k8s.io/api/core/v1"
 58         "k8s.io/apimachinery/pkg/runtime"
 59         ctrl "sigs.k8s.io/controller-runtime"
 60         "sigs.k8s.io/controller-runtime/pkg/client"
 61 
 62         batchv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1"
 63 +       "sdewan.akraino.org/sdewan/openwrt"
 64  )
 65 
 66  // CniInterfaceReconciler reconciles a CniInterface object
 67 @@ -38,10 +40,48 @@ type CniInterfaceReconciler struct {
 68  // +kubebuilder:rbac:groups=batch.sdewan.akraino.org,resources=cniinterfaces/status,verbs=get;update;patch
 69 
 70  func (r *CniInterfaceReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
 71 -       _ = context.Background()
 72 -       _ = r.Log.WithValues("cniinterface", req.NamespacedName)
 73 +       ctx := context.Background()
 74 +       log := r.Log.WithValues("cniinterface", req.NamespacedName)
 75 
 76         // your logic here
 77 +       var cniInterface batchv1alpha1.CniInterface
 78 +       if err := r.Get(ctx, req.NamespacedName, &cniInterface); err != nil {
 79 +               log.Error(err, "unable to fetch CniInterface")
 80 +               // we'll ignore not-found errors, since they can't be fixed by an immediate
 81 +               // requeue (we'll need to wait for a new notification), and we can get them
 82 +               // on deleted requests.
 83 +               return ctrl.Result{}, client.IgnoreNotFound(err)
 84 +       }
 85 +
 86 +       podList := &corev1.PodList{}
 87 +       label := cniInterface.Spec.CnfSelector.MatchLabels["sdewanPurpose"]
 88 +       err := r.Client.List(ctx, podList, client.MatchingLabels{"sdewanPurpose": label})
 89 +       if err != nil {
 90 +               log.Error(err, "Failed to get pod list")
 91 +               return ctrl.Result{}, err
 92 +       }
 93 +
 94 +       fcs := &openwrt.AvailableInterfaces{}
 95 +       for _, pod := range podList.Items {
 96 +               openwrtClient := openwrt.NewOpenwrtClient(pod.Status.PodIP, "root", "")
 97 +               ifc := openwrt.InterfaceClient{OpenwrtClient: openwrtClient}
 98 +               fcs, _ = ifc.GetAvailableInterfaces() // TODO, support get one interface?
 99 +       }
100 +
101 +       for _, fc := range fcs.Interfaces {
102 +               if cniInterface.Spec.Name == fc.Name {
103 +                       cniInterface.Status.Status = fc.Status
104 +                       cniInterface.Status.ReceivedPackets = fc.ReceivedPackets
105 +                       cniInterface.Status.SendPackets = fc.SendPackets
106 +                       cniInterface.Status.MacAddress = fc.MacAddress
107 +                       cniInterface.Status.IpAddress = fc.IpAddress
108 +                       break
109 +               }
110 +       }
111 +       if err := r.Update(ctx, &cniInterface); err != nil {
112 +               log.Error(err, "unable to update CniInterface status")
113 +               return ctrl.Result{}, err
114 +       }
115 
116         return ctrl.Result{}, nil
117  }
118 EOF
View Code

test

patch -p1 < sdwan_interface.patch
make install && make run

kubectl apply -f config/samples/
kubectl describe CniInterface
kubectl delete -f config/samples/

make uninstall

exmaple: controller-runtime/tree/master/examples/builtins

  • All the code is in github

  • deploy Admission webhook

# https://github.com/kubernetes-sigs/kubebuilder/blob/master/docs/book/src/cronjob-tutorial/webhook-implementation.md
kubebuilder create webhook --group batch --version  v1alpha1  --kind  CniInterface --programmatic-validation --defaulting

# https://segmentfault.com/a/1190000020338350

mkdir -p /tmp/k8s-webhook-server/serving-certs/ cp /etc/kubernetes/pki/apiserver.crt /tmp/k8s-webhook-server/serving-certs/tls.crt sudo cp /etc/kubernetes/pki/apiserver.key /tmp/k8s-webhook-server/serving-certs/tls.key sudo chown $(whoami):$(whoami) /tmp/k8s-webhook-server/serving-certs/tls.key CABUNDLE=`kubectl config view --raw -o json | jq -r '.clusters[0].cluster."certificate-authority-data"' | tr -d '"'` sed -i -e "s/caBundle: Cg==/caBundle: $CABUNDLE/" config/webhook/manifests.yaml # sed -i -e "s/caBundle: $CABUNDLE/caBundle: Cg==/" config/webhook/manifests.yaml sed -i -e "/service:/,+2d" config/webhook/manifests.yaml sed -i -e "s/ path: \(.*\)$/url: https:\/\/$API_SERVER_IP:9443\1/" config/webhook/manifests.yaml sed -i -e "s/namespace: system/namespace: kube-system/" config/webhook/manifests.yaml config/webhook/service.yaml # sed -i -e "s/namespace: kube-system/namespace: system/" config/webhook/manifests.yaml config/webhook/service.yaml kubectl apply -f config/webhook/service.yaml kubectl -n kube-system describe Service webhook-service # kubectl describe Service webhook-service kubectl apply -f config/webhook/manifests.yaml kubectl describe ValidatingWebhookConfiguration validating-webhook-configuration kubectl describe MutatingWebhookConfiguration mutating-webhook-configuration make run kubectl apply -f config/samples/ # kubectl delete Service webhook-service kubectl -n kube-system delete Service webhook-service
  • create user

OPERATOR=cnfop
ORGANIZATION=icn:cnf:sdewan
# https://zhuanlan.zhihu.com/p/43237959
# https://github.com/openssl/openssl/issues/7754
cat /etc/ssl/openssl.cnf | sed "s/RANDFILE\s*=\s*\$ENV::HOME\/\.rnd/#/"
dd if=/dev/urandom of=~/.rnd bs=256 count=1 
openssl genrsa -out $OPERATOR.key 2048
openssl req -new -key $OPERATOR.key -out $OPERATOR.csr -subj "/CN=$OPERATOR/O=$ORGANIZATION" 
sudo openssl x509 -req -in $OPERATOR.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out $OPERATOR.crt -days 36500
openssl x509 -in $OPERATOR.crt -noout -text  
kubectl config set-credentials $OPERATOR --client-certificate=$OPERATOR.crt --client-key=$OPERATOR.key

kubectl config get-clusters
K8SUSER=`kubectl config get-contexts | tail -n 1 | awk '{print $4}'`
CLUSTERNAME=`kubectl config get-contexts | tail -n 1 | awk '{print $3}'`
# CLUSTERNAME=`grep -n3 "\- cluster:" ~/.kube/config | grep -o "name:.*" | cut -d " " -f 2`
# kubectl config set-context $OPERATOR@$CLUSTERNAME --cluster=$CLUSTERNAME --namespace=default --user=$OPERATOR
kubectl config set-context $OPERATOR@$CLUSTERNAME --cluster=$CLUSTERNAME --user=$OPERATOR
kubectl config view
kubectl config current-context  
kubectl config use-context $OPERATOR@$CLUSTERNAME
kubectl config use-context $OPERATOR@$CLUSTERNAME
CURRENTNAME=`kubectl config get-contexts -o name | head -n1`
LASTNAME=`kubectl config get-contexts -o name | tail -n1`

CRTDATA=`cat $OPERATOR.crt | base64 --wrap=0`
KEYDATA=`cat $OPERATOR.key | base64 --wrap=0` 
sed -i -e "s/client-certificate:.*$OPERATOR.crt/client-certificate-data: $CRTDATA/" ~/.kube/config
sed -i -e "s/client-key:\s.*$OPERATOR.key/client-key-data: $KEYDATA/" ~/.kube/config
  • Add/Delete clust role binding

kubectl --context=$LASTNAME create clusterrolebinding ${OPERATOR}-cluster-admin-binding --clusterrole=cluster-admin --user=${OPERATOR}
kubectl --context=$LASTNAME delete clusterrolebinding ${OPERATOR}-cluster-admin-binding
  • Add role binding  

# ref: https://kubernetes.io/zh/docs/reference/access-authn-authz/rbac/
# "kind": "batch.sdewan.akraino.org/v1alpha1, Kind=CniInterface"
# "resource": {"group":"batch.sdewan.akraino.org","version":"v1alpha1","resource":"cniinterfaces"}
OPERATOR=cnfop
ORGANIZATION=icn:nfc:sdewan
K8SUSER=`grep -n1 "users:" ~/.kube/config |grep -o "name:.*" | cut -d " " -f 2`
cat > sdewan-cnf-role-binding.yaml << EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: icn:cnf-operator
  # namespace: kube-system
rules:
- apiGroups:
  - "batch.sdewan.akraino.org"
  resources:
  - cniinterfaces
  verbs: ["*"]
  # resourceNames: [cniinterfaces-eth0]
  
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: icn:cnf-operator-binding
  # namespace: kube-system
roleRef:
  apiGroup: ""
  kind: Role
  name: icn:cnf-operator
subjects:
- apiGroup: ""
  kind: User
  name: $OPERATOR
- kind: Group
  name: $ORGANIZATION
  apiGroup: rbac.authorization.k8s.io
EOF

kubectl --context=$LASTNAME apply -f sdewan-cnf-role-binding.yaml
rm sdewan-cnf-role-binding.yaml
kubectl --context=$LASTNAME describe  Role icn:cnf-operator 
kubectl --context=$LASTNAME describe  Rolebinding icn:cnf-operator-binding

kubectl --context=$LASTNAME delete Role icn:cnf-operator 
kubectl --context=$LASTNAME delete Rolebinding icn:cnf-operator-binding
  • switch context

kubectl config use-context $LASTNAME
  • create CRT and USER/GROUP by kubectl

CSRDATA=$(cat ${OPERATOR}.csr | base64 | tr -d '\n')

# Ref: 
# https://kubernetes.io/docs/reference/access-authn-authz/certificate-signing-requests/
# https://github.com/rancher/k3s/issues/1764
# https://thenewstack.io/a-practical-approach-to-understanding-kubernetes-authentication/
# https://dev.to/focusedlabs/provisioning-users-and-groups-for-kubernetes-4251
# https://docs.bitnami.com/tutorials/configure-rbac-in-your-kubernetes-cluster/

cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
  name: $OPERATOR
spec:
  username: $OPERATOR
  groups:
  - system:authenticated
  request: ${CSRDATA}
  usages:
  - digital signature
  - key encipherment
  - client auth
EOF

kubectl certificate approve $OPERATOR
kubectl get csr $OPERATOR -o jsonpath={.status.certificate} | base64 --decode > $OPERATOR.pem

USERNAME=$OPERATOR
kubectl create namespace $USERNAME

cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: $USERNAME-namespace
rules:
- apiGroups: [""]
  resources: ["*"]
  verbs: ["*"]
EOF

cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: access-$USERNAME-namespace
  namespace: $USERNAME
subjects:
- kind: User
  name: $USERNAME
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: $USERNAME-namespace
  apiGroup: rbac.authorization.k8s.io
EOF
  • others

REF:

kubespray deploy k8s

使用Kubespray自动化部署Kubernetes 1.13.1

使用Kubespray安装k8s集群

Kubernetes指南 kubespray

使用 Kubespray 在基础设施或云平台上安装 Kubernetes

openwrt

uHTTPd org/cn  

uHTTPd webserver (EN)   

uHTTPd github

Rest API supported in OpenWrt  

Run Custom Lua Script as CGI with uhttpd  

ubus (OpenWrt micro bus architecture)   

OpenWrt forum: Lua uhttpd API  

Kss PKI CA 

Calculating the CA Certificate Hash for Kubeadm

Reconstructing the Join Command for Kubeadm

join cluster after init token expired?

kubeadm token  

kubeadm 令牌   

kubeadm join   

go 

Installing Go from source 

get_go.sh (install latest)

update-golang (github repo, wget -O- https://raw.githubusercontent.com/udhos/update-golang/master/update-golang.sh  | sudo bash)

github 

go-latest (get project latest version in github)

go-github (github v3 API)

REST API v3

ovn

How to use Open Virtual Networking with Kubernetes  

如何将OVN虚拟网络连接到外部网络?

OVN代替OVS后怎样定制虚拟网络实现虚拟机与外网通信  

SDN控制器之OVN实验三:从OVN虚拟网络访问物理网络  

OVN 简介

YAML

YAML 语言教程   

K8S

Getting started 

污点和容忍度  

获取正在运行容器的 Shell  

kubectl 概述   

flannel (github)

network addons  

LUA 语言 (简易教程) 

Lua 教程| 菜鸟教程   

Lua 教程_w3cschool - 编程狮   

other

Here_document 

Get final URL after curl is redirected  

Submitting ONAP Code Reviews through the Nordix Gerrit

50 `sed` Command Examples  

kustomize 

Configuring Kubeflow with kfctl and kustomize  

Declarative Management of Kubernetes Objects Using Kustomize  

Install the kustomize CLI from source without cloning the repo   

ksonnet (github

 godoc

sigs.k8s.io/controller-runtime/pkg/client   (ListOptionsList,  controller/webhook examples/builtinshandler/enqueue_mappedeventhandler)

k8s.io/client-go/kubernetes   type Clientset

k8s.io/api/core/v1    /  k8s.io/api/apps/v1

k8s.io/apimachinery/pkg/apis/meta/v1   

k8s.io/apimachinery/pkg/fields#Set   

sigs.k8s.io/controller-runtime/pkg/builder#Builder.For

k8s.io/apimachinery/pkg/fields#Set

 Webhook

kubebuilder2.0学习笔记——搭建和使用 /进阶使用  (更多)

Writing and testing Kubernetes webhooks using Kubebuilder v2

install  cert-manager   

使用 Kubebuilder 创建自定义 K8s AdmissionWebhooks  

通过自定义资源扩展Kubernetes 

部署 cert manager (官网)

TIPS: 

1 Filed 和 label都可以做selector,但是annotations不可以

概念: Annotations(CN 注解),  label, labels vs  annotationsLabels and Selectors,  Field Selectors标签和选择算符  

 

kubectl get pods --field-selector status.phase=Running

Differentiate between Kubernetes labels vs annotations

Kubernetes labels and annotations are both ways of adding metadata to Kubernetes objects. The similarities end there, however. Kubernetes labels allow you to identify, select and operate on Kubernetes objects. Annotations are non-identifying metadata and do none of these things.

Annotations allow you to add non-identifying metadata to Kubernetes objects. Examples include phone numbers of persons responsible for the object or tool information for debugging purposes. In short, annotations can hold any kind of information that is useful and can provide context to DevOps teams.

Conclusion

Kubernetes labels are a great way of identifying and organizing Kubernetes objects and resources. Kubernetes team leads and IT managers are best placed to develop and implement Kubernetes labeling plans. By ensuring labeling conventions are followed across teams, they can tame Kubernetes environments and prevent sprawl. Since Labels make it easier to operate on Kubernetes objects in bulk, adhering to labeling conventions will also make teams more productive in the long run.

你可以使用 Kubernetes 注解为对象附加任意的非标识的元数据。客户端程序(例如工具和库)能够获取这些元数据信息。

你可以使用标签或注解将元数据附加到 Kubernetes 对象。 标签可以用来选择对象和查找满足某些条件的对象集合。 相反,注解不用于标识和选择对象。 注解中的元数据,可以很小,也可以很大,可以是结构化的,也可以是非结构化的,能够包含标签不允许的字符。

注解和标签一样,是键/值对。

 2 debug

insatll 

# https://github.com/go-delve/delve/blob/master/Documentation/usage/dlv.md
# https://github.com/go-delve/delve/blob/master/Documentation/installation/linux/install.md
go get github.com/go-delve/delve/cmd/dlv

hack

diff --git a/Makefile b/Makefile
index 5da22b5..f571f03 100644
--- a/Makefile
+++ b/Makefile
@@ -23,6 +23,7 @@ manager: generate fmt vet

 # Run against the configured Kubernetes cluster in ~/.kube/config
 run: generate fmt vet manifests
+       # dlv debug ./main.go
        go run ./main.go

 # Install CRDs into a cluster

 3. simple "Reconcile" Pseudocode  example:

# https://github.com/kubernetes/kubernetes/issues/84430
func (r *ReconcileObject) Reconcile(req reconcileRequest) {
  myObj = r.client.Get(context.TODO(), "my Namespace", "my object")
  myObj.Foo = "bar"
  sleep(30)

  r.client.Update(context.TODO(), myObj)
}

 4. Owns VS Watches 

 In icn-sdwan projects, it uses Watches for serveral related resources and customize the enqueue handler / event filter.  Manual for Watches/Owns from builder doc.

And it tells us:

func (*Builder) Owns  Uses

func (blder *Builder) Owns(object runtime.Object, opts ...OwnsOption) *Builder

Owns defines types of Objects being *generated* by the ControllerManagedBy, and configures the ControllerManagedBy to respond to create / delete / update events by *reconciling the owner object*. This is the equivalent of calling Watches(&source.Kind{Type: <ForType-forInput>}, &handler.EnqueueRequestForOwner{OwnerType: apiType, IsController: true})

func (*Builder) Watches  Uses

func (blder *Builder) Watches(src source.Source, eventhandler handler.EventHandler, opts ...WatchesOption) *Builder

Watches exposes the lower-level ControllerManagedBy Watches functions through the builder. Consider using Owns or For instead of Watches directly. Specified predicates are registered only for given source.

文档建议使用Owns.

 

 handle doc  about customize EnqueueRequestsFromMapFunc (源代码): 

func EnqueueRequestsFromMapFunc  Uses  

func EnqueueRequestsFromMapFunc(mapFN func(MapObject) []reconcile.Request) EventHandler

EnqueueRequestsFromMapFunc enqueues Requests by running a transformation function that outputs a collection of reconcile.Requests on each Event. The reconcile.Requests may be for an arbitrary set of objects defined by some user specified transformation of the source Event. (e.g. trigger Reconciler for a set of objects in response to a cluster resize event caused by adding or deleting a Node)

EnqueueRequestsFromMapFunc is frequently used to fan-out updates from one object to one or more other objects of a differing type.

For UpdateEvents which contain both a new and old object, the transformation function is run on both objects and both sets of Requests are enqueue.

 5. 理解 func set

理解go的function types

golang中 type func() 用法分析

EnqueueRequestsFromMapFunc 内部实现,使用了functions type, 根据/handler/enqueue_mapped.go的源码,实现了一个简化的例子:

  1 package main
  2 
  3 import (
  4     "fmt"
  5 )
  6 
  7 type NamespacedName struct {
  8     Namespace string
  9     Name      string
 10 }
 11 
 12 type Request struct {
 13     // NamespacedName is the name and namespace of the object to reconcile.
 14     NamespacedName
 15 }
 16 
 17 type Object interface {
 18     GetObjectKind(MapObject) []Request
 19 }
 20 
 21 type MapObject struct {
 22     Object Object 
 23 }
 24 
 25 type ObjectIns struct {
 26     Name string
 27 }
 28 
 29 func (o ObjectIns ) GetObjectKind(i MapObject) []Request {
 30     var enqueueRequest []Request
 31     return enqueueRequest 
 32 }
 33 
 34 
 35 // mapper maps an object to a collection of keys to be enqueued
 36 type mapper interface {
 37     // Map maps an object
 38     Map(MapObject) []Request
 39 }
 40 
 41 type EventHandler interface {
 42     Create(string, string)
 43 }
 44 type enqueueRequestsFromMapFunc struct {
 45     // Mapper transforms the argument into a slice of keys to be reconciled
 46     ToRequests mapper
 47 }
 48 func (e *enqueueRequestsFromMapFunc) Create(evt string, q string) {
 49     fmt.Println(evt, q)
 50 }
 51 
 52 
 53 // var _ EventHandler = &enqueueRequestsFromMapFunc{}
 54 
 55 type toRequestsFunc func(MapObject) []Request
 56 
 57 func EnqueueRequestsFromMapFunc(mapFN func(MapObject) []Request) EventHandler {
 58     return &enqueueRequestsFromMapFunc{
 59         ToRequests: toRequestsFunc(mapFN),
 60     }
 61 }
 62 
 63 func (m toRequestsFunc) Map(i MapObject) []Request {
 64     return m(i)
 65 }
 66 func (m toRequestsFunc) Say() {
 67     fmt.Println("Function sets, say hello")
 68 }
 69 
 70 func GetToRequestsFunc(r string, crliststruct string) func(h MapObject) []Request {
 71 
 72     return func(h MapObject) []Request {
 73         fmt.Println("GetToRequestsFunc r parameter", r)
 74         fmt.Println("GetToRequestsFunc crliststruct  parameter", crliststruct )
 75         var enqueueRequest []Request
 76         req := Request{
 77             NamespacedName: NamespacedName{
 78                 Name:      "meta.GetName",
 79                 Namespace: "meta.GetNamespace()",
 80             }}
 81         enqueueRequest = append(enqueueRequest, req)
 82 
 83         return enqueueRequest
 84     }
 85 }
 86 
 87 func main() {
 88     fmt.Println("Hello, playground")
 89     oi := ObjectIns{Name: "ObjectIns"}
 90     mo := MapObject{Object: oi}
 91     a := GetToRequestsFunc("requert", "test")
 92     fmt.Println("=======================")
 93     fmt.Println(a(mo))
 94     fmt.Println("=======================")
 95     fmt.Println(&a)
 96     TR := toRequestsFunc(a)
 97     fmt.Println(TR)
 98     TR.Say()
 99     fmt.Println("=======================")
100     TR.Map(mo) // == a(mo)
101     fmt.Println("=======================")
102 
103     eq := &enqueueRequestsFromMapFunc{ToRequests: toRequestsFunc(a)}
104     fmt.Println(eq)
105     // &EnqueueRequestsFromMapFunc{TR: "xxxx"}
106 }
View Code

and try it in  https://play.golang.org/

 6. 关键数据结构 和方法

apis/meta/v1#OwnerReference

pkg/handler#EnqueueRequestForOwner     

pkg/reconcile    

pkg/types#NamespacedName   

pkg/controller/controllerutil#Objectpkg/runtime#Object   

controller-runtime/pkg/event#GenericEvent    

controller-runtime/pkg/controller#example-Controller   

controller-runtime/pkg/predicate#Predicate  

controller-runtime/pkg/handler#example-EnqueueRequestsFromMapFunc  

7. kubectl 格式化输出

JSONPath 支持     

8. 获取struct的名字

Golang- Getting struct attribute name

 1 package main
 2 
 3 import (
 4     "fmt"
 5     "reflect"
 6 )
 7 
 8 type MultiQuestions struct {
 9     QuestionId   int64
10     QuestionType string
11     QuestionText string
12 }
13 
14 func (q *MultiQuestions) StructAttrName() string{
15     v := reflect.ValueOf(q).Elem()
16     t := v.Type()
17     for i := 0; i < t.NumField(); i++ {
18         fmt.Println(reflect.TypeOf(q).Elem().Field(i).Name)
19     }
20     return reflect.TypeOf(q).Elem().Field(0).Name
21 }
22 
23 func main() {
24     fmt.Println((&MultiQuestions{}).StructAttrName())
25 }

 9. EnqueueRequestsFromMapFunc系列函数和 shared_informer的关系(L18-23)

pkg/handler/enqueue_mapped.go 实现了怎么从将request 放入到workqueue中。

 1 E0921 17:51:40.304838   26318 runtime.go:78] Observed a panic: "assignment to entry in nil map" (assignment to entry in nil map)
 2 goroutine 309 [running]:
 3 k8s.io/apimachinery/pkg/util/runtime.logPanic(0x1441b80, 0x173f1a0)
 4         /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/runtime/runtime.go:74 +0xa6
 5 k8s.io/apimachinery/pkg/util/runtime.HandleCrash(0x0, 0x0, 0x0)
 6         /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/runtime/runtime.go:48 +0x89
 7 panic(0x1441b80, 0x173f1a0)
 8         /usr/local/go/src/runtime/panic.go:969 +0x175
 9 sdewan.akraino.org/sdewan/controllers.createCniInterfaces(0x0, 0x0, 0x0, 0x0, 0xc000040f80, 0xd, 0x0, 0x0, 0xc000040f99, 0x7, ...)
10         /home/vagrant/sdwan_setup/sdewan/controllers/cniinterface_controller.go:88 +0xa6
11 sdewan.akraino.org/sdewan/controllers.GetToRequestsFunc2.func1(0x7fe810117918, 0xc000601000, 0x175c5a0, 0xc000601000, 0x0, 0x0, 0x40be1f)
12         /home/vagrant/sdwan_setup/sdewan/controllers/cniinterface_controller.go:348 +0x16f
13 sigs.k8s.io/controller-runtime/pkg/handler.ToRequestsFunc.Map(0xc00061afc0, 0x7fe810117918, 0xc000601000, 0x175c5a0, 0xc000601000, 0x15b4da0, 0x7fe810117918, 0xc000986120)
14         /home/vagrant/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.6.0/pkg/handler/enqueue_mapped.go:104 +0x4e
15 sigs.k8s.io/controller-runtime/pkg/handler.(*EnqueueRequestsFromMapFunc).mapAndEnqueue(0xc000632c80, 0x1798060, 0xc000986120, 0x7fe810117918, 0xc000601000, 0x175c5a0, 0xc0006010
16 00)
17         /home/vagrant/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.6.0/pkg/handler/enqueue_mapped.go:67 +0x7c
18 sigs.k8s.io/controller-runtime/pkg/handler.(*EnqueueRequestsFromMapFunc).Create(0xc000632c80, 0x7fe810117918, 0xc000601000, 0x175c5a0, 0xc000601000, 0x1798060, 0xc000986120)
19         /home/vagrant/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.6.0/pkg/handler/enqueue_mapped.go:47 +0x67
20 sigs.k8s.io/controller-runtime/pkg/source/internal.EventHandler.OnAdd(0x1786f00, 0xc000632c80, 0x1798060, 0xc000986120, 0xc000632d50, 0x1, 0x1, 0x15b4da0, 0xc000601000)
21         /home/vagrant/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.6.0/pkg/source/internal/eventsource.go:74 +0x1a5
22 k8s.io/client-go/tools/cache.(*processorListener).run.func1()
23         /home/vagrant/go/pkg/mod/k8s.io/client-go@v0.18.2/tools/cache/shared_informer.go:744 +0xc2
24 k8s.io/apimachinery/pkg/util/wait.BackoffUntil.func1(0xc0003cf760)
25         /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:155 +0x5f
26 k8s.io/apimachinery/pkg/util/wait.BackoffUntil(0xc000365f60, 0x1753b40, 0xc0009340f0, 0xc00018e001, 0xc000936000)
27         /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:156 +0xad
28 k8s.io/apimachinery/pkg/util/wait.JitterUntil(0xc0003cf760, 0x3b9aca00, 0x0, 0x1, 0xc000936000)
29         /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:133 +0xe5
30 k8s.io/apimachinery/pkg/util/wait.Until(...)
31         /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:90
32 k8s.io/client-go/tools/cache.(*processorListener).run(0xc000730180)
33         /home/vagrant/go/pkg/mod/k8s.io/client-go@v0.18.2/tools/cache/shared_informer.go:738 +0x95
34 k8s.io/apimachinery/pkg/util/wait.(*Group).Start.func1(0xc000170140, 0xc0004c89f0)
35         /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:73 +0x51
36 created by k8s.io/apimachinery/pkg/util/wait.(*Group).Start
37         /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:71 +0x65
38 panic: assignment to entry in nil map [recovered]
39         panic: assignment to entry in nil map
View Code

 10. predicate Create/Update 与 shared_informer的关系(L7-10)

How to dump goroutine stacktraces?   

 1 runtime/debug.Stack(0x36, 0x0, 0x0)
 2         /usr/local/go/src/runtime/debug/stack.go:24 +0x9f
 3 runtime/debug.PrintStack()
 4         /usr/local/go/src/runtime/debug/stack.go:16 +0x25
 5 sdewan.akraino.org/sdewan/controllers.glob..func1(0x7fd35829a0d8, 0xc000384e80, 0x175c5a0, 0xc000384e80, 0x175c5a0)
 6         /home/vagrant/sdwan_setup/sdewan/controllers/cniinterface_controller.go:396 +0xbf
 7 sigs.k8s.io/controller-runtime/pkg/predicate.Funcs.Create(...)
 8         /home/vagrant/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.6.0/pkg/predicate/predicate.go:63
 9 sigs.k8s.io/controller-runtime/pkg/source/internal.EventHandler.OnAdd(0x1786f00, 0xc00020d690, 0x1798060, 0xc000986220, 0xc00020d720, 0x1, 0x1, 0x15b4da0, 0xc000384e80)
10         /home/vagrant/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.6.0/pkg/source/internal/eventsource.go:68 +0x130
11 k8s.io/client-go/tools/cache.(*processorListener).run.func1()
12         /home/vagrant/go/pkg/mod/k8s.io/client-go@v0.18.2/tools/cache/shared_informer.go:744 +0xc2
13 k8s.io/apimachinery/pkg/util/wait.BackoffUntil.func1(0xc000280760)
14         /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:155 +0x5f
15 k8s.io/apimachinery/pkg/util/wait.BackoffUntil(0xc000153f60, 0x1753b40, 0xc00037c0f0, 0xc000393201, 0xc0000d94a0)
16         /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:156 +0xad
17 k8s.io/apimachinery/pkg/util/wait.JitterUntil(0xc000280760, 0x3b9aca00, 0x0, 0x1, 0xc0000d94a0)
18         /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:133 +0xe5
19 k8s.io/apimachinery/pkg/util/wait.Until(...)
20         /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:90
21 k8s.io/client-go/tools/cache.(*processorListener).run(0xc000410000)
22         /home/vagrant/go/pkg/mod/k8s.io/client-go@v0.18.2/tools/cache/shared_informer.go:738 +0x95
23 k8s.io/apimachinery/pkg/util/wait.(*Group).Start.func1(0xc0001da140, 0xc00038e510)
24         /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:73 +0x51
25 created by k8s.io/apimachinery/pkg/util/wait.(*Group).Start
26         /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:71 +0x65

 11. reconcile 与  与 shared_informer的关系(L6-12)

 1 goroutine 343 [running]:
 2 runtime/debug.Stack(0xc00053a900, 0x0, 0x0)
 3         /usr/local/go/src/runtime/debug/stack.go:24 +0x9f
 4 runtime/debug.PrintStack()
 5         /usr/local/go/src/runtime/debug/stack.go:16 +0x25
 6 sdewan.akraino.org/sdewan/controllers.(*CniInterfaceReconciler).Reconcile(0xc0006c7470, 0xc0000eb179, 0x7, 0xc00002a840, 0x13, 0x2a473adbda, 0xc0004506c0, 0xc000450638, 0xc000450630)
 7         /home/vagrant/sdwan_setup/sdewan/controllers/cniinterface_controller.go:202 +0x107
 8 sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller).reconcileHandler(0xc000232000, 0x14901e0, 0xc00033a5a0, 0x1567300)
 9         /home/vagrant/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.6.0/pkg/internal/controller/controller.go:256 +0x166
10 sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller).processNextWorkItem(0xc000232000, 0x203000)
11         /home/vagrant/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.6.0/pkg/internal/controller/controller.go:232 +0xb0
12 sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller).worker(0xc000232000)
13 /home/vagrant/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.6.0/pkg/internal/controller/controller.go:211 +0x2b 14 k8s.io/apimachinery/pkg/util/wait.BackoffUntil.func1(0xc0003d64d0) 15 /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:155 +0x5f 16 k8s.io/apimachinery/pkg/util/wait.BackoffUntil(0xc0003d64d0, 0x1753b40, 0xc0003de360, 0xc00038c001, 0xc00010e240) 17 /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:156 +0xad 18 k8s.io/apimachinery/pkg/util/wait.JitterUntil(0xc0003d64d0, 0x3b9aca00, 0x0, 0xc000337701, 0xc00010e240) 19 /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:133 +0xe5 20 k8s.io/apimachinery/pkg/util/wait.Until(0xc0003d64d0, 0x3b9aca00, 0xc00010e240) 21 /home/vagrant/go/pkg/mod/k8s.io/apimachinery@v0.18.2/pkg/util/wait/wait.go:90 +0x4d 22 created by sigs.k8s.io/controller-runtime/pkg/internal/controller.(*Controller).Start.func1 23 /home/vagrant/go/pkg/mod/sigs.k8s.io/controller-runtime@v0.6.0/pkg/internal/controller/controller.go:193 +0x32d

 12.  kubernetes cluster inited by kubeadm does not start automatically after rebooting os #110

node 重启之后需要,After sudo swapoff -a , I try systemctl enable kubelet again.

13. missing .rnd  

Purpose of RANDFILE in OpenSSL?

Can't load ./.rnd into RNG #7754 (fixed in this issue)

14. 增加用户

Certificate Signing Requests (org)      

Provisioning Users and Groups for Kubernetes   (step by step, create user by kubectl)

A Practical Approach to Understanding Kubernetes Authentication    

CertificateSigningRequest / user creation   

为Kubernetes集群添加用户   

15.  RBAC

Configure RBAC in your Kubernetes Cluster (step by step)   

Authenticating  (org)

QUESTION: How to create the k8s users and groups to map to? #139  

Using RBAC Authorization  (org/ CN 使用 RBAC 鉴权)   

Users and RBAC authorizations in Kubernetes (提供了几个很有用的tools,不过有个小错误: s/access_matrix/access-matrix

 1 # Installation of KREW
 2 # Krew is a tool that makes it easy to use kubectl plugins. Krew helps you discover plugins, install and manage them on your machine. It is similar to tools like apt, dnf or brew. Krew is only compatible with kubectl v1.12 and above.
 3 
 4 set -x; cd "$(mktemp -d)" &&
 5 curl -fsSLO "https://storage.googleapis.com/krew/v0.2.1/krew.{tar.gz,yaml}" &&
 6 tar zxvf krew.tar.gz &&
 7 ./krew-"$(uname | tr '[:upper:]' '[:lower:]')_amd64" install \
 8     --manifest=krew.yaml --archive=krew.tar.gz
 9 
10 export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"
11 
12 kubectl krew install access_matrix
13 
14 kubectl access-matrix -n my-project-dev --as  $OPERATOR  # username
15 
16 go get -v github.com/aquasecurity/kubectl-who-can
17 kubectl-who-can list pods -n default
18 
19 kubectl krew install rbac-lookup
20 kubectl-rbac_lookup $OPERATOR  # username
21 
22 kubectl-rbac_lookup --kind user
23 kubectl-rbac_lookup --kind group
View Code

 

16. service account

为 Pod 配置服务账户

管理 Service Accounts   

管理服务帐号(Service Accounts)  

Access Clusters Using the Kubernetes API   

在kubernetes 集群(POD)内访问k8s API服务  

kubernetes认证和serviceaccount  

example

How To Create Kubernetes Service Account For API Access:

 1 kubectl apply -f - <<EOF
 2 apiVersion: v1
 3 kind: ServiceAccount
 4 metadata:
 5   name: api-service-account
 6 EOF
 7 
 8 kubectl create serviceaccount api-service-account
 9 
10 cat <<EOF | kubectl apply -f -
11 apiVersion: rbac.authorization.k8s.io/v1
12 kind: ClusterRole
13 metadata:
14   name: api-access
15 rules:
16   -
17     apiGroups:
18       - ""
19       - apps
20       - autoscaling
21       - batch
22       - extensions
23       - policy
24       - rbac.authorization.k8s.io
25     resources:
26       - componentstatuses
27       - configmaps
28       - daemonsets
29       - deployments
30       - events
31       - endpoints
32       - horizontalpodautoscalers
33       - ingress
34       - jobs
35       - limitranges
36       - namespaces
37       - nodes
38       - pods
39       - persistentvolumes
40       - persistentvolumeclaims
41       - resourcequotas
42       - replicasets
43       - replicationcontrollers
44       - serviceaccounts
45       - services
46     verbs: ["*"]
47   - nonResourceURLs: ["*"]
48     verbs: ["*"]
49 ---
50 apiVersion: rbac.authorization.k8s.io/v1
51 kind: ClusterRoleBinding
52 metadata:
53   name: api-access
54 roleRef:
55   apiGroup: rbac.authorization.k8s.io
56   kind: ClusterRole
57   name: api-access
58 subjects:
59 - kind: ServiceAccount
60   name: api-service-account
61   namespace: default
62 EOF
63 
64 kubectl describe serviceaccount api-service-account
65 kubectl get serviceaccount api-service-account  -o json | jq -Mr '.secrets[].name'
66 SVCSERVICE=$(kubectl get serviceaccount api-service-account  -o json | jq -Mr '.secrets[].name')
67 kubectl get secrets ${SVCSERVICE} -o json | jq -Mr '.data.token' | base64 -d
68 TOKEN=`kubectl get secrets ${SVCSERVICE} -o json | jq -Mr '.data.token' | base64 -d`
69 kubectl get endpoints -o custom-columns=MATELABLE:.metadata.labels
70 
71 
72 SERVERIP=$(kubectl get endpoints kubernetes -o jsonpath='{.subsets[0].addresses[0].ip}')
73 SERVERPORT=$(kubectl get endpoints kubernetes -o jsonpath='{.subsets[0].ports[0].port}')
74 curl -k  https://$SERVERIP:$SERVERPORT/api/v1/namespaces 
75 curl -k  https://$SERVERIP:$SERVERPORT/api/v1/namespaces -H "Authorization: Bearer $TOKEN"
76 curl -k -X GET https://$SERVERIP:$SERVERPORT/api/v1/namespaces/default/pods -H "Authorization: Bearer $TOKEN" |python3 -m json.tool
77 
78 kubectl delete ClusterRoleBinding api-access
79 kubectl delete ClusterRole api-access
80 kubectl delete serviceaccount api-service-account
View Code

k8s创建服务账号——Service Account   

k8s API使用  

k8s 名词解释:Service Account (包括: 使用Vagrant, 各种部署例子: Spark例子

16. controller examples in github

indexer  GetFieldIndexer().IndexField  

Reconcile icn provisioning_controller /  pod_controller

flannel_migration_utils   

kubernetes   

17. admission webhook 

动态准入控制   (org)

api: k8s.io/api/admissionregistration/  v1beta1

最简单的webhook,Creating Your Own Admission Controller, 纯手工写的, 起了一个server,配置了证书,监听某个地址,返回v1beta1.AdmissionReview对象。

the AdmissionReview consists of AdmissionRequest and AdmissionResponse objects ,详情请参考 Some Admission Webhook Basics  

 

 

 

 

serve 将 http请求的body反序列化为AdmissionReview, 传给了mutate 和 validating 处理函数, 具体实现的一个例子(深入理解 Kubernetes Admission Webhook)介绍及代码

红帽 Custom Admission Controllers yaml文件元素具体解读,官方配置解读  

经典例子: admission-webhook-exampleblog) 和 kube-mutating-webhook-tutorial

但是我们这个kubebuilder帮我们生成的脚手架代码基于controller-runtime的framework,比没有用上面的framwork, 我们想获取跟多的用户信息,很是受限。

// log is for logging in this package.
var cniinterfacelog = logf.Log.WithName("cniinterface-resource")

func (r *CniInterface) SetupWebhookWithManager(mgr ctrl.Manager) error {
        return ctrl.NewWebhookManagedBy(mgr).
                For(r).
                Complete()
}
      

改代码的官方例子,example-WebhookBuilder, 

controller-runtimeNewWebhookManagedBy 返回WebhookBuilder, 实现了 func (*WebhookBuilder) For  Uses 和 func (*WebhookBuilder) Complete  Uses 方法。

WebhookBuilder  的For (传入的参数要实现Validator 接口)方法返回了WebhookBuilder,Complete方法通过registerWebhooks实现了注册了三个webhook, registerDefaultingWebhook, registerValidatingWebhook, registerConversionWebhook。这三个函数最终通过controllerManager 的GetWebhookServer实现了server的Register. 我们看 registerValidatingWebhook流程,通过ValidatingWebhookFor来生成validatingHandler。 validatingHandlerHandle会通过Validator来调用Create,Update和Delete方法。

没有办法,需要自己来定制webhook,重新注册handler。 

 

Manager 提供了GetWebhookServer 方法, 返回webhook.Server, Server提供了Register 方法, 注册handler和对应的url路径。

webhookAdmissionWebhoo就是一个Handler (type HandlerFunc  Uses,参考官方example), 两个参数context.Context 和 type Request  Uses 其返回值是 type Response  Uses

posted @ 2020-09-04 23:59  lvmxh  阅读(1106)  评论(1编辑  收藏  举报