├── resourcedistribution-generator ├── data.env ├── application.properties ├── kustomization.yaml ├── README.md └── rdGenerator.yaml ├── README.md ├── hotupgrade ├── vendor │ ├── modules.txt │ └── k8s.io │ │ └── klog │ │ ├── go.mod │ │ ├── code-of-conduct.md │ │ ├── go.sum │ │ ├── OWNERS │ │ ├── .travis.yml │ │ ├── RELEASE.md │ │ ├── SECURITY_CONTACTS │ │ ├── CONTRIBUTING.md │ │ ├── klog_file.go │ │ └── README.md ├── img │ └── migration.png ├── go.mod ├── go.sum ├── migrate.sh ├── config │ ├── cloneset.yaml │ └── sidecarset.yaml ├── busybox.Dockerfile ├── Makefile ├── sidecar.Dockerfile ├── busybox │ └── main.go └── README.md ├── lifecycle-hook-controller ├── config │ ├── manager │ │ ├── kustomization.yaml │ │ └── manager.yaml │ ├── prometheus │ │ ├── kustomization.yaml │ │ └── monitor.yaml │ ├── certmanager │ │ ├── kustomization.yaml │ │ ├── kustomizeconfig.yaml │ │ └── certificate.yaml │ ├── webhook │ │ ├── kustomization.yaml │ │ ├── service.yaml │ │ └── kustomizeconfig.yaml │ ├── rbac │ │ ├── auth_proxy_client_clusterrole.yaml │ │ ├── role.yaml │ │ ├── role_binding.yaml │ │ ├── auth_proxy_role_binding.yaml │ │ ├── leader_election_role_binding.yaml │ │ ├── auth_proxy_service.yaml │ │ ├── auth_proxy_role.yaml │ │ ├── kustomization.yaml │ │ ├── sample_viewer_role.yaml │ │ ├── sample_editor_role.yaml │ │ └── leader_election_role.yaml │ ├── crd │ │ ├── kustomizeconfig.yaml │ │ └── kustomization.yaml │ └── default │ │ ├── manager_webhook_patch.yaml │ │ ├── webhookcainjection_patch.yaml │ │ ├── manager_auth_proxy_patch.yaml │ │ └── kustomization.yaml ├── README.md ├── PROJECT ├── .gitignore ├── go.mod ├── hack │ └── boilerplate.go.txt ├── Dockerfile ├── controllers │ └── predeletesample │ │ ├── artifacts │ │ └── cloneset.yaml │ │ └── sample_controller.go ├── Makefile └── main.go ├── containernetworking └── plugins │ ├── plugins │ └── ipam │ │ └── host-local │ │ ├── README.md │ │ ├── host_local_suite_test.go │ │ ├── backend │ │ ├── disk │ │ │ ├── disk_suite_test.go │ │ │ ├── lock.go │ │ │ ├── lock_test.go │ │ │ └── backend.go │ │ ├── allocator │ │ │ ├── allocator_suite_test.go │ │ │ ├── range_set_test.go │ │ │ ├── range_set.go │ │ │ ├── range.go │ │ │ ├── config.go │ │ │ └── allocator.go │ │ ├── store.go │ │ └── testing │ │ │ └── fake_store.go │ │ ├── dns.go │ │ ├── dns_test.go │ │ ├── reserved.go │ │ └── main.go │ ├── pkg │ ├── testutils │ │ ├── echo │ │ │ ├── init_test.go │ │ │ ├── server │ │ │ │ └── main.go │ │ │ ├── client │ │ │ │ └── client.go │ │ │ └── echo_test.go │ │ ├── bad_reader.go │ │ ├── ping.go │ │ ├── testing.go │ │ ├── dns.go │ │ ├── cmd.go │ │ └── netns_linux.go │ ├── ip │ │ ├── ip_suite_test.go │ │ ├── ipforward_linux_test.go │ │ ├── route_linux.go │ │ ├── cidr.go │ │ ├── ipforward_linux.go │ │ ├── addr_linux.go │ │ ├── ip.go │ │ ├── utils_linux.go │ │ └── ipmasq_linux.go │ ├── ipam │ │ ├── ipam_suite_test.go │ │ ├── ipam.go │ │ └── ipam_linux.go │ ├── link │ │ └── link_suite_test.go │ ├── utils │ │ ├── utils_suite_test.go │ │ ├── sysctl │ │ │ ├── sysctl_suite_test.go │ │ │ ├── sysctl_linux.go │ │ │ └── sysctl_linux_test.go │ │ ├── buildversion │ │ │ └── buildversion.go │ │ ├── utils.go │ │ ├── conntrack.go │ │ ├── iptables_test.go │ │ ├── iptables.go │ │ └── utils_test.go │ ├── hns │ │ └── netconf_suite_windows_test.go │ ├── ns │ │ ├── ns_suite_test.go │ │ └── README.md │ └── errors │ │ ├── errors.go │ │ └── errors_test.go │ ├── build_windows.sh │ ├── build_linux.sh │ ├── go.mod │ └── scripts │ └── release.sh ├── .gitignore ├── gitops-demo ├── rollout.yaml └── echo-server.yaml ├── guestbook ├── guestbook.yaml └── redis.yaml └── argo-demo └── echo-server.yaml /resourcedistribution-generator/data.env: -------------------------------------------------------------------------------- 1 | a=2 2 | b=3 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # samples 2 | Samples for various features in OpenKruise 3 | -------------------------------------------------------------------------------- /resourcedistribution-generator/application.properties: -------------------------------------------------------------------------------- 1 | application.data -------------------------------------------------------------------------------- /hotupgrade/vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # k8s.io/klog v1.0.0 2 | ## explicit 3 | k8s.io/klog 4 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /hotupgrade/img/migration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openkruise/samples/HEAD/hotupgrade/img/migration.png -------------------------------------------------------------------------------- /hotupgrade/go.mod: -------------------------------------------------------------------------------- 1 | module openkruise/samples/hotupgrade-demo 2 | 3 | go 1.16 4 | 5 | require k8s.io/klog v1.0.0 6 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/README.md: -------------------------------------------------------------------------------- 1 | # sample-hook-controller 2 | 3 | Sample controller for pod lifecycle hook. 4 | -------------------------------------------------------------------------------- /resourcedistribution-generator/kustomization.yaml: -------------------------------------------------------------------------------- 1 | commonLabels: 2 | app: hello 3 | generators: 4 | - rdGenerator.yaml 5 | -------------------------------------------------------------------------------- /hotupgrade/vendor/k8s.io/klog/go.mod: -------------------------------------------------------------------------------- 1 | module k8s.io/klog 2 | 3 | go 1.12 4 | 5 | require github.com/go-logr/logr v0.1.0 6 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/certmanager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - certificate.yaml 3 | 4 | configurations: 5 | - kustomizeconfig.yaml 6 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/webhook/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manifests.yaml 3 | - service.yaml 4 | 5 | configurations: 6 | - kustomizeconfig.yaml 7 | -------------------------------------------------------------------------------- /hotupgrade/vendor/k8s.io/klog/code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Kubernetes Community Code of Conduct 2 | 3 | Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md) 4 | -------------------------------------------------------------------------------- /hotupgrade/vendor/k8s.io/klog/go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= 2 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= 3 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/PROJECT: -------------------------------------------------------------------------------- 1 | domain: sample.kruise.io 2 | repo: github.com/openkruise/samples/lifecycle-hook-controller 3 | resources: 4 | - group: hook 5 | kind: Sample 6 | version: v1alpha1 7 | version: "2" 8 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/rbac/auth_proxy_client_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1beta1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: ["/metrics"] 7 | verbs: ["get"] 8 | -------------------------------------------------------------------------------- /resourcedistribution-generator/README.md: -------------------------------------------------------------------------------- 1 | Before using it, download the resourcedistributiongenerator for your system and architecture version from this [page](https://github.com/openkruise/kruise-tools/releases/latest) and place it in this directory. -------------------------------------------------------------------------------- /hotupgrade/go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= 2 | k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= 3 | k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= 4 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/README.md: -------------------------------------------------------------------------------- 1 | 2 | This document has moved to the [containernetworking/cni.dev](https://github.com/containernetworking/cni.dev) repo. 3 | 4 | You can find it online here: https://cni.dev/plugins/current/ipam/host-local/ 5 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/webhook/service.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: webhook-service 6 | namespace: system 7 | spec: 8 | ports: 9 | - port: 443 10 | targetPort: 9443 11 | selector: 12 | control-plane: controller-manager 13 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/testutils/echo/init_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestEchosvr(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "pkg/testutils/echosvr") 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /hotupgrade/migrate.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | if [ "${SIDECARSET_VERSION}" == "0" ]; then 4 | echo "empty image" 5 | exit 0 6 | fi 7 | 8 | for a in `seq 60`; 9 | do 10 | if [ -e "./result" ];then 11 | echo "check start success" 12 | rm -f ./result 13 | exit 0 14 | fi 15 | sleep 1 16 | done 17 | 18 | echo "start failed" 19 | exit 1 20 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | apiVersion: rbac.authorization.k8s.io/v1 4 | kind: ClusterRole 5 | metadata: 6 | creationTimestamp: null 7 | name: manager-role 8 | rules: 9 | - apiGroups: 10 | - "" 11 | resources: 12 | - pods 13 | verbs: 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: default 12 | namespace: system 13 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: proxy-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: proxy-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: default 12 | namespace: system 13 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: leader-election-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: Role 8 | name: leader-election-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: default 12 | namespace: system 13 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | bin/ 8 | 9 | # Test binary, built with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | .idea/ 15 | 16 | # Dependency directories (remove the comment below to include it) 17 | # vendor/ 18 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: controller-manager-metrics-service 7 | namespace: system 8 | spec: 9 | ports: 10 | - name: https 11 | port: 8443 12 | targetPort: https 13 | selector: 14 | control-plane: controller-manager 15 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: proxy-role 5 | rules: 6 | - apiGroups: ["authentication.k8s.io"] 7 | resources: 8 | - tokenreviews 9 | verbs: ["create"] 10 | - apiGroups: ["authorization.k8s.io"] 11 | resources: 12 | - subjectaccessreviews 13 | verbs: ["create"] 14 | -------------------------------------------------------------------------------- /hotupgrade/vendor/k8s.io/klog/OWNERS: -------------------------------------------------------------------------------- 1 | # See the OWNERS docs at https://go.k8s.io/owners 2 | reviewers: 3 | - jayunit100 4 | - hoegaarden 5 | - andyxning 6 | - neolit123 7 | - pohly 8 | - yagonobre 9 | - vincepri 10 | - detiber 11 | approvers: 12 | - dims 13 | - thockin 14 | - justinsb 15 | - tallclair 16 | - piosz 17 | - brancz 18 | - DirectXMan12 19 | - lavalamp 20 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/openkruise/samples/lifecycle-hook-controller 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/go-logr/logr v0.1.0 7 | github.com/onsi/ginkgo v1.11.0 8 | github.com/onsi/gomega v1.8.1 9 | github.com/openkruise/kruise-api v0.8.0 10 | k8s.io/api v0.17.2 11 | k8s.io/apimachinery v0.17.2 12 | k8s.io/client-go v0.17.2 13 | sigs.k8s.io/controller-runtime v0.5.0 14 | ) 15 | -------------------------------------------------------------------------------- /hotupgrade/vendor/k8s.io/klog/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go_import_path: k8s.io/klog 3 | dist: xenial 4 | go: 5 | - 1.9.x 6 | - 1.10.x 7 | - 1.11.x 8 | - 1.12.x 9 | script: 10 | - go get -t -v ./... 11 | - diff -u <(echo -n) <(gofmt -d .) 12 | - diff -u <(echo -n) <(golint $(go list -e ./...)) 13 | - go tool vet . || go vet . 14 | - go test -v -race ./... 15 | install: 16 | - go get golang.org/x/lint/golint 17 | -------------------------------------------------------------------------------- /containernetworking/plugins/build_windows.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | cd "$(dirname "$0")" 4 | 5 | export GO="${GO:-go}" 6 | export GOOS=windows 7 | export GOFLAGS="${GOFLAGS} -mod=vendor" 8 | echo "$GOFLAGS" 9 | 10 | PLUGINS=$(cat plugins/windows_only.txt | dos2unix ) 11 | for d in $PLUGINS; do 12 | plugin="$(basename "$d").exe" 13 | echo "building $plugin" 14 | $GO build -o "${PWD}/bin/$plugin" "$@" ./"${d}" 15 | done 16 | -------------------------------------------------------------------------------- /hotupgrade/config/cloneset.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps.kruise.io/v1alpha1 2 | kind: CloneSet 3 | metadata: 4 | labels: 5 | app: hotupgrade 6 | name: busybox 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: hotupgrade 12 | template: 13 | metadata: 14 | labels: 15 | app: hotupgrade 16 | spec: 17 | containers: 18 | - name: busybox 19 | image: openkruise/hotupgrade-sample:busybox 20 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Prometheus Monitor Service (Metrics) 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | labels: 7 | control-plane: controller-manager 8 | name: controller-manager-metrics-monitor 9 | namespace: system 10 | spec: 11 | endpoints: 12 | - path: /metrics 13 | port: https 14 | selector: 15 | matchLabels: 16 | control-plane: controller-manager 17 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - role.yaml 3 | - role_binding.yaml 4 | - leader_election_role.yaml 5 | - leader_election_role_binding.yaml 6 | # Comment the following 4 lines if you want to disable 7 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy) 8 | # which protects your /metrics endpoint. 9 | - auth_proxy_service.yaml 10 | - auth_proxy_role.yaml 11 | - auth_proxy_role_binding.yaml 12 | - auth_proxy_client_clusterrole.yaml 13 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/rbac/sample_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view samples. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: sample-viewer-role 6 | rules: 7 | - apiGroups: 8 | - hook.sample.kruise.io 9 | resources: 10 | - samples 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - hook.sample.kruise.io 17 | resources: 18 | - samples/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/certmanager/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This configuration is for teaching kustomize how to update name ref and var substitution 2 | nameReference: 3 | - kind: Issuer 4 | group: cert-manager.io 5 | fieldSpecs: 6 | - kind: Certificate 7 | group: cert-manager.io 8 | path: spec/issuerRef/name 9 | 10 | varReference: 11 | - kind: Certificate 12 | group: cert-manager.io 13 | path: spec/commonName 14 | - kind: Certificate 15 | group: cert-manager.io 16 | path: spec/dnsNames 17 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/rbac/sample_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit samples. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: sample-editor-role 6 | rules: 7 | - apiGroups: 8 | - hook.sample.kruise.io 9 | resources: 10 | - samples 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - hook.sample.kruise.io 21 | resources: 22 | - samples/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /hotupgrade/vendor/k8s.io/klog/RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release Process 2 | 3 | The `klog` is released on an as-needed basis. The process is as follows: 4 | 5 | 1. An issue is proposing a new release with a changelog since the last release 6 | 1. All [OWNERS](OWNERS) must LGTM this release 7 | 1. An OWNER runs `git tag -s $VERSION` and inserts the changelog and pushes the tag with `git push $VERSION` 8 | 1. The release issue is closed 9 | 1. An announcement email is sent to `kubernetes-dev@googlegroups.com` with the subject `[ANNOUNCE] kubernetes-template-project $VERSION is released` 10 | -------------------------------------------------------------------------------- /containernetworking/plugins/build_linux.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | cd "$(dirname "$0")" 4 | 5 | if [ "$(uname)" == "Darwin" ]; then 6 | export GOOS="${GOOS:-linux}" 7 | fi 8 | 9 | export GOFLAGS="${GOFLAGS} -mod=vendor" 10 | 11 | mkdir -p "${PWD}/bin" 12 | 13 | echo "Building plugins ${GOOS}" 14 | PLUGINS="plugins/ipam/host-local" 15 | for d in $PLUGINS; do 16 | if [ -d "$d" ]; then 17 | plugin="$(basename "$d")" 18 | if [ "${plugin}" != "windows" ]; then 19 | echo " $plugin" 20 | ${GO:-go} build -o "${PWD}/bin/$plugin" "$@" ./"$d" 21 | fi 22 | fi 23 | done 24 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/crd/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD 2 | nameReference: 3 | - kind: Service 4 | version: v1 5 | fieldSpecs: 6 | - kind: CustomResourceDefinition 7 | group: apiextensions.k8s.io 8 | path: spec/conversion/webhookClientConfig/service/name 9 | 10 | namespace: 11 | - kind: CustomResourceDefinition 12 | group: apiextensions.k8s.io 13 | path: spec/conversion/webhookClientConfig/service/namespace 14 | create: false 15 | 16 | varReference: 17 | - path: metadata/annotations 18 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: leader-election-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - create 16 | - update 17 | - patch 18 | - delete 19 | - apiGroups: 20 | - "" 21 | resources: 22 | - configmaps/status 23 | verbs: 24 | - get 25 | - update 26 | - patch 27 | - apiGroups: 28 | - "" 29 | resources: 30 | - events 31 | verbs: 32 | - create 33 | -------------------------------------------------------------------------------- /hotupgrade/config/sidecarset.yaml: -------------------------------------------------------------------------------- 1 | # sidecarset.yaml 2 | apiVersion: apps.kruise.io/v1alpha1 3 | kind: SidecarSet 4 | metadata: 5 | name: hotupgrade-sidecarset 6 | spec: 7 | selector: 8 | matchLabels: 9 | app: hotupgrade 10 | containers: 11 | - name: sidecar 12 | image: openkruise/hotupgrade-sample:sidecarv1 13 | imagePullPolicy: Always 14 | lifecycle: 15 | postStart: 16 | exec: 17 | command: 18 | - /bin/sh 19 | - /migrate.sh 20 | upgradeStrategy: 21 | upgradeType: HotUpgrade 22 | hotUpgradeEmptyImage: openkruise/hotupgrade-sample:empty 23 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Kruise Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/default/manager_webhook_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | ports: 12 | - containerPort: 9443 13 | name: webhook-server 14 | protocol: TCP 15 | volumeMounts: 16 | - mountPath: /tmp/k8s-webhook-server/serving-certs 17 | name: cert 18 | readOnly: true 19 | volumes: 20 | - name: cert 21 | secret: 22 | defaultMode: 420 23 | secretName: webhook-server-cert 24 | -------------------------------------------------------------------------------- /hotupgrade/vendor/k8s.io/klog/SECURITY_CONTACTS: -------------------------------------------------------------------------------- 1 | # Defined below are the security contacts for this repo. 2 | # 3 | # They are the contact point for the Product Security Committee to reach out 4 | # to for triaging and handling of incoming issues. 5 | # 6 | # The below names agree to abide by the 7 | # [Embargo Policy](https://git.k8s.io/security/private-distributors-list.md#embargo-policy) 8 | # and will be removed and replaced if they violate that agreement. 9 | # 10 | # DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE 11 | # INSTRUCTIONS AT https://kubernetes.io/security/ 12 | 13 | dims 14 | thockin 15 | justinsb 16 | tallclair 17 | piosz 18 | brancz 19 | DirectXMan12 20 | lavalamp 21 | -------------------------------------------------------------------------------- /hotupgrade/busybox.Dockerfile: -------------------------------------------------------------------------------- 1 | # Build the manager binary 2 | FROM golang:1.15 as builder 3 | 4 | WORKDIR /workspace 5 | # Copy the Go Modules manifests 6 | COPY go.mod go.mod 7 | COPY go.sum go.sum 8 | # cache deps before building and copying source so that we don't need to re-download as much 9 | # and so that source changes don't invalidate our downloaded layer 10 | #RUN go mod download 11 | 12 | # Copy the go source 13 | COPY busybox/main.go main.go 14 | COPY vendor/ vendor/ 15 | 16 | ARG VERSION 17 | # Build 18 | RUN CGO_ENABLED=0 GO111MODULE=on go build -ldflags "-X main.Version=${VERSION}" -mod=vendor -a -o manager main.go 19 | 20 | FROM busybox:latest 21 | 22 | COPY --from=builder /workspace/manager ./ 23 | ENTRYPOINT ["/manager"] 24 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/default/webhookcainjection_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch add annotation to admission webhook config and 2 | # the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. 3 | apiVersion: admissionregistration.k8s.io/v1beta1 4 | kind: MutatingWebhookConfiguration 5 | metadata: 6 | name: mutating-webhook-configuration 7 | annotations: 8 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 9 | --- 10 | apiVersion: admissionregistration.k8s.io/v1beta1 11 | kind: ValidatingWebhookConfiguration 12 | metadata: 13 | name: validating-webhook-configuration 14 | annotations: 15 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 16 | -------------------------------------------------------------------------------- /hotupgrade/Makefile: -------------------------------------------------------------------------------- 1 | # Platforms to build the image for 2 | PLATFORMS ?= linux/amd64,linux/arm64,linux/arm 3 | 4 | # Run go fmt against code 5 | fmt: 6 | go fmt ./... 7 | 8 | # Run go vet against code 9 | vet: 10 | go vet ./... 11 | 12 | # Build the docker image 13 | docker-build: fmt vet 14 | docker build --pull --no-cache --build-arg VERSION=v1 -f sidecar.Dockerfile . -t openkruise/hotupgrade-sample:sidecarv1 15 | docker build --pull --no-cache --build-arg VERSION=v2 -f sidecar.Dockerfile . -t openkruise/hotupgrade-sample:sidecarv2 16 | docker build --pull --no-cache -f busybox.Dockerfile . -t openkruise/hotupgrade-sample:busybox 17 | docker build --pull --no-cache --build-arg VERSION=empty -f sidecar.Dockerfile . -t openkruise/hotupgrade-sample:empty 18 | -------------------------------------------------------------------------------- /hotupgrade/sidecar.Dockerfile: -------------------------------------------------------------------------------- 1 | # Build the manager binary 2 | FROM golang:1.15 as builder 3 | 4 | WORKDIR /workspace 5 | # Copy the Go Modules manifests 6 | COPY go.mod go.mod 7 | COPY go.sum go.sum 8 | # cache deps before building and copying source so that we don't need to re-download as much 9 | # and so that source changes don't invalidate our downloaded layer 10 | #RUN go mod download 11 | 12 | # Copy the go source 13 | COPY sidecar/main.go main.go 14 | COPY vendor/ vendor/ 15 | 16 | ARG VERSION 17 | # Build 18 | RUN CGO_ENABLED=0 GO111MODULE=on go build -ldflags "-X main.Version=${VERSION}" -mod=vendor -a -o manager main.go 19 | 20 | FROM busybox:latest 21 | 22 | COPY --from=builder /workspace/manager ./ 23 | COPY migrate.sh ./ 24 | ENTRYPOINT ["/manager"] 25 | -------------------------------------------------------------------------------- /gitops-demo/rollout.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rollouts.kruise.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: rollouts-demo 5 | annotations: 6 | rollouts.kruise.io/rolling-style: partition 7 | spec: 8 | objectRef: 9 | workloadRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: echo-server 13 | strategy: 14 | canary: 15 | steps: 16 | - replicas: 10% 17 | weight: 5 18 | - replicas: 20% 19 | weight: 20 20 | pause: 21 | duration: 10 22 | - replicas: 100% 23 | weight: 100 24 | pause: 25 | duration: 0 26 | trafficRoutings: 27 | - service: echo-server 28 | ingress: 29 | classType: nginx 30 | name: ingress-demo 31 | -------------------------------------------------------------------------------- /guestbook/guestbook.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps.kruise.io/v1alpha1 2 | kind: CloneSet 3 | metadata: 4 | name: guestbook 5 | labels: 6 | app: guestbook 7 | spec: 8 | replicas: 3 9 | selector: 10 | matchLabels: 11 | app: guestbook 12 | template: 13 | metadata: 14 | labels: 15 | app: guestbook 16 | spec: 17 | containers: 18 | - name: guestbook 19 | image: 'registry.k8s.io/guestbook:v3' 20 | env: 21 | - name: "VERSION" 22 | value: "v1" 23 | ports: 24 | - name: http-server 25 | containerPort: 3000 26 | --- 27 | kind: Service 28 | apiVersion: v1 29 | metadata: 30 | name: guestbook 31 | labels: 32 | app: guestbook 33 | spec: 34 | ports: 35 | - port: 3000 36 | targetPort: http-server 37 | selector: 38 | app: guestbook 39 | type: LoadBalancer 40 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This kustomization.yaml is not intended to be run by itself, 2 | # since it depends on service name and namespace that are out of this kustomize package. 3 | # It should be run by config/default 4 | resources: 5 | # +kubebuilder:scaffold:crdkustomizeresource 6 | 7 | patchesStrategicMerge: 8 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. 9 | # patches here are for enabling the conversion webhook for each CRD 10 | # +kubebuilder:scaffold:crdkustomizewebhookpatch 11 | 12 | # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. 13 | # patches here are for enabling the CA injection for each CRD 14 | # +kubebuilder:scaffold:crdkustomizecainjectionpatch 15 | 16 | # the following config is for teaching kustomize how to do kustomization for CRDs. 17 | configurations: 18 | - kustomizeconfig.yaml 19 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/default/manager_auth_proxy_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch inject a sidecar container which is a HTTP proxy for the 2 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: controller-manager 7 | namespace: system 8 | spec: 9 | template: 10 | spec: 11 | containers: 12 | - name: kube-rbac-proxy 13 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 14 | args: 15 | - "--secure-listen-address=0.0.0.0:8443" 16 | - "--upstream=http://127.0.0.1:8080/" 17 | - "--logtostderr=true" 18 | - "--v=10" 19 | ports: 20 | - containerPort: 8443 21 | name: https 22 | - name: manager 23 | args: 24 | - "--metrics-addr=127.0.0.1:8080" 25 | - "--enable-leader-election" 26 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/ip/ip_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ip_test 16 | 17 | import ( 18 | . "github.com/onsi/ginkgo" 19 | . "github.com/onsi/gomega" 20 | 21 | "testing" 22 | ) 23 | 24 | func TestIp(t *testing.T) { 25 | RegisterFailHandler(Fail) 26 | RunSpecs(t, "pkg/ip") 27 | } 28 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/ipam/ipam_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ipam_test 16 | 17 | import ( 18 | . "github.com/onsi/ginkgo" 19 | . "github.com/onsi/gomega" 20 | 21 | "testing" 22 | ) 23 | 24 | func TestIpam(t *testing.T) { 25 | RegisterFailHandler(Fail) 26 | RunSpecs(t, "pkg/ipam") 27 | } 28 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/link/link_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package link_test 16 | 17 | import ( 18 | "testing" 19 | 20 | . "github.com/onsi/ginkgo" 21 | . "github.com/onsi/gomega" 22 | ) 23 | 24 | func TestIp(t *testing.T) { 25 | RegisterFailHandler(Fail) 26 | RunSpecs(t, "pkg/link") 27 | } 28 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/utils/utils_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils_test 16 | 17 | import ( 18 | . "github.com/onsi/ginkgo" 19 | . "github.com/onsi/gomega" 20 | 21 | "testing" 22 | ) 23 | 24 | func TestUtils(t *testing.T) { 25 | RegisterFailHandler(Fail) 26 | RunSpecs(t, "pkg/utils") 27 | } 28 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build the manager binary 2 | FROM golang:1.13 as builder 3 | 4 | WORKDIR /workspace 5 | # Copy the Go Modules manifests 6 | COPY go.mod go.mod 7 | COPY go.sum go.sum 8 | # cache deps before building and copying source so that we don't need to re-download as much 9 | # and so that source changes don't invalidate our downloaded layer 10 | RUN go mod download 11 | 12 | # Copy the go source 13 | COPY main.go main.go 14 | COPY api/ api/ 15 | COPY controllers/ controllers/ 16 | 17 | # Build 18 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go 19 | 20 | # Use distroless as minimal base image to package the manager binary 21 | # Refer to https://github.com/GoogleContainerTools/distroless for more details 22 | FROM gcr.io/distroless/static:nonroot 23 | WORKDIR / 24 | COPY --from=builder /workspace/manager . 25 | USER nonroot:nonroot 26 | 27 | ENTRYPOINT ["/manager"] 28 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/hns/netconf_suite_windows_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package hns 15 | 16 | import ( 17 | . "github.com/onsi/ginkgo" 18 | . "github.com/onsi/gomega" 19 | 20 | "testing" 21 | ) 22 | 23 | func TestNetConf(t *testing.T) { 24 | RegisterFailHandler(Fail) 25 | RunSpecs(t, "NetConf Suite") 26 | } 27 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/webhook/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # the following config is for teaching kustomize where to look at when substituting vars. 2 | # It requires kustomize v2.1.0 or newer to work properly. 3 | nameReference: 4 | - kind: Service 5 | version: v1 6 | fieldSpecs: 7 | - kind: MutatingWebhookConfiguration 8 | group: admissionregistration.k8s.io 9 | path: webhooks/clientConfig/service/name 10 | - kind: ValidatingWebhookConfiguration 11 | group: admissionregistration.k8s.io 12 | path: webhooks/clientConfig/service/name 13 | 14 | namespace: 15 | - kind: MutatingWebhookConfiguration 16 | group: admissionregistration.k8s.io 17 | path: webhooks/clientConfig/service/namespace 18 | create: true 19 | - kind: ValidatingWebhookConfiguration 20 | group: admissionregistration.k8s.io 21 | path: webhooks/clientConfig/service/namespace 22 | create: true 23 | 24 | varReference: 25 | - path: metadata/annotations 26 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/utils/sysctl/sysctl_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2020 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sysctl_test 16 | 17 | import ( 18 | "testing" 19 | 20 | . "github.com/onsi/ginkgo" 21 | . "github.com/onsi/gomega" 22 | ) 23 | 24 | func TestSysctl(t *testing.T) { 25 | RegisterFailHandler(Fail) 26 | RunSpecs(t, "Sysctl Suite") 27 | } 28 | -------------------------------------------------------------------------------- /resourcedistribution-generator/rdGenerator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps.kruise.io/v1alpha1 2 | kind: ResourceDistributionGenerator 3 | metadata: 4 | name: rdname 5 | annotations: 6 | config.kubernetes.io/function: | 7 | exec: 8 | path: ./resourcedistributiongenerator 9 | resource: 10 | resourceKind: ConfigMap 11 | resourceName: cmname 12 | envs: 13 | - data.env 14 | files: 15 | - application.properties 16 | literals: 17 | - JAVA_HOME=/opt/java/jdk 18 | - foo=bar 19 | - one=1 20 | resourceOptions: 21 | annotations: 22 | dashboard: "1" 23 | immutable: true 24 | options: 25 | labels: 26 | app.kubernetes.io/name: "app1" 27 | targets: 28 | includedNamespaces: 29 | - ns-1 30 | - ns-2 31 | namespaceLabelSelector: 32 | matchLabels: 33 | group: "test" 34 | matchExpressions: 35 | - key: exc 36 | operator: NotIn 37 | values: 38 | - abc 39 | - e -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/host_local_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | . "github.com/onsi/ginkgo" 19 | . "github.com/onsi/gomega" 20 | 21 | "testing" 22 | ) 23 | 24 | func TestHostLocal(t *testing.T) { 25 | RegisterFailHandler(Fail) 26 | RunSpecs(t, "plugins/ipam/host-local") 27 | } 28 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/backend/disk/disk_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package disk 16 | 17 | import ( 18 | . "github.com/onsi/ginkgo" 19 | . "github.com/onsi/gomega" 20 | 21 | "testing" 22 | ) 23 | 24 | func TestLock(t *testing.T) { 25 | RegisterFailHandler(Fail) 26 | RunSpecs(t, "plugins/ipam/host-local/backend/disk") 27 | } 28 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: system 7 | --- 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | metadata: 11 | name: controller-manager 12 | namespace: system 13 | labels: 14 | control-plane: controller-manager 15 | spec: 16 | selector: 17 | matchLabels: 18 | control-plane: controller-manager 19 | replicas: 1 20 | template: 21 | metadata: 22 | labels: 23 | control-plane: controller-manager 24 | spec: 25 | containers: 26 | - command: 27 | - /manager 28 | args: 29 | - --enable-leader-election 30 | image: controller:latest 31 | name: manager 32 | resources: 33 | limits: 34 | cpu: 100m 35 | memory: 30Mi 36 | requests: 37 | cpu: 100m 38 | memory: 20Mi 39 | terminationGracePeriodSeconds: 10 40 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/backend/allocator/allocator_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package allocator_test 16 | 17 | import ( 18 | . "github.com/onsi/ginkgo" 19 | . "github.com/onsi/gomega" 20 | 21 | "testing" 22 | ) 23 | 24 | func TestAllocator(t *testing.T) { 25 | RegisterFailHandler(Fail) 26 | RunSpecs(t, "plugins/ipam/host-local/backend/allocator") 27 | } 28 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/ip/ipforward_linux_test.go: -------------------------------------------------------------------------------- 1 | package ip 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "time" 7 | 8 | . "github.com/onsi/ginkgo" 9 | . "github.com/onsi/gomega" 10 | ) 11 | 12 | var _ = Describe("IpforwardLinux", func() { 13 | It("echo1 must not write the file if content is 1", func() { 14 | file, err := ioutil.TempFile(os.TempDir(), "containernetworking") 15 | Expect(err).NotTo(HaveOccurred()) 16 | defer os.Remove(file.Name()) 17 | err = echo1(file.Name()) 18 | Expect(err).NotTo(HaveOccurred()) 19 | statBefore, err := file.Stat() 20 | Expect(err).NotTo(HaveOccurred()) 21 | 22 | // take a duration here, otherwise next file modification operation time 23 | // will be same as previous one. 24 | time.Sleep(100 * time.Millisecond) 25 | 26 | err = echo1(file.Name()) 27 | Expect(err).NotTo(HaveOccurred()) 28 | statAfter, err := file.Stat() 29 | Expect(err).NotTo(HaveOccurred()) 30 | Expect(statBefore.ModTime()).To(Equal(statAfter.ModTime())) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/utils/buildversion/buildversion.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Buildversion is a destination for the linker trickery so we can auto 16 | // set the build-version 17 | package buildversion 18 | 19 | import "fmt" 20 | 21 | // This is overridden in the linker script 22 | var BuildVersion = "version unknown" 23 | 24 | func BuildString(pluginName string) string { 25 | return fmt.Sprintf("CNI %s plugin %s", pluginName, BuildVersion) 26 | } 27 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/ns/ns_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ns_test 16 | 17 | import ( 18 | "math/rand" 19 | "runtime" 20 | 21 | . "github.com/onsi/ginkgo" 22 | "github.com/onsi/ginkgo/config" 23 | . "github.com/onsi/gomega" 24 | 25 | "testing" 26 | ) 27 | 28 | func TestNs(t *testing.T) { 29 | rand.Seed(config.GinkgoConfig.RandomSeed) 30 | runtime.LockOSThread() 31 | 32 | RegisterFailHandler(Fail) 33 | RunSpecs(t, "pkg/ns") 34 | } 35 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/testutils/bad_reader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package testutils 16 | 17 | import "errors" 18 | 19 | // BadReader is an io.Reader which always errors 20 | type BadReader struct { 21 | Error error 22 | } 23 | 24 | func (r *BadReader) Read(buffer []byte) (int, error) { 25 | if r.Error != nil { 26 | return 0, r.Error 27 | } 28 | return 0, errors.New("banana") 29 | } 30 | 31 | func (r *BadReader) Close() error { 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/backend/store.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package backend 16 | 17 | import "net" 18 | 19 | type Store interface { 20 | Lock() error 21 | Unlock() error 22 | Close() error 23 | Reserve(id string, ifname string, ip net.IP, rangeID string) (bool, error) 24 | LastReservedIP(rangeID string) (net.IP, error) 25 | Release(ip net.IP) error 26 | ReleaseByID(id string, ifname string) error 27 | GetByID(id string, ifname string) []net.IP 28 | } 29 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/certmanager/certificate.yaml: -------------------------------------------------------------------------------- 1 | # The following manifests contain a self-signed issuer CR and a certificate CR. 2 | # More document can be found at https://docs.cert-manager.io 3 | # WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for 4 | # breaking changes 5 | apiVersion: cert-manager.io/v1alpha2 6 | kind: Issuer 7 | metadata: 8 | name: selfsigned-issuer 9 | namespace: system 10 | spec: 11 | selfSigned: {} 12 | --- 13 | apiVersion: cert-manager.io/v1alpha2 14 | kind: Certificate 15 | metadata: 16 | name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml 17 | namespace: system 18 | spec: 19 | # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize 20 | dnsNames: 21 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc 22 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local 23 | issuerRef: 24 | kind: Issuer 25 | name: selfsigned-issuer 26 | secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize 27 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/controllers/predeletesample/artifacts/cloneset.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps.kruise.io/v1alpha1 2 | kind: CloneSet 3 | metadata: 4 | name: cloneset-hook-example 5 | spec: 6 | replicas: 3 7 | selector: 8 | matchLabels: 9 | app: guestbook 10 | lifecycle: 11 | preDelete: 12 | labelsHandler: 13 | hook.example.kruise.io/unready-blocker: "true" 14 | template: 15 | metadata: 16 | labels: 17 | app: guestbook 18 | hook.example.kruise.io/unready-blocker: "true" 19 | spec: 20 | affinity: 21 | podAntiAffinity: 22 | preferredDuringSchedulingIgnoredDuringExecution: 23 | - podAffinityTerm: 24 | labelSelector: 25 | matchExpressions: 26 | - key: app 27 | operator: In 28 | values: 29 | - guestbook 30 | topologyKey: kubernetes.io/hostname 31 | weight: 100 32 | containers: 33 | - name: main 34 | image: registry.cn-hangzhou.aliyuncs.com/kruise-test/guestbook:v1 35 | updateStrategy: 36 | type: InPlaceIfPossible 37 | maxUnavailable: 20% 38 | inPlaceUpdateStrategy: 39 | gracePeriodSeconds: 3 40 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/ipam/ipam.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ipam 16 | 17 | import ( 18 | "context" 19 | "github.com/containernetworking/cni/pkg/invoke" 20 | "github.com/containernetworking/cni/pkg/types" 21 | ) 22 | 23 | func ExecAdd(plugin string, netconf []byte) (types.Result, error) { 24 | return invoke.DelegateAdd(context.TODO(), plugin, netconf, nil) 25 | } 26 | 27 | func ExecCheck(plugin string, netconf []byte) error { 28 | return invoke.DelegateCheck(context.TODO(), plugin, netconf, nil) 29 | } 30 | 31 | func ExecDel(plugin string, netconf []byte) error { 32 | return invoke.DelegateDel(context.TODO(), plugin, netconf, nil) 33 | } 34 | -------------------------------------------------------------------------------- /hotupgrade/busybox/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Kruise Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "io/ioutil" 21 | "k8s.io/klog" 22 | "net/http" 23 | "time" 24 | ) 25 | 26 | func main() { 27 | for { 28 | resp, err := http.Get("http://127.0.0.1:9091/serve") 29 | if err != nil { 30 | klog.Errorf("request sidecar(http://127.0.0.1:9091/serve) failed: %s", err.Error()) 31 | return 32 | } 33 | 34 | by, err := ioutil.ReadAll(resp.Body) 35 | if err != nil { 36 | klog.Errorf("read resp body failed: %s", err.Error()) 37 | return 38 | } 39 | klog.Infof("request sidecar server success, and response(body=%s)", string(by)) 40 | time.Sleep(time.Millisecond * 100) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/errors/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package errors 16 | 17 | import "fmt" 18 | 19 | // Annotate is used to add extra context to an existing error. The return will be 20 | // a new error which carries error message from both context message and existing error. 21 | func Annotate(err error, message string) error { 22 | if err == nil { 23 | return nil 24 | } 25 | 26 | return fmt.Errorf("%s: %v", message, err) 27 | } 28 | 29 | // Annotatef is used to add extra context with args to an existing error. The return will be 30 | // a new error which carries error message from both context message and existing error. 31 | func Annotatef(err error, message string, args ...interface{}) error { 32 | if err == nil { 33 | return nil 34 | } 35 | 36 | return fmt.Errorf("%s: %v", fmt.Sprintf(message, args...), err) 37 | } 38 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/ip/route_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2017 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ip 16 | 17 | import ( 18 | "net" 19 | 20 | "github.com/vishvananda/netlink" 21 | ) 22 | 23 | // AddRoute adds a universally-scoped route to a device. 24 | func AddRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error { 25 | return netlink.RouteAdd(&netlink.Route{ 26 | LinkIndex: dev.Attrs().Index, 27 | Scope: netlink.SCOPE_UNIVERSE, 28 | Dst: ipn, 29 | Gw: gw, 30 | }) 31 | } 32 | 33 | // AddHostRoute adds a host-scoped route to a device. 34 | func AddHostRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error { 35 | return netlink.RouteAdd(&netlink.Route{ 36 | LinkIndex: dev.Attrs().Index, 37 | Scope: netlink.SCOPE_HOST, 38 | Dst: ipn, 39 | Gw: gw, 40 | }) 41 | } 42 | 43 | // AddDefaultRoute sets the default route on the given gateway. 44 | func AddDefaultRoute(gw net.IP, dev netlink.Link) error { 45 | _, defNet, _ := net.ParseCIDR("0.0.0.0/0") 46 | return AddRoute(defNet, gw, dev) 47 | } 48 | -------------------------------------------------------------------------------- /hotupgrade/vendor/k8s.io/klog/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://github.com/kubernetes/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt: 4 | 5 | _As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._ 6 | 7 | ## Getting Started 8 | 9 | We have full documentation on how to get started contributing here: 10 | 11 | - [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests 12 | - [Kubernetes Contributor Guide](http://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](http://git.k8s.io/community/contributors/guide#contributing) 13 | - [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet.md) - Common resources for existing developers 14 | 15 | ## Mentorship 16 | 17 | - [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers! 18 | 19 | ## Contact Information 20 | 21 | - [Slack](https://kubernetes.slack.com/messages/sig-architecture) 22 | - [Mailing List](https://groups.google.com/forum/#!forum/kubernetes-sig-architecture) 23 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/testutils/ping.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package testutils 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "net" 21 | "os/exec" 22 | "strconv" 23 | "syscall" 24 | ) 25 | 26 | // Ping shells out to the `ping` command. Returns nil if successful. 27 | func Ping(saddr, daddr string, timeoutSec int) error { 28 | ip := net.ParseIP(saddr) 29 | if ip == nil { 30 | return fmt.Errorf("failed to parse IP %q", saddr) 31 | } 32 | 33 | bin := "ping6" 34 | if ip.To4() != nil { 35 | bin = "ping" 36 | } 37 | 38 | args := []string{ 39 | "-c", "1", 40 | "-W", strconv.Itoa(timeoutSec), 41 | "-I", saddr, 42 | daddr, 43 | } 44 | 45 | cmd := exec.Command(bin, args...) 46 | var stderr bytes.Buffer 47 | cmd.Stderr = &stderr 48 | 49 | if err := cmd.Run(); err != nil { 50 | switch e := err.(type) { 51 | case *exec.ExitError: 52 | return fmt.Errorf("%v exit status %d: %s", 53 | args, e.Sys().(syscall.WaitStatus).ExitStatus(), 54 | stderr.String()) 55 | default: 56 | return err 57 | } 58 | } 59 | 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/backend/disk/lock.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package disk 16 | 17 | import ( 18 | "github.com/alexflint/go-filemutex" 19 | "os" 20 | "path" 21 | ) 22 | 23 | // FileLock wraps os.File to be used as a lock using flock 24 | type FileLock struct { 25 | f *filemutex.FileMutex 26 | } 27 | 28 | // NewFileLock opens file/dir at path and returns unlocked FileLock object 29 | func NewFileLock(lockPath string) (*FileLock, error) { 30 | fi, err := os.Stat(lockPath) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | if fi.IsDir() { 36 | lockPath = path.Join(lockPath, "lock") 37 | } 38 | 39 | f, err := filemutex.New(lockPath) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | return &FileLock{f}, nil 45 | } 46 | 47 | func (l *FileLock) Close() error { 48 | return l.f.Close() 49 | } 50 | 51 | // Lock acquires an exclusive lock 52 | func (l *FileLock) Lock() error { 53 | return l.f.Lock() 54 | } 55 | 56 | // Unlock releases the lock 57 | func (l *FileLock) Unlock() error { 58 | return l.f.Unlock() 59 | } 60 | -------------------------------------------------------------------------------- /guestbook/redis.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps.kruise.io/v1alpha1 2 | kind: CloneSet 3 | metadata: 4 | name: redis-master 5 | labels: 6 | app: redis 7 | role: master 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: redis 13 | role: master 14 | template: 15 | metadata: 16 | labels: 17 | app: redis 18 | role: master 19 | spec: 20 | containers: 21 | - name: redis-master 22 | image: 'registry.k8s.io/redis:e2e' 23 | ports: 24 | - name: redis-server 25 | containerPort: 6379 26 | --- 27 | kind: Service 28 | apiVersion: v1 29 | metadata: 30 | name: redis-master 31 | labels: 32 | app: redis 33 | role: master 34 | spec: 35 | ports: 36 | - port: 6379 37 | targetPort: redis-server 38 | selector: 39 | app: redis 40 | role: master 41 | --- 42 | apiVersion: apps.kruise.io/v1alpha1 43 | kind: CloneSet 44 | metadata: 45 | name: redis-slave 46 | labels: 47 | app: redis 48 | role: slave 49 | spec: 50 | replicas: 2 51 | selector: 52 | matchLabels: 53 | app: redis 54 | role: slave 55 | template: 56 | metadata: 57 | labels: 58 | app: redis 59 | role: slave 60 | spec: 61 | containers: 62 | - name: redis-slave 63 | image: 'registry.k8s.io/redis-slave:v2' 64 | ports: 65 | - name: redis-server 66 | containerPort: 6379 67 | --- 68 | kind: Service 69 | apiVersion: v1 70 | metadata: 71 | name: redis-slave 72 | labels: 73 | app: redis 74 | role: slave 75 | spec: 76 | ports: 77 | - port: 6379 78 | targetPort: redis-server 79 | selector: 80 | app: redis 81 | role: slave 82 | -------------------------------------------------------------------------------- /containernetworking/plugins/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/containernetworking/plugins 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/Microsoft/hcsshim v0.8.20 7 | github.com/alexflint/go-filemutex v1.1.0 8 | github.com/buger/jsonparser v1.1.1 9 | github.com/containernetworking/cni v1.0.1 10 | github.com/coreos/go-iptables v0.6.0 11 | github.com/coreos/go-systemd/v22 v22.3.2 12 | github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c 13 | github.com/d2g/dhcp4client v1.0.0 14 | github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5 15 | github.com/godbus/dbus/v5 v5.0.4 16 | github.com/mattn/go-shellwords v1.0.12 17 | github.com/networkplumbing/go-nft v0.2.0 18 | github.com/onsi/ginkgo v1.16.4 19 | github.com/onsi/gomega v1.15.0 20 | github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1 21 | github.com/vishvananda/netlink v1.2.0-beta 22 | golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e 23 | ) 24 | 25 | require ( 26 | github.com/Microsoft/go-winio v0.4.17 // indirect 27 | github.com/containerd/cgroups v1.0.1 // indirect 28 | github.com/fsnotify/fsnotify v1.4.9 // indirect 29 | github.com/gogo/protobuf v1.3.2 // indirect 30 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect 31 | github.com/nxadm/tail v1.4.8 // indirect 32 | github.com/pkg/errors v0.9.1 // indirect 33 | github.com/sirupsen/logrus v1.8.1 // indirect 34 | github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect 35 | go.opencensus.io v0.22.3 // indirect 36 | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect 37 | golang.org/x/text v0.3.6 // indirect 38 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect 39 | gopkg.in/yaml.v2 v2.4.0 // indirect 40 | ) 41 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/ip/cidr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ip 16 | 17 | import ( 18 | "math/big" 19 | "net" 20 | ) 21 | 22 | // NextIP returns IP incremented by 1 23 | func NextIP(ip net.IP) net.IP { 24 | i := ipToInt(ip) 25 | return intToIP(i.Add(i, big.NewInt(1))) 26 | } 27 | 28 | // PrevIP returns IP decremented by 1 29 | func PrevIP(ip net.IP) net.IP { 30 | i := ipToInt(ip) 31 | return intToIP(i.Sub(i, big.NewInt(1))) 32 | } 33 | 34 | // Cmp compares two IPs, returning the usual ordering: 35 | // a < b : -1 36 | // a == b : 0 37 | // a > b : 1 38 | func Cmp(a, b net.IP) int { 39 | aa := ipToInt(a) 40 | bb := ipToInt(b) 41 | return aa.Cmp(bb) 42 | } 43 | 44 | func ipToInt(ip net.IP) *big.Int { 45 | if v := ip.To4(); v != nil { 46 | return big.NewInt(0).SetBytes(v) 47 | } 48 | return big.NewInt(0).SetBytes(ip.To16()) 49 | } 50 | 51 | func intToIP(i *big.Int) net.IP { 52 | return net.IP(i.Bytes()) 53 | } 54 | 55 | // Network masks off the host portion of the IP 56 | func Network(ipn *net.IPNet) *net.IPNet { 57 | return &net.IPNet{ 58 | IP: ipn.IP.Mask(ipn.Mask), 59 | Mask: ipn.Mask, 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/ip/ipforward_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ip 16 | 17 | import ( 18 | "bytes" 19 | "io/ioutil" 20 | 21 | current "github.com/containernetworking/cni/pkg/types/100" 22 | ) 23 | 24 | func EnableIP4Forward() error { 25 | return echo1("/proc/sys/net/ipv4/ip_forward") 26 | } 27 | 28 | func EnableIP6Forward() error { 29 | return echo1("/proc/sys/net/ipv6/conf/all/forwarding") 30 | } 31 | 32 | // EnableForward will enable forwarding for all configured 33 | // address families 34 | func EnableForward(ips []*current.IPConfig) error { 35 | v4 := false 36 | v6 := false 37 | 38 | for _, ip := range ips { 39 | isV4 := ip.Address.IP.To4() != nil 40 | if isV4 && !v4 { 41 | if err := EnableIP4Forward(); err != nil { 42 | return err 43 | } 44 | v4 = true 45 | } else if !isV4 && !v6 { 46 | if err := EnableIP6Forward(); err != nil { 47 | return err 48 | } 49 | v6 = true 50 | } 51 | } 52 | return nil 53 | } 54 | 55 | func echo1(f string) error { 56 | if content, err := ioutil.ReadFile(f); err == nil { 57 | if bytes.Equal(bytes.TrimSpace(content), []byte("1")) { 58 | return nil 59 | } 60 | } 61 | return ioutil.WriteFile(f, []byte("1"), 0644) 62 | } 63 | -------------------------------------------------------------------------------- /containernetworking/plugins/scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -xe 3 | 4 | SRC_DIR="${SRC_DIR:-$PWD}" 5 | DOCKER="${DOCKER:-docker}" 6 | 7 | TAG=$(git describe --tags --dirty) 8 | RELEASE_DIR=release-${TAG} 9 | 10 | BUILDFLAGS="-ldflags '-extldflags -static -X github.com/containernetworking/plugins/pkg/utils/buildversion.BuildVersion=${TAG}'" 11 | 12 | OUTPUT_DIR=bin 13 | 14 | # Always clean first 15 | rm -Rf ${SRC_DIR}/${RELEASE_DIR} 16 | mkdir -p ${SRC_DIR}/${RELEASE_DIR} 17 | mkdir -p ${OUTPUT_DIR} 18 | 19 | $DOCKER run -ti -v ${SRC_DIR}:/go/src/github.com/containernetworking/plugins:z --rm golang:1.18-alpine \ 20 | /bin/sh -xe -c "\ 21 | apk --no-cache add bash tar; 22 | cd /go/src/github.com/containernetworking/plugins; umask 0022; 23 | 24 | for arch in amd64 arm arm64 ppc64le s390x mips64le riscv64; do \ 25 | rm -f ${OUTPUT_DIR}/*; \ 26 | CGO_ENABLED=0 GOARCH=\$arch ./build_linux.sh ${BUILDFLAGS}; \ 27 | for format in tgz; do \ 28 | FILENAME=cni-plugins-linux-\$arch-${TAG}.\$format; \ 29 | FILEPATH=${RELEASE_DIR}/\$FILENAME; \ 30 | tar -C ${OUTPUT_DIR} --owner=0 --group=0 -caf \$FILEPATH .; \ 31 | done; \ 32 | done; 33 | 34 | rm -rf ${OUTPUT_DIR}/*; \ 35 | CGO_ENABLED=0 GOARCH=amd64 ./build_windows.sh ${BUILDFLAGS}; \ 36 | for format in tgz; do \ 37 | FILENAME=cni-plugins-windows-amd64-${TAG}.\$format; \ 38 | FILEPATH=${RELEASE_DIR}/\$FILENAME; \ 39 | tar -C ${OUTPUT_DIR} --owner=0 --group=0 -caf \$FILEPATH .; \ 40 | done; 41 | 42 | 43 | cd ${RELEASE_DIR}; 44 | for f in *.tgz; do sha1sum \$f > \$f.sha1; done; 45 | for f in *.tgz; do sha256sum \$f > \$f.sha256; done; 46 | for f in *.tgz; do sha512sum \$f > \$f.sha512; done; 47 | cd .. 48 | chown -R ${UID} ${OUTPUT_DIR} ${RELEASE_DIR}" 49 | -------------------------------------------------------------------------------- /gitops-demo/echo-server.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: echo-server 5 | labels: 6 | app: echo-server 7 | spec: 8 | selector: 9 | matchLabels: 10 | app: echo-server 11 | replicas: 10 12 | template: 13 | metadata: 14 | labels: 15 | app: echo-server 16 | spec: 17 | containers: 18 | - name: echo-server 19 | env: 20 | - name: PORT 21 | value: '8080' 22 | - name: POD_NAME 23 | valueFrom: 24 | fieldRef: 25 | fieldPath: metadata.name 26 | - name: POD_NAMESPACE 27 | valueFrom: 28 | fieldRef: 29 | fieldPath: metadata.namespace 30 | - name: POD_IP 31 | valueFrom: 32 | fieldRef: 33 | fieldPath: status.podIP 34 | - name: NODE_NAME 35 | value: v1 36 | image: cilium/echoserver:latest 37 | imagePullPolicy: Always 38 | resources: 39 | requests: 40 | cpu: 50m 41 | memory: 50Mi 42 | ports: 43 | - containerPort: 8080 44 | --- 45 | apiVersion: v1 46 | kind: Service 47 | metadata: 48 | name: echo-server 49 | labels: 50 | app: echo-server 51 | spec: 52 | ports: 53 | - port: 8080 54 | protocol: TCP 55 | targetPort: 8080 56 | selector: 57 | app: echo-server 58 | --- 59 | apiVersion: networking.k8s.io/v1 60 | kind: Ingress 61 | metadata: 62 | name: ingress-demo 63 | annotations: 64 | nginx.ingress.kubernetes.io/rewrite-target: / 65 | spec: 66 | rules: 67 | - http: 68 | paths: 69 | - path: /apis/echo 70 | pathType: Prefix 71 | backend: 72 | service: 73 | name: echo-server 74 | port: 75 | number: 8080 76 | -------------------------------------------------------------------------------- /argo-demo/echo-server.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps.kruise.io/v1alpha1 2 | kind: CloneSet 3 | metadata: 4 | name: echo-server 5 | labels: 6 | app: echo-server 7 | spec: 8 | selector: 9 | matchLabels: 10 | app: echo-server 11 | replicas: 10 12 | template: 13 | metadata: 14 | labels: 15 | app: echo-server 16 | spec: 17 | containers: 18 | - name: echo-server 19 | env: 20 | - name: PORT 21 | value: '8080' 22 | - name: POD_NAME 23 | valueFrom: 24 | fieldRef: 25 | fieldPath: metadata.name 26 | - name: POD_NAMESPACE 27 | valueFrom: 28 | fieldRef: 29 | fieldPath: metadata.namespace 30 | - name: POD_IP 31 | valueFrom: 32 | fieldRef: 33 | fieldPath: status.podIP 34 | - name: NODE_NAME 35 | value: failed 36 | image: cilium/echoserver:failed 37 | imagePullPolicy: Always 38 | resources: 39 | requests: 40 | cpu: 50m 41 | memory: 50Mi 42 | ports: 43 | - containerPort: 8080 44 | --- 45 | apiVersion: v1 46 | kind: Service 47 | metadata: 48 | name: echo-server 49 | labels: 50 | app: echo-server 51 | spec: 52 | ports: 53 | - port: 8080 54 | protocol: TCP 55 | targetPort: 8080 56 | selector: 57 | app: echo-server 58 | --- 59 | apiVersion: networking.k8s.io/v1 60 | kind: Ingress 61 | metadata: 62 | name: ingress-demo 63 | annotations: 64 | nginx.ingress.kubernetes.io/rewrite-target: / 65 | spec: 66 | rules: 67 | - http: 68 | paths: 69 | - path: /apis/echo 70 | pathType: Prefix 71 | backend: 72 | service: 73 | name: echo-server 74 | port: 75 | number: 8080 76 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/dns.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "bufio" 19 | "os" 20 | "strings" 21 | 22 | "github.com/containernetworking/cni/pkg/types" 23 | ) 24 | 25 | // parseResolvConf parses an existing resolv.conf in to a DNS struct 26 | func parseResolvConf(filename string) (*types.DNS, error) { 27 | fp, err := os.Open(filename) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | dns := types.DNS{} 33 | scanner := bufio.NewScanner(fp) 34 | for scanner.Scan() { 35 | line := scanner.Text() 36 | line = strings.TrimSpace(line) 37 | 38 | // Skip comments, empty lines 39 | if len(line) == 0 || line[0] == '#' || line[0] == ';' { 40 | continue 41 | } 42 | 43 | fields := strings.Fields(line) 44 | if len(fields) < 2 { 45 | continue 46 | } 47 | switch fields[0] { 48 | case "nameserver": 49 | dns.Nameservers = append(dns.Nameservers, fields[1]) 50 | case "domain": 51 | dns.Domain = fields[1] 52 | case "search": 53 | dns.Search = append(dns.Search, fields[1:]...) 54 | case "options": 55 | dns.Options = append(dns.Options, fields[1:]...) 56 | } 57 | } 58 | 59 | if err := scanner.Err(); err != nil { 60 | return nil, err 61 | } 62 | 63 | return &dns, nil 64 | } 65 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/backend/disk/lock_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package disk 16 | 17 | import ( 18 | "io/ioutil" 19 | "os" 20 | "path/filepath" 21 | 22 | . "github.com/onsi/ginkgo" 23 | . "github.com/onsi/gomega" 24 | ) 25 | 26 | var _ = Describe("Lock Operations", func() { 27 | It("locks a file path", func() { 28 | dir, err := ioutil.TempDir("", "") 29 | Expect(err).ToNot(HaveOccurred()) 30 | defer os.RemoveAll(dir) 31 | 32 | // create a dummy file to lock 33 | path := filepath.Join(dir, "x") 34 | f, err := os.OpenFile(path, os.O_RDONLY|os.O_CREATE, 0666) 35 | Expect(err).ToNot(HaveOccurred()) 36 | err = f.Close() 37 | Expect(err).ToNot(HaveOccurred()) 38 | 39 | // now use it to lock 40 | m, err := NewFileLock(path) 41 | Expect(err).ToNot(HaveOccurred()) 42 | 43 | err = m.Lock() 44 | Expect(err).ToNot(HaveOccurred()) 45 | err = m.Unlock() 46 | Expect(err).ToNot(HaveOccurred()) 47 | }) 48 | 49 | It("locks a folder path", func() { 50 | dir, err := ioutil.TempDir("", "") 51 | Expect(err).ToNot(HaveOccurred()) 52 | defer os.RemoveAll(dir) 53 | 54 | // use the folder to lock 55 | m, err := NewFileLock(dir) 56 | Expect(err).ToNot(HaveOccurred()) 57 | 58 | err = m.Lock() 59 | Expect(err).ToNot(HaveOccurred()) 60 | err = m.Unlock() 61 | Expect(err).ToNot(HaveOccurred()) 62 | }) 63 | }) 64 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/testutils/testing.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package testutils 16 | 17 | import ( 18 | "github.com/containernetworking/cni/pkg/version" 19 | ) 20 | 21 | // AllSpecVersions contains all CNI spec version numbers 22 | var AllSpecVersions = [...]string{"0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0", "1.0.0"} 23 | 24 | // SpecVersionHasIPVersion returns true if the given CNI specification version 25 | // includes the "version" field in the IP address elements 26 | func SpecVersionHasIPVersion(ver string) bool { 27 | for _, i := range []string{"0.3.0", "0.3.1", "0.4.0"} { 28 | if ver == i { 29 | return true 30 | } 31 | } 32 | return false 33 | } 34 | 35 | // SpecVersionHasCHECK returns true if the given CNI specification version 36 | // supports the CHECK command 37 | func SpecVersionHasCHECK(ver string) bool { 38 | ok, _ := version.GreaterThanOrEqualTo(ver, "0.4.0") 39 | return ok 40 | } 41 | 42 | // SpecVersionHasChaining returns true if the given CNI specification version 43 | // supports plugin chaining 44 | func SpecVersionHasChaining(ver string) bool { 45 | ok, _ := version.GreaterThanOrEqualTo(ver, "0.3.0") 46 | return ok 47 | } 48 | 49 | // SpecVersionHasMultipleIPs returns true if the given CNI specification version 50 | // supports more than one IP address of each family 51 | func SpecVersionHasMultipleIPs(ver string) bool { 52 | ok, _ := version.GreaterThanOrEqualTo(ver, "0.3.0") 53 | return ok 54 | } 55 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/testutils/echo/server/main.go: -------------------------------------------------------------------------------- 1 | // Echosvr is a simple TCP echo server 2 | // 3 | // It prints its listen address on stdout 4 | // 127.0.0.1:xxxxx 5 | // A test should wait for this line, parse it 6 | // and may then attempt to connect. 7 | package main 8 | 9 | import ( 10 | "bufio" 11 | "fmt" 12 | "io" 13 | "log" 14 | "net" 15 | "os" 16 | "strings" 17 | "time" 18 | ) 19 | 20 | func main() { 21 | // Start TCP server 22 | listener, err := net.Listen("tcp", ":") 23 | if err != nil { 24 | panic(err) 25 | } 26 | defer listener.Close() 27 | // use the same port for UDP 28 | _, port, err := net.SplitHostPort(listener.Addr().String()) 29 | if err != nil { 30 | panic(err) 31 | } 32 | fmt.Printf("127.0.0.1:%s\n", port) 33 | go func() { 34 | for { 35 | conn, err := listener.Accept() 36 | if err != nil { 37 | panic(err) 38 | } 39 | go handleConnection(conn) 40 | } 41 | }() 42 | 43 | // Start UDP server 44 | addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%s", port)) 45 | if err != nil { 46 | log.Fatalf("Error from net.ResolveUDPAddr(): %s", err) 47 | } 48 | sock, err := net.ListenUDP("udp", addr) 49 | if err != nil { 50 | log.Fatalf("Error from ListenUDP(): %s", err) 51 | } 52 | defer sock.Close() 53 | 54 | buffer := make([]byte, 1024) 55 | for { 56 | n, addr, err := sock.ReadFrom(buffer) 57 | if err != nil { 58 | log.Fatalf("Error from ReadFrom(): %s", err) 59 | } 60 | sock.SetWriteDeadline(time.Now().Add(1 * time.Minute)) 61 | n, err = sock.WriteTo(buffer[0:n], addr) 62 | if err != nil { 63 | return 64 | } 65 | } 66 | } 67 | 68 | func handleConnection(conn net.Conn) { 69 | conn.SetReadDeadline(time.Now().Add(1 * time.Minute)) 70 | content, err := bufio.NewReader(conn).ReadString('\n') 71 | if err != nil && err != io.EOF { 72 | fmt.Fprint(os.Stderr, err.Error()) 73 | return 74 | } 75 | 76 | conn.SetWriteDeadline(time.Now().Add(1 * time.Minute)) 77 | if _, err = conn.Write([]byte(strings.TrimSuffix(content, "\n"))); err != nil { 78 | fmt.Fprint(os.Stderr, err.Error()) 79 | return 80 | } 81 | 82 | if err = conn.Close(); err != nil { 83 | fmt.Fprint(os.Stderr, err.Error()) 84 | return 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/ip/addr_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ip 16 | 17 | import ( 18 | "fmt" 19 | "syscall" 20 | "time" 21 | 22 | "github.com/vishvananda/netlink" 23 | ) 24 | 25 | const SETTLE_INTERVAL = 50 * time.Millisecond 26 | 27 | // SettleAddresses waits for all addresses on a link to leave tentative state. 28 | // This is particularly useful for ipv6, where all addresses need to do DAD. 29 | // There is no easy way to wait for this as an event, so just loop until the 30 | // addresses are no longer tentative. 31 | // If any addresses are still tentative after timeout seconds, then error. 32 | func SettleAddresses(ifName string, timeout int) error { 33 | link, err := netlink.LinkByName(ifName) 34 | if err != nil { 35 | return fmt.Errorf("failed to retrieve link: %v", err) 36 | } 37 | 38 | deadline := time.Now().Add(time.Duration(timeout) * time.Second) 39 | for { 40 | addrs, err := netlink.AddrList(link, netlink.FAMILY_ALL) 41 | if err != nil { 42 | return fmt.Errorf("could not list addresses: %v", err) 43 | } 44 | 45 | if len(addrs) == 0 { 46 | return nil 47 | } 48 | 49 | ok := true 50 | for _, addr := range addrs { 51 | if addr.Flags&(syscall.IFA_F_TENTATIVE|syscall.IFA_F_DADFAILED) > 0 { 52 | ok = false 53 | break // Break out of the `range addrs`, not the `for` 54 | } 55 | } 56 | 57 | if ok { 58 | return nil 59 | } 60 | if time.Now().After(deadline) { 61 | return fmt.Errorf("link %s still has tentative addresses after %d seconds", 62 | ifName, 63 | timeout) 64 | } 65 | 66 | time.Sleep(SETTLE_INTERVAL) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range_set_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package allocator 16 | 17 | import ( 18 | "net" 19 | 20 | . "github.com/onsi/ginkgo" 21 | . "github.com/onsi/gomega" 22 | ) 23 | 24 | var _ = Describe("range sets", func() { 25 | It("should detect set membership correctly", func() { 26 | p := RangeSet{ 27 | Range{Subnet: mustSubnet("192.168.0.0/24")}, 28 | Range{Subnet: mustSubnet("172.16.1.0/24")}, 29 | } 30 | 31 | err := p.Canonicalize() 32 | Expect(err).NotTo(HaveOccurred()) 33 | 34 | Expect(p.Contains(net.IP{192, 168, 0, 55})).To(BeTrue()) 35 | 36 | r, err := p.RangeFor(net.IP{192, 168, 0, 55}) 37 | Expect(err).NotTo(HaveOccurred()) 38 | Expect(r).To(Equal(&p[0])) 39 | 40 | r, err = p.RangeFor(net.IP{192, 168, 99, 99}) 41 | Expect(r).To(BeNil()) 42 | Expect(err).To(MatchError("192.168.99.99 not in range set 192.168.0.1-192.168.0.254,172.16.1.1-172.16.1.254")) 43 | 44 | }) 45 | 46 | It("should discover overlaps within a set", func() { 47 | p := RangeSet{ 48 | {Subnet: mustSubnet("192.168.0.0/20")}, 49 | {Subnet: mustSubnet("192.168.2.0/24")}, 50 | } 51 | 52 | err := p.Canonicalize() 53 | Expect(err).To(MatchError("subnets 192.168.0.1-192.168.15.254 and 192.168.2.1-192.168.2.254 overlap")) 54 | }) 55 | 56 | It("should discover overlaps outside a set", func() { 57 | p1 := RangeSet{ 58 | {Subnet: mustSubnet("192.168.0.0/20")}, 59 | } 60 | p2 := RangeSet{ 61 | {Subnet: mustSubnet("192.168.2.0/24")}, 62 | } 63 | 64 | p1.Canonicalize() 65 | p2.Canonicalize() 66 | 67 | Expect(p1.Overlaps(&p2)).To(BeTrue()) 68 | Expect(p2.Overlaps(&p1)).To(BeTrue()) 69 | }) 70 | }) 71 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/testutils/dns.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package testutils 16 | 17 | import ( 18 | "fmt" 19 | "io/ioutil" 20 | "os" 21 | "strings" 22 | 23 | "github.com/containernetworking/cni/pkg/types" 24 | ) 25 | 26 | // TmpResolvConf will create a temporary file and write the provided DNS settings to 27 | // it in the resolv.conf format. It returns the path of the created temporary file or 28 | // an error if any occurs while creating/writing the file. It is the caller's 29 | // responsibility to remove the file. 30 | func TmpResolvConf(dnsConf types.DNS) (string, error) { 31 | f, err := ioutil.TempFile("", "cni_test_resolv.conf") 32 | if err != nil { 33 | return "", fmt.Errorf("failed to get temp file for CNI test resolv.conf: %v", err) 34 | } 35 | defer f.Close() 36 | 37 | path := f.Name() 38 | defer func() { 39 | if err != nil { 40 | os.RemoveAll(path) 41 | } 42 | }() 43 | 44 | // see "man 5 resolv.conf" for the format of resolv.conf 45 | var resolvConfLines []string 46 | for _, nameserver := range dnsConf.Nameservers { 47 | resolvConfLines = append(resolvConfLines, fmt.Sprintf("nameserver %s", nameserver)) 48 | } 49 | resolvConfLines = append(resolvConfLines, fmt.Sprintf("domain %s", dnsConf.Domain)) 50 | resolvConfLines = append(resolvConfLines, fmt.Sprintf("search %s", strings.Join(dnsConf.Search, " "))) 51 | resolvConfLines = append(resolvConfLines, fmt.Sprintf("options %s", strings.Join(dnsConf.Options, " "))) 52 | 53 | resolvConf := strings.Join(resolvConfLines, "\n") 54 | _, err = f.Write([]byte(resolvConf)) 55 | if err != nil { 56 | return "", fmt.Errorf("failed to write temp resolv.conf for CNI test: %v", err) 57 | } 58 | 59 | return path, err 60 | } 61 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/utils/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "crypto/sha512" 19 | "fmt" 20 | ) 21 | 22 | const ( 23 | maxChainLength = 28 24 | chainPrefix = "CNI-" 25 | ) 26 | 27 | // FormatChainName generates a chain name to be used 28 | // with iptables. Ensures that the generated chain 29 | // name is exactly maxChainLength chars in length. 30 | func FormatChainName(name string, id string) string { 31 | return MustFormatChainNameWithPrefix(name, id, "") 32 | } 33 | 34 | // MustFormatChainNameWithPrefix generates a chain name similar 35 | // to FormatChainName, but adds a custom prefix between 36 | // chainPrefix and unique identifier. Ensures that the 37 | // generated chain name is exactly maxChainLength chars in length. 38 | // Panics if the given prefix is too long. 39 | func MustFormatChainNameWithPrefix(name string, id string, prefix string) string { 40 | return MustFormatHashWithPrefix(maxChainLength, chainPrefix+prefix, name+id) 41 | } 42 | 43 | // FormatComment returns a comment used for easier 44 | // rule identification within iptables. 45 | func FormatComment(name string, id string) string { 46 | return fmt.Sprintf("name: %q id: %q", name, id) 47 | } 48 | 49 | const MaxHashLen = sha512.Size * 2 50 | 51 | // MustFormatHashWithPrefix returns a string of given length that begins with the 52 | // given prefix. It is filled with entropy based on the given string toHash. 53 | func MustFormatHashWithPrefix(length int, prefix string, toHash string) string { 54 | if len(prefix) >= length || length > MaxHashLen { 55 | panic("invalid length") 56 | } 57 | 58 | output := sha512.Sum512([]byte(toHash)) 59 | return fmt.Sprintf("%s%x", prefix, output)[:length] 60 | } 61 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Image URL to use all building/pushing image targets 3 | IMG ?= controller:latest 4 | # Produce CRDs that work back to Kubernetes 1.11 (no version conversion) 5 | CRD_OPTIONS ?= "crd:trivialVersions=true" 6 | 7 | # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) 8 | ifeq (,$(shell go env GOBIN)) 9 | GOBIN=$(shell go env GOPATH)/bin 10 | else 11 | GOBIN=$(shell go env GOBIN) 12 | endif 13 | 14 | all: manager 15 | 16 | # Run tests 17 | test: generate fmt vet manifests 18 | go test ./... -coverprofile cover.out 19 | 20 | # Build manager binary 21 | manager: generate fmt vet 22 | go build -o bin/manager main.go 23 | 24 | # Run against the configured Kubernetes cluster in ~/.kube/config 25 | run: generate fmt vet manifests 26 | go run ./main.go 27 | 28 | # Install CRDs into a cluster 29 | install: manifests 30 | kustomize build config/crd | kubectl apply -f - 31 | 32 | # Uninstall CRDs from a cluster 33 | uninstall: manifests 34 | kustomize build config/crd | kubectl delete -f - 35 | 36 | # Deploy controller in the configured Kubernetes cluster in ~/.kube/config 37 | deploy: manifests 38 | cd config/manager && kustomize edit set image controller=${IMG} 39 | kustomize build config/default | kubectl apply -f - 40 | 41 | # Generate manifests e.g. CRD, RBAC etc. 42 | manifests: controller-gen 43 | $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases 44 | 45 | # Run go fmt against code 46 | fmt: 47 | go fmt ./... 48 | 49 | # Run go vet against code 50 | vet: 51 | go vet ./... 52 | 53 | # Generate code 54 | generate: controller-gen 55 | $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." 56 | 57 | # Build the docker image 58 | docker-build: test 59 | docker build . -t ${IMG} 60 | 61 | # Push the docker image 62 | docker-push: 63 | docker push ${IMG} 64 | 65 | # find or download controller-gen 66 | # download controller-gen if necessary 67 | controller-gen: 68 | ifeq (, $(shell which controller-gen)) 69 | @{ \ 70 | set -e ;\ 71 | CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\ 72 | cd $$CONTROLLER_GEN_TMP_DIR ;\ 73 | go mod init tmp ;\ 74 | go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.2.5 ;\ 75 | rm -rf $$CONTROLLER_GEN_TMP_DIR ;\ 76 | } 77 | CONTROLLER_GEN=$(GOBIN)/controller-gen 78 | else 79 | CONTROLLER_GEN=$(shell which controller-gen) 80 | endif 81 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/testutils/echo/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io" 7 | "net" 8 | ) 9 | 10 | func main() { 11 | target := flag.String("target", "", "the server address") 12 | payload := flag.String("message", "", "the message to send to the server") 13 | protocol := flag.String("protocol", "tcp", "the protocol to use with the server [udp,tcp], default tcp") 14 | flag.Parse() 15 | 16 | if *target == "" || *payload == "" { 17 | flag.Usage() 18 | panic("invalid arguments") 19 | } 20 | 21 | switch *protocol { 22 | case "tcp": 23 | connectTCP(*target, *payload) 24 | case "udp": 25 | connectUDP(*target, *payload) 26 | default: 27 | panic("invalid protocol") 28 | } 29 | } 30 | 31 | func connectTCP(target, payload string) { 32 | conn, err := net.Dial("tcp", target) 33 | if err != nil { 34 | panic(fmt.Sprintf("Failed to open connection to [%s] %v", target, err)) 35 | } 36 | defer conn.Close() 37 | 38 | _, err = conn.Write([]byte(payload)) 39 | if err != nil { 40 | panic("Failed to send payload") 41 | } 42 | _, err = conn.Write([]byte("\n")) 43 | if err != nil { 44 | panic("Failed to send payload") 45 | } 46 | buf := make([]byte, 1024) 47 | for { 48 | n, err := conn.Read(buf) 49 | fmt.Print(string(buf[:n])) 50 | if err == io.EOF { 51 | break 52 | } 53 | if err != nil { 54 | panic("Failed to read from socket") 55 | } 56 | } 57 | } 58 | 59 | // UDP uses a constant source port to trigger conntrack problems 60 | func connectUDP(target, payload string) { 61 | LocalAddr, err := net.ResolveUDPAddr("udp", ":54321") 62 | if err != nil { 63 | panic(fmt.Sprintf("Failed to resolve UDP local address on port 54321 %v", err)) 64 | } 65 | RemoteAddr, err := net.ResolveUDPAddr("udp", target) 66 | if err != nil { 67 | panic(fmt.Sprintf("Failed to resolve UDP remote address [%s] %v", target, err)) 68 | } 69 | conn, err := net.DialUDP("udp", LocalAddr, RemoteAddr) 70 | if err != nil { 71 | panic(fmt.Sprintf("Failed to open connection to [%s] %v", target, err)) 72 | } 73 | defer conn.Close() 74 | 75 | _, err = conn.Write([]byte(payload)) 76 | if err != nil { 77 | panic("Failed to send payload") 78 | } 79 | _, err = conn.Write([]byte("\n")) 80 | if err != nil { 81 | panic("Failed to send payload") 82 | } 83 | 84 | buf := make([]byte, 1024) 85 | n, err := conn.Read(buf) 86 | if err != nil { 87 | panic("Failed to read from socket") 88 | } 89 | fmt.Print(string(buf[:n])) 90 | } 91 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/errors/errors_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package errors 16 | 17 | import ( 18 | "errors" 19 | "reflect" 20 | "testing" 21 | ) 22 | 23 | func TestAnnotate(t *testing.T) { 24 | tests := []struct { 25 | name string 26 | existingErr error 27 | contextMessage string 28 | expectedErr error 29 | }{ 30 | { 31 | "nil error", 32 | nil, 33 | "context", 34 | nil, 35 | }, 36 | { 37 | "normal case", 38 | errors.New("existing error"), 39 | "context", 40 | errors.New("context: existing error"), 41 | }, 42 | } 43 | 44 | for _, test := range tests { 45 | t.Run(test.name, func(t *testing.T) { 46 | if !reflect.DeepEqual(Annotatef(test.existingErr, test.contextMessage), test.expectedErr) { 47 | t.Errorf("test case %s fails", test.name) 48 | return 49 | } 50 | }) 51 | } 52 | } 53 | 54 | func TestAnnotatef(t *testing.T) { 55 | tests := []struct { 56 | name string 57 | existingErr error 58 | contextMessage string 59 | contextArgs []interface{} 60 | expectedErr error 61 | }{ 62 | { 63 | "nil error", 64 | nil, 65 | "context", 66 | nil, 67 | nil, 68 | }, 69 | { 70 | "normal case", 71 | errors.New("existing error"), 72 | "context", 73 | nil, 74 | errors.New("context: existing error"), 75 | }, 76 | { 77 | "normal case with args", 78 | errors.New("existing error"), 79 | "context %s %d", 80 | []interface{}{ 81 | "arg", 82 | 100, 83 | }, 84 | errors.New("context arg 100: existing error"), 85 | }, 86 | } 87 | 88 | for _, test := range tests { 89 | t.Run(test.name, func(t *testing.T) { 90 | if !reflect.DeepEqual(Annotatef(test.existingErr, test.contextMessage, test.contextArgs...), test.expectedErr) { 91 | t.Errorf("test case %s fails", test.name) 92 | return 93 | } 94 | }) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/dns_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "io/ioutil" 19 | "os" 20 | 21 | "github.com/containernetworking/cni/pkg/types" 22 | . "github.com/onsi/ginkgo" 23 | . "github.com/onsi/gomega" 24 | ) 25 | 26 | var _ = Describe("parsing resolv.conf", func() { 27 | It("parses a simple resolv.conf file", func() { 28 | contents := ` 29 | nameserver 192.0.2.0 30 | nameserver 192.0.2.1 31 | ` 32 | dns, err := parse(contents) 33 | Expect(err).NotTo(HaveOccurred()) 34 | Expect(*dns).Should(Equal(types.DNS{Nameservers: []string{"192.0.2.0", "192.0.2.1"}})) 35 | }) 36 | It("ignores comments", func() { 37 | dns, err := parse(` 38 | nameserver 192.0.2.0 39 | ;nameserver 192.0.2.1 40 | `) 41 | Expect(err).NotTo(HaveOccurred()) 42 | Expect(*dns).Should(Equal(types.DNS{Nameservers: []string{"192.0.2.0"}})) 43 | }) 44 | It("parses all fields", func() { 45 | dns, err := parse(` 46 | nameserver 192.0.2.0 47 | nameserver 192.0.2.2 48 | domain example.com 49 | ;nameserver comment 50 | #nameserver comment 51 | search example.net example.org 52 | search example.gov 53 | options one two three 54 | options four 55 | `) 56 | Expect(err).NotTo(HaveOccurred()) 57 | Expect(*dns).Should(Equal(types.DNS{ 58 | Nameservers: []string{"192.0.2.0", "192.0.2.2"}, 59 | Domain: "example.com", 60 | Search: []string{"example.net", "example.org", "example.gov"}, 61 | Options: []string{"one", "two", "three", "four"}, 62 | })) 63 | }) 64 | }) 65 | 66 | func parse(contents string) (*types.DNS, error) { 67 | f, err := ioutil.TempFile("", "host_local_resolv") 68 | if err != nil { 69 | return nil, err 70 | } 71 | defer f.Close() 72 | defer os.Remove(f.Name()) 73 | 74 | if _, err := f.WriteString(contents); err != nil { 75 | return nil, err 76 | } 77 | 78 | return parseResolvConf(f.Name()) 79 | } 80 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/utils/sysctl/sysctl_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sysctl 16 | 17 | import ( 18 | "fmt" 19 | "io/ioutil" 20 | "path/filepath" 21 | "strings" 22 | ) 23 | 24 | // Sysctl provides a method to set/get values from /proc/sys - in linux systems 25 | // new interface to set/get values of variables formerly handled by sysctl syscall 26 | // If optional `params` have only one string value - this function will 27 | // set this value into corresponding sysctl variable 28 | func Sysctl(name string, params ...string) (string, error) { 29 | if len(params) > 1 { 30 | return "", fmt.Errorf("unexcepted additional parameters") 31 | } else if len(params) == 1 { 32 | return setSysctl(name, params[0]) 33 | } 34 | return getSysctl(name) 35 | } 36 | 37 | func getSysctl(name string) (string, error) { 38 | fullName := filepath.Join("/proc/sys", toNormalName(name)) 39 | data, err := ioutil.ReadFile(fullName) 40 | if err != nil { 41 | return "", err 42 | } 43 | 44 | return string(data[:len(data)-1]), nil 45 | } 46 | 47 | func setSysctl(name, value string) (string, error) { 48 | fullName := filepath.Join("/proc/sys", toNormalName(name)) 49 | if err := ioutil.WriteFile(fullName, []byte(value), 0644); err != nil { 50 | return "", err 51 | } 52 | 53 | return getSysctl(name) 54 | } 55 | 56 | // Normalize names by using slash as separator 57 | // Sysctl names can use dots or slashes as separator: 58 | // - if dots are used, dots and slashes are interchanged. 59 | // - if slashes are used, slashes and dots are left intact. 60 | // Separator in use is determined by first occurrence. 61 | func toNormalName(name string) string { 62 | interchange := false 63 | for _, c := range name { 64 | if c == '.' { 65 | interchange = true 66 | break 67 | } 68 | if c == '/' { 69 | break 70 | } 71 | } 72 | 73 | if interchange { 74 | r := strings.NewReplacer(".", "/", "/", ".") 75 | return r.Replace(name) 76 | } 77 | return name 78 | } 79 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/backend/testing/fake_store.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package testing 16 | 17 | import ( 18 | "net" 19 | "os" 20 | 21 | "github.com/containernetworking/plugins/plugins/ipam/host-local/backend" 22 | ) 23 | 24 | type FakeStore struct { 25 | ipMap map[string]string 26 | lastReservedIP map[string]net.IP 27 | } 28 | 29 | // FakeStore implements the Store interface 30 | var _ backend.Store = &FakeStore{} 31 | 32 | func NewFakeStore(ipmap map[string]string, lastIPs map[string]net.IP) *FakeStore { 33 | return &FakeStore{ipmap, lastIPs} 34 | } 35 | 36 | func (s *FakeStore) Lock() error { 37 | return nil 38 | } 39 | 40 | func (s *FakeStore) Unlock() error { 41 | return nil 42 | } 43 | 44 | func (s *FakeStore) Close() error { 45 | return nil 46 | } 47 | 48 | func (s *FakeStore) Reserve(id string, ifname string, ip net.IP, rangeID string) (bool, error) { 49 | key := ip.String() 50 | if _, ok := s.ipMap[key]; !ok { 51 | s.ipMap[key] = id 52 | s.lastReservedIP[rangeID] = ip 53 | return true, nil 54 | } 55 | return false, nil 56 | } 57 | 58 | func (s *FakeStore) LastReservedIP(rangeID string) (net.IP, error) { 59 | ip, ok := s.lastReservedIP[rangeID] 60 | if !ok { 61 | return nil, os.ErrNotExist 62 | } 63 | return ip, nil 64 | } 65 | 66 | func (s *FakeStore) Release(ip net.IP) error { 67 | delete(s.ipMap, ip.String()) 68 | return nil 69 | } 70 | 71 | func (s *FakeStore) ReleaseByID(id string, ifname string) error { 72 | toDelete := []string{} 73 | for k, v := range s.ipMap { 74 | if v == id { 75 | toDelete = append(toDelete, k) 76 | } 77 | } 78 | for _, ip := range toDelete { 79 | delete(s.ipMap, ip) 80 | } 81 | return nil 82 | } 83 | 84 | func (s *FakeStore) GetByID(id string, ifname string) []net.IP { 85 | var ips []net.IP 86 | for k, v := range s.ipMap { 87 | if v == id { 88 | ips = append(ips, net.ParseIP(k)) 89 | } 90 | } 91 | return ips 92 | } 93 | 94 | func (s *FakeStore) SetIPMap(m map[string]string) { 95 | s.ipMap = m 96 | } 97 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/utils/conntrack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "fmt" 19 | "net" 20 | 21 | "github.com/vishvananda/netlink" 22 | "golang.org/x/sys/unix" 23 | ) 24 | 25 | // Assigned Internet Protocol Numbers 26 | // https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml 27 | const ( 28 | PROTOCOL_TCP = 6 29 | PROTOCOL_UDP = 17 30 | PROTOCOL_SCTP = 132 31 | ) 32 | 33 | // getNetlinkFamily returns the Netlink IP family constant 34 | func getNetlinkFamily(isIPv6 bool) netlink.InetFamily { 35 | if isIPv6 { 36 | return unix.AF_INET6 37 | } 38 | return unix.AF_INET 39 | } 40 | 41 | // DeleteConntrackEntriesForDstIP delete the conntrack entries for the connections 42 | // specified by the given destination IP and protocol 43 | func DeleteConntrackEntriesForDstIP(dstIP string, protocol uint8) error { 44 | ip := net.ParseIP(dstIP) 45 | if ip == nil { 46 | return fmt.Errorf("error deleting connection tracking state, bad IP %s", ip) 47 | } 48 | family := getNetlinkFamily(ip.To4() == nil) 49 | 50 | filter := &netlink.ConntrackFilter{} 51 | filter.AddIP(netlink.ConntrackOrigDstIP, ip) 52 | filter.AddProtocol(protocol) 53 | 54 | _, err := netlink.ConntrackDeleteFilter(netlink.ConntrackTable, family, filter) 55 | if err != nil { 56 | return fmt.Errorf("error deleting connection tracking state for protocol: %d IP: %s, error: %v", protocol, ip, err) 57 | } 58 | return nil 59 | } 60 | 61 | // DeleteConntrackEntriesForDstPort delete the conntrack entries for the connections specified 62 | // by the given destination port, protocol and IP family 63 | func DeleteConntrackEntriesForDstPort(port uint16, protocol uint8, family netlink.InetFamily) error { 64 | filter := &netlink.ConntrackFilter{} 65 | filter.AddProtocol(protocol) 66 | filter.AddPort(netlink.ConntrackOrigDstPort, port) 67 | 68 | _, err := netlink.ConntrackDeleteFilter(netlink.ConntrackTable, family, filter) 69 | if err != nil { 70 | return fmt.Errorf("error deleting connection tracking state for protocol: %d Port: %d, error: %v", protocol, port, err) 71 | } 72 | return nil 73 | } 74 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Kruise Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "flag" 21 | "os" 22 | 23 | "github.com/openkruise/samples/lifecycle-hook-controller/controllers/predeletesample" 24 | 25 | "k8s.io/apimachinery/pkg/runtime" 26 | clientgoscheme "k8s.io/client-go/kubernetes/scheme" 27 | _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" 28 | ctrl "sigs.k8s.io/controller-runtime" 29 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 30 | // +kubebuilder:scaffold:imports 31 | ) 32 | 33 | var ( 34 | scheme = runtime.NewScheme() 35 | setupLog = ctrl.Log.WithName("setup") 36 | ) 37 | 38 | func init() { 39 | _ = clientgoscheme.AddToScheme(scheme) 40 | 41 | // +kubebuilder:scaffold:scheme 42 | } 43 | 44 | func main() { 45 | var metricsAddr string 46 | var enableLeaderElection bool 47 | flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") 48 | flag.BoolVar(&enableLeaderElection, "enable-leader-election", false, 49 | "Enable leader election for controller manager. "+ 50 | "Enabling this will ensure there is only one active controller manager.") 51 | flag.Parse() 52 | 53 | ctrl.SetLogger(zap.New(zap.UseDevMode(true))) 54 | 55 | mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ 56 | Scheme: scheme, 57 | MetricsBindAddress: metricsAddr, 58 | Port: 9443, 59 | LeaderElection: enableLeaderElection, 60 | LeaderElectionID: "fbfa4683.sample.kruise.io", 61 | }) 62 | if err != nil { 63 | setupLog.Error(err, "unable to start manager") 64 | os.Exit(1) 65 | } 66 | 67 | if err = (&predeletesample.SampleReconciler{ 68 | Client: mgr.GetClient(), 69 | Log: ctrl.Log.WithName("controllers").WithName("Sample"), 70 | Scheme: mgr.GetScheme(), 71 | }).SetupWithManager(mgr); err != nil { 72 | setupLog.Error(err, "unable to create controller", "controller", "Sample") 73 | os.Exit(1) 74 | } 75 | // +kubebuilder:scaffold:builder 76 | 77 | setupLog.Info("starting manager") 78 | if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { 79 | setupLog.Error(err, "problem running manager") 80 | os.Exit(1) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: sample-hook-controller-system 3 | 4 | # Value of this field is prepended to the 5 | # names of all resources, e.g. a deployment named 6 | # "wordpress" becomes "alices-wordpress". 7 | # Note that it should also match with the prefix (text before '-') of the namespace 8 | # field above. 9 | namePrefix: sample-hook-controller- 10 | 11 | # Labels to add to all resources and selectors. 12 | #commonLabels: 13 | # someName: someValue 14 | 15 | bases: 16 | - ../crd 17 | - ../rbac 18 | - ../manager 19 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 20 | # crd/kustomization.yaml 21 | #- ../webhook 22 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. 23 | #- ../certmanager 24 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 25 | #- ../prometheus 26 | 27 | patchesStrategicMerge: 28 | # Protect the /metrics endpoint by putting it behind auth. 29 | # If you want your controller-manager to expose the /metrics 30 | # endpoint w/o any authn/z, please comment the following line. 31 | - manager_auth_proxy_patch.yaml 32 | 33 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 34 | # crd/kustomization.yaml 35 | #- manager_webhook_patch.yaml 36 | 37 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 38 | # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. 39 | # 'CERTMANAGER' needs to be enabled to use ca injection 40 | #- webhookcainjection_patch.yaml 41 | 42 | # the following config is for teaching kustomize how to do var substitution 43 | vars: 44 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. 45 | #- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR 46 | # objref: 47 | # kind: Certificate 48 | # group: cert-manager.io 49 | # version: v1alpha2 50 | # name: serving-cert # this name should match the one in certificate.yaml 51 | # fieldref: 52 | # fieldpath: metadata.namespace 53 | #- name: CERTIFICATE_NAME 54 | # objref: 55 | # kind: Certificate 56 | # group: cert-manager.io 57 | # version: v1alpha2 58 | # name: serving-cert # this name should match the one in certificate.yaml 59 | #- name: SERVICE_NAMESPACE # namespace of the service 60 | # objref: 61 | # kind: Service 62 | # version: v1 63 | # name: webhook-service 64 | # fieldref: 65 | # fieldpath: metadata.namespace 66 | #- name: SERVICE_NAME 67 | # objref: 68 | # kind: Service 69 | # version: v1 70 | # name: webhook-service 71 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range_set.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package allocator 16 | 17 | import ( 18 | "fmt" 19 | "net" 20 | "strings" 21 | ) 22 | 23 | // Contains returns true if any range in this set contains an IP 24 | func (s *RangeSet) Contains(addr net.IP) bool { 25 | r, _ := s.RangeFor(addr) 26 | return r != nil 27 | } 28 | 29 | // RangeFor finds the range that contains an IP, or nil if not found 30 | func (s *RangeSet) RangeFor(addr net.IP) (*Range, error) { 31 | if err := canonicalizeIP(&addr); err != nil { 32 | return nil, err 33 | } 34 | 35 | for _, r := range *s { 36 | if r.Contains(addr) { 37 | return &r, nil 38 | } 39 | } 40 | 41 | return nil, fmt.Errorf("%s not in range set %s", addr.String(), s.String()) 42 | } 43 | 44 | // Overlaps returns true if any ranges in any set overlap with this one 45 | func (s *RangeSet) Overlaps(p1 *RangeSet) bool { 46 | for _, r := range *s { 47 | for _, r1 := range *p1 { 48 | if r.Overlaps(&r1) { 49 | return true 50 | } 51 | } 52 | } 53 | return false 54 | } 55 | 56 | // Canonicalize ensures the RangeSet is in a standard form, and detects any 57 | // invalid input. Call Range.Canonicalize() on every Range in the set 58 | func (s *RangeSet) Canonicalize() error { 59 | if len(*s) == 0 { 60 | return fmt.Errorf("empty range set") 61 | } 62 | 63 | fam := 0 64 | for i := range *s { 65 | if err := (*s)[i].Canonicalize(); err != nil { 66 | return err 67 | } 68 | if i == 0 { 69 | fam = len((*s)[i].RangeStart) 70 | } else { 71 | if fam != len((*s)[i].RangeStart) { 72 | return fmt.Errorf("mixed address families") 73 | } 74 | } 75 | } 76 | 77 | // Make sure none of the ranges in the set overlap 78 | l := len(*s) 79 | for i, r1 := range (*s)[:l-1] { 80 | for _, r2 := range (*s)[i+1:] { 81 | if r1.Overlaps(&r2) { 82 | return fmt.Errorf("subnets %s and %s overlap", r1.String(), r2.String()) 83 | } 84 | } 85 | } 86 | 87 | return nil 88 | } 89 | 90 | func (s *RangeSet) String() string { 91 | out := []string{} 92 | for _, r := range *s { 93 | out = append(out, r.String()) 94 | } 95 | 96 | return strings.Join(out, ",") 97 | } 98 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/utils/iptables_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2018 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "fmt" 19 | "math/rand" 20 | "runtime" 21 | 22 | "github.com/containernetworking/plugins/pkg/ns" 23 | "github.com/containernetworking/plugins/pkg/testutils" 24 | "github.com/coreos/go-iptables/iptables" 25 | . "github.com/onsi/ginkgo" 26 | . "github.com/onsi/gomega" 27 | ) 28 | 29 | const TABLE = "filter" // We'll monkey around here 30 | 31 | var _ = Describe("chain tests", func() { 32 | var testChain string 33 | var ipt *iptables.IPTables 34 | var cleanup func() 35 | 36 | BeforeEach(func() { 37 | 38 | // Save a reference to the original namespace, 39 | // Add a new NS 40 | currNs, err := ns.GetCurrentNS() 41 | Expect(err).NotTo(HaveOccurred()) 42 | 43 | testNs, err := testutils.NewNS() 44 | Expect(err).NotTo(HaveOccurred()) 45 | 46 | testChain = fmt.Sprintf("cni-test-%d", rand.Intn(10000000)) 47 | 48 | ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4) 49 | Expect(err).NotTo(HaveOccurred()) 50 | 51 | runtime.LockOSThread() 52 | err = testNs.Set() 53 | Expect(err).NotTo(HaveOccurred()) 54 | 55 | cleanup = func() { 56 | if ipt == nil { 57 | return 58 | } 59 | ipt.ClearChain(TABLE, testChain) 60 | ipt.DeleteChain(TABLE, testChain) 61 | currNs.Set() 62 | } 63 | 64 | }) 65 | 66 | AfterEach(func() { 67 | cleanup() 68 | }) 69 | 70 | Describe("EnsureChain", func() { 71 | It("creates chains idempotently", func() { 72 | err := EnsureChain(ipt, TABLE, testChain) 73 | Expect(err).NotTo(HaveOccurred()) 74 | 75 | // Create it again! 76 | err = EnsureChain(ipt, TABLE, testChain) 77 | Expect(err).NotTo(HaveOccurred()) 78 | }) 79 | }) 80 | 81 | Describe("DeleteChain", func() { 82 | It("delete chains idempotently", func() { 83 | // Create chain 84 | err := EnsureChain(ipt, TABLE, testChain) 85 | Expect(err).NotTo(HaveOccurred()) 86 | 87 | // Delete chain 88 | err = DeleteChain(ipt, TABLE, testChain) 89 | Expect(err).NotTo(HaveOccurred()) 90 | 91 | // Delete it again! 92 | err = DeleteChain(ipt, TABLE, testChain) 93 | Expect(err).NotTo(HaveOccurred()) 94 | }) 95 | }) 96 | 97 | }) 98 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/ip/ip.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ip 16 | 17 | import ( 18 | "fmt" 19 | "net" 20 | "strings" 21 | ) 22 | 23 | // IP is a CNI maintained type inherited from net.IPNet which can 24 | // represent a single IP address with or without prefix. 25 | type IP struct { 26 | net.IPNet 27 | } 28 | 29 | // newIP will create an IP with net.IP and net.IPMask 30 | func newIP(ip net.IP, mask net.IPMask) *IP { 31 | return &IP{ 32 | IPNet: net.IPNet{ 33 | IP: ip, 34 | Mask: mask, 35 | }, 36 | } 37 | } 38 | 39 | // ParseIP will parse string s as an IP, and return it. 40 | // The string s must be formed like [/]. 41 | // If s is not a valid textual representation of an IP, 42 | // will return nil. 43 | func ParseIP(s string) *IP { 44 | if strings.ContainsAny(s, "/") { 45 | ip, ipNet, err := net.ParseCIDR(s) 46 | if err != nil { 47 | return nil 48 | } 49 | return newIP(ip, ipNet.Mask) 50 | } else { 51 | ip := net.ParseIP(s) 52 | if ip == nil { 53 | return nil 54 | } 55 | return newIP(ip, nil) 56 | } 57 | } 58 | 59 | // ToIP will return a net.IP in standard form from this IP. 60 | // If this IP can not be converted to a valid net.IP, will return nil. 61 | func (i *IP) ToIP() net.IP { 62 | switch { 63 | case i.IP.To4() != nil: 64 | return i.IP.To4() 65 | case i.IP.To16() != nil: 66 | return i.IP.To16() 67 | default: 68 | return nil 69 | } 70 | } 71 | 72 | // String returns the string form of this IP. 73 | func (i *IP) String() string { 74 | if len(i.Mask) > 0 { 75 | return i.IPNet.String() 76 | } 77 | return i.IP.String() 78 | } 79 | 80 | // MarshalText implements the encoding.TextMarshaler interface. 81 | // The encoding is the same as returned by String, 82 | // But when len(ip) is zero, will return an empty slice. 83 | func (i *IP) MarshalText() ([]byte, error) { 84 | if len(i.IP) == 0 { 85 | return []byte{}, nil 86 | } 87 | return []byte(i.String()), nil 88 | } 89 | 90 | // UnmarshalText implements the encoding.TextUnmarshaler interface. 91 | // The textual bytes are expected in a form accepted by Parse, 92 | // But when len(b) is zero, will return an empty IP. 93 | func (i *IP) UnmarshalText(b []byte) error { 94 | if len(b) == 0 { 95 | *i = IP{} 96 | return nil 97 | } 98 | 99 | ip := ParseIP(string(b)) 100 | if ip == nil { 101 | return fmt.Errorf("invalid IP address %s", string(b)) 102 | } 103 | *i = *ip 104 | return nil 105 | } 106 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/testutils/echo/echo_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net" 7 | "os/exec" 8 | "strings" 9 | 10 | . "github.com/onsi/ginkgo" 11 | . "github.com/onsi/gomega" 12 | "github.com/onsi/gomega/gbytes" 13 | "github.com/onsi/gomega/gexec" 14 | ) 15 | 16 | var serverBinaryPath, clientBinaryPath string 17 | 18 | var _ = SynchronizedBeforeSuite(func() []byte { 19 | serverBinaryPath, err := gexec.Build("github.com/containernetworking/plugins/pkg/testutils/echo/server") 20 | Expect(err).NotTo(HaveOccurred()) 21 | clientBinaryPath, err := gexec.Build("github.com/containernetworking/plugins/pkg/testutils/echo/client") 22 | Expect(err).NotTo(HaveOccurred()) 23 | return []byte(strings.Join([]string{serverBinaryPath, clientBinaryPath}, ",")) 24 | }, func(data []byte) { 25 | binaries := strings.Split(string(data), ",") 26 | serverBinaryPath = binaries[0] 27 | clientBinaryPath = binaries[1] 28 | }) 29 | 30 | var _ = SynchronizedAfterSuite(func() {}, func() { 31 | gexec.CleanupBuildArtifacts() 32 | }) 33 | 34 | var _ = Describe("Echosvr", func() { 35 | var session *gexec.Session 36 | BeforeEach(func() { 37 | var err error 38 | cmd := exec.Command(serverBinaryPath) 39 | session, err = gexec.Start(cmd, GinkgoWriter, GinkgoWriter) 40 | Expect(err).NotTo(HaveOccurred()) 41 | }) 42 | 43 | AfterEach(func() { 44 | session.Kill().Wait() 45 | }) 46 | 47 | Context("Server test", func() { 48 | It("starts and doesn't terminate immediately", func() { 49 | Consistently(session).ShouldNot(gexec.Exit()) 50 | }) 51 | 52 | tryConnect := func() (net.Conn, error) { 53 | programOutput := session.Out.Contents() 54 | addr := strings.TrimSpace(string(programOutput)) 55 | 56 | conn, err := net.Dial("tcp", addr) 57 | if err != nil { 58 | return nil, err 59 | } 60 | return conn, err 61 | } 62 | 63 | It("prints its listening address to stdout", func() { 64 | Eventually(session.Out).Should(gbytes.Say("\n")) 65 | conn, err := tryConnect() 66 | Expect(err).NotTo(HaveOccurred()) 67 | conn.Close() 68 | }) 69 | 70 | It("will echo data back to us", func() { 71 | Eventually(session.Out).Should(gbytes.Say("\n")) 72 | conn, err := tryConnect() 73 | Expect(err).NotTo(HaveOccurred()) 74 | defer conn.Close() 75 | 76 | fmt.Fprintf(conn, "hello\n") 77 | Expect(ioutil.ReadAll(conn)).To(Equal([]byte("hello"))) 78 | }) 79 | }) 80 | 81 | Context("Client Server Test", func() { 82 | It("starts and doesn't terminate immediately", func() { 83 | Consistently(session).ShouldNot(gexec.Exit()) 84 | }) 85 | 86 | It("connects successfully using echo client", func() { 87 | Eventually(session.Out).Should(gbytes.Say("\n")) 88 | serverAddress := strings.TrimSpace(string(session.Out.Contents())) 89 | fmt.Println("Server address", string(serverAddress)) 90 | 91 | cmd := exec.Command(clientBinaryPath, "-target", serverAddress, "-message", "hello") 92 | clientSession, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter) 93 | Expect(err).NotTo(HaveOccurred()) 94 | Eventually(clientSession.Out).Should(gbytes.Say("hello")) 95 | Eventually(clientSession).Should(gexec.Exit()) 96 | }) 97 | }) 98 | }) 99 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/utils/sysctl/sysctl_linux_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2020 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sysctl_test 16 | 17 | import ( 18 | "fmt" 19 | "math/rand" 20 | "runtime" 21 | "strings" 22 | 23 | "github.com/containernetworking/plugins/pkg/ns" 24 | "github.com/containernetworking/plugins/pkg/testutils" 25 | "github.com/containernetworking/plugins/pkg/utils/sysctl" 26 | . "github.com/onsi/ginkgo" 27 | . "github.com/onsi/gomega" 28 | "github.com/vishvananda/netlink" 29 | ) 30 | 31 | const ( 32 | sysctlDotKeyTemplate = "net.ipv4.conf.%s.proxy_arp" 33 | sysctlSlashKeyTemplate = "net/ipv4/conf/%s/proxy_arp" 34 | ) 35 | 36 | var _ = Describe("Sysctl tests", func() { 37 | var testIfaceName string 38 | var cleanup func() 39 | 40 | BeforeEach(func() { 41 | 42 | // Save a reference to the original namespace, 43 | // Add a new NS 44 | currNs, err := ns.GetCurrentNS() 45 | Expect(err).NotTo(HaveOccurred()) 46 | 47 | testNs, err := testutils.NewNS() 48 | Expect(err).NotTo(HaveOccurred()) 49 | 50 | testIfaceName = fmt.Sprintf("cnitest.%d", rand.Intn(100000)) 51 | testIface := &netlink.Dummy{ 52 | LinkAttrs: netlink.LinkAttrs{ 53 | Name: testIfaceName, 54 | Namespace: netlink.NsFd(int(testNs.Fd())), 55 | }, 56 | } 57 | 58 | err = netlink.LinkAdd(testIface) 59 | Expect(err).NotTo(HaveOccurred()) 60 | 61 | runtime.LockOSThread() 62 | err = testNs.Set() 63 | Expect(err).NotTo(HaveOccurred()) 64 | 65 | cleanup = func() { 66 | netlink.LinkDel(testIface) 67 | currNs.Set() 68 | } 69 | 70 | }) 71 | 72 | AfterEach(func() { 73 | cleanup() 74 | }) 75 | 76 | Describe("Sysctl", func() { 77 | It("reads keys with dot separators", func() { 78 | sysctlIfaceName := strings.Replace(testIfaceName, ".", "/", -1) 79 | sysctlKey := fmt.Sprintf(sysctlDotKeyTemplate, sysctlIfaceName) 80 | 81 | _, err := sysctl.Sysctl(sysctlKey) 82 | Expect(err).NotTo(HaveOccurred()) 83 | }) 84 | }) 85 | 86 | Describe("Sysctl", func() { 87 | It("reads keys with slash separators", func() { 88 | sysctlKey := fmt.Sprintf(sysctlSlashKeyTemplate, testIfaceName) 89 | 90 | _, err := sysctl.Sysctl(sysctlKey) 91 | Expect(err).NotTo(HaveOccurred()) 92 | }) 93 | }) 94 | 95 | Describe("Sysctl", func() { 96 | It("writes keys with dot separators", func() { 97 | sysctlIfaceName := strings.Replace(testIfaceName, ".", "/", -1) 98 | sysctlKey := fmt.Sprintf(sysctlDotKeyTemplate, sysctlIfaceName) 99 | 100 | _, err := sysctl.Sysctl(sysctlKey, "1") 101 | Expect(err).NotTo(HaveOccurred()) 102 | }) 103 | }) 104 | 105 | Describe("Sysctl", func() { 106 | It("writes keys with slash separators", func() { 107 | sysctlKey := fmt.Sprintf(sysctlSlashKeyTemplate, testIfaceName) 108 | 109 | _, err := sysctl.Sysctl(sysctlKey, "1") 110 | Expect(err).NotTo(HaveOccurred()) 111 | }) 112 | }) 113 | 114 | }) 115 | -------------------------------------------------------------------------------- /lifecycle-hook-controller/controllers/predeletesample/sample_controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Kruise Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package predeletesample 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/go-logr/logr" 24 | kruiseappspub "github.com/openkruise/kruise-api/apps/pub" 25 | v1 "k8s.io/api/core/v1" 26 | "k8s.io/apimachinery/pkg/api/errors" 27 | "k8s.io/apimachinery/pkg/runtime" 28 | "k8s.io/apimachinery/pkg/types" 29 | ctrl "sigs.k8s.io/controller-runtime" 30 | "sigs.k8s.io/controller-runtime/pkg/client" 31 | "sigs.k8s.io/controller-runtime/pkg/controller" 32 | "sigs.k8s.io/controller-runtime/pkg/event" 33 | "sigs.k8s.io/controller-runtime/pkg/predicate" 34 | ) 35 | 36 | const ( 37 | hookLabelKey = "hook.example.kruise.io/unready-blocker" 38 | ) 39 | 40 | // SampleReconciler reconciles a Sample object 41 | type SampleReconciler struct { 42 | client.Client 43 | Log logr.Logger 44 | Scheme *runtime.Scheme 45 | } 46 | 47 | // +kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;update;patch 48 | 49 | func (r *SampleReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { 50 | pod := &v1.Pod{} 51 | if err := r.Get(context.TODO(), req.NamespacedName, pod); err != nil { 52 | if errors.IsNotFound(err) { 53 | return ctrl.Result{}, nil 54 | } 55 | r.Log.Error(err, "failed to get", "pod", req.NamespacedName) 56 | return ctrl.Result{}, err 57 | } 58 | 59 | if !isPreDeleteHooked(pod) { 60 | return ctrl.Result{}, nil 61 | } 62 | 63 | { 64 | // your hook logic here, e.g. call an external URL 65 | } 66 | 67 | // after hook succeeded: 68 | body := fmt.Sprintf(`{"metadata":{"labels":{"%s":"false"}}}`, hookLabelKey) 69 | if err := r.Patch(context.TODO(), pod, client.RawPatch(types.StrategicMergePatchType, []byte(body))); err != nil { 70 | r.Log.Error(err, "failed to patch", "pod", req.NamespacedName) 71 | return ctrl.Result{}, err 72 | } 73 | 74 | r.Log.Info("Successfully handle pre-delete hook", "pod", req.NamespacedName) 75 | return ctrl.Result{}, nil 76 | } 77 | 78 | func (r *SampleReconciler) SetupWithManager(mgr ctrl.Manager) error { 79 | return ctrl.NewControllerManagedBy(mgr). 80 | WithOptions(controller.Options{MaxConcurrentReconciles: 3}). 81 | For(&v1.Pod{}). 82 | WithEventFilter(predicate.Funcs{ 83 | CreateFunc: func(e event.CreateEvent) bool { 84 | pod := e.Object.(*v1.Pod) 85 | return isPreDeleteHooked(pod) 86 | }, 87 | DeleteFunc: func(_ event.DeleteEvent) bool { return false }, 88 | GenericFunc: func(_ event.GenericEvent) bool { return false }, 89 | UpdateFunc: func(e event.UpdateEvent) bool { 90 | pod := e.ObjectNew.(*v1.Pod) 91 | return isPreDeleteHooked(pod) 92 | }, 93 | }). 94 | Complete(r) 95 | } 96 | 97 | func isPreDeleteHooked(pod *v1.Pod) bool { 98 | return pod.Labels[kruiseappspub.LifecycleStateKey] == string(kruiseappspub.LifecycleStatePreparingDelete) && pod.Labels[hookLabelKey] == "true" 99 | } 100 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/testutils/cmd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package testutils 16 | 17 | import ( 18 | "io/ioutil" 19 | "os" 20 | 21 | "github.com/containernetworking/cni/pkg/skel" 22 | "github.com/containernetworking/cni/pkg/types" 23 | "github.com/containernetworking/cni/pkg/version" 24 | ) 25 | 26 | func envCleanup() { 27 | os.Unsetenv("CNI_COMMAND") 28 | os.Unsetenv("CNI_PATH") 29 | os.Unsetenv("CNI_NETNS") 30 | os.Unsetenv("CNI_IFNAME") 31 | os.Unsetenv("CNI_CONTAINERID") 32 | } 33 | 34 | func CmdAdd(cniNetns, cniContainerID, cniIfname string, conf []byte, f func() error) (types.Result, []byte, error) { 35 | os.Setenv("CNI_COMMAND", "ADD") 36 | os.Setenv("CNI_PATH", os.Getenv("PATH")) 37 | os.Setenv("CNI_NETNS", cniNetns) 38 | os.Setenv("CNI_IFNAME", cniIfname) 39 | os.Setenv("CNI_CONTAINERID", cniContainerID) 40 | defer envCleanup() 41 | 42 | // Redirect stdout to capture plugin result 43 | oldStdout := os.Stdout 44 | r, w, err := os.Pipe() 45 | if err != nil { 46 | return nil, nil, err 47 | } 48 | 49 | os.Stdout = w 50 | err = f() 51 | w.Close() 52 | 53 | var out []byte 54 | if err == nil { 55 | out, err = ioutil.ReadAll(r) 56 | } 57 | os.Stdout = oldStdout 58 | 59 | // Return errors after restoring stdout so Ginkgo will correctly 60 | // emit verbose error information on stdout 61 | if err != nil { 62 | return nil, nil, err 63 | } 64 | 65 | // Plugin must return result in same version as specified in netconf 66 | versionDecoder := &version.ConfigDecoder{} 67 | confVersion, err := versionDecoder.Decode(conf) 68 | if err != nil { 69 | return nil, nil, err 70 | } 71 | 72 | result, err := version.NewResult(confVersion, out) 73 | if err != nil { 74 | return nil, nil, err 75 | } 76 | 77 | return result, out, nil 78 | } 79 | 80 | func CmdAddWithArgs(args *skel.CmdArgs, f func() error) (types.Result, []byte, error) { 81 | return CmdAdd(args.Netns, args.ContainerID, args.IfName, args.StdinData, f) 82 | } 83 | 84 | func CmdCheck(cniNetns, cniContainerID, cniIfname string, conf []byte, f func() error) error { 85 | os.Setenv("CNI_COMMAND", "CHECK") 86 | os.Setenv("CNI_PATH", os.Getenv("PATH")) 87 | os.Setenv("CNI_NETNS", cniNetns) 88 | os.Setenv("CNI_IFNAME", cniIfname) 89 | os.Setenv("CNI_CONTAINERID", cniContainerID) 90 | defer envCleanup() 91 | 92 | return f() 93 | } 94 | 95 | func CmdCheckWithArgs(args *skel.CmdArgs, f func() error) error { 96 | return CmdCheck(args.Netns, args.ContainerID, args.IfName, args.StdinData, f) 97 | } 98 | 99 | func CmdDel(cniNetns, cniContainerID, cniIfname string, f func() error) error { 100 | os.Setenv("CNI_COMMAND", "DEL") 101 | os.Setenv("CNI_PATH", os.Getenv("PATH")) 102 | os.Setenv("CNI_NETNS", cniNetns) 103 | os.Setenv("CNI_IFNAME", cniIfname) 104 | os.Setenv("CNI_CONTAINERID", cniContainerID) 105 | defer envCleanup() 106 | 107 | return f() 108 | } 109 | 110 | func CmdDelWithArgs(args *skel.CmdArgs, f func() error) error { 111 | return CmdDel(args.Netns, args.ContainerID, args.IfName, f) 112 | } 113 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/ip/utils_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | // Copyright 2016 CNI authors 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | package ip 19 | 20 | import ( 21 | "fmt" 22 | "net" 23 | 24 | "github.com/containernetworking/cni/pkg/types" 25 | current "github.com/containernetworking/cni/pkg/types/100" 26 | "github.com/vishvananda/netlink" 27 | ) 28 | 29 | func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig) error { 30 | 31 | // Ensure ips 32 | for _, ips := range resultIPs { 33 | ourAddr := netlink.Addr{IPNet: &ips.Address} 34 | match := false 35 | 36 | link, err := netlink.LinkByName(ifName) 37 | if err != nil { 38 | return fmt.Errorf("Cannot find container link %v", ifName) 39 | } 40 | 41 | addrList, err := netlink.AddrList(link, netlink.FAMILY_ALL) 42 | if err != nil { 43 | return fmt.Errorf("Cannot obtain List of IP Addresses") 44 | } 45 | 46 | for _, addr := range addrList { 47 | if addr.Equal(ourAddr) { 48 | match = true 49 | break 50 | } 51 | } 52 | if match == false { 53 | return fmt.Errorf("Failed to match addr %v on interface %v", ourAddr, ifName) 54 | } 55 | 56 | // Convert the host/prefixlen to just prefix for route lookup. 57 | _, ourPrefix, err := net.ParseCIDR(ourAddr.String()) 58 | 59 | findGwy := &netlink.Route{Dst: ourPrefix} 60 | routeFilter := netlink.RT_FILTER_DST 61 | 62 | family := netlink.FAMILY_V6 63 | if ips.Address.IP.To4() != nil { 64 | family = netlink.FAMILY_V4 65 | } 66 | 67 | gwy, err := netlink.RouteListFiltered(family, findGwy, routeFilter) 68 | if err != nil { 69 | return fmt.Errorf("Error %v trying to find Gateway %v for interface %v", err, ips.Gateway, ifName) 70 | } 71 | if gwy == nil { 72 | return fmt.Errorf("Failed to find Gateway %v for interface %v", ips.Gateway, ifName) 73 | } 74 | } 75 | 76 | return nil 77 | } 78 | 79 | func ValidateExpectedRoute(resultRoutes []*types.Route) error { 80 | 81 | // Ensure that each static route in prevResults is found in the routing table 82 | for _, route := range resultRoutes { 83 | find := &netlink.Route{Dst: &route.Dst, Gw: route.GW} 84 | routeFilter := netlink.RT_FILTER_DST | netlink.RT_FILTER_GW 85 | var family int 86 | 87 | switch { 88 | case route.Dst.IP.To4() != nil: 89 | family = netlink.FAMILY_V4 90 | // Default route needs Dst set to nil 91 | if route.Dst.String() == "0.0.0.0/0" { 92 | find = &netlink.Route{Dst: nil, Gw: route.GW} 93 | routeFilter = netlink.RT_FILTER_DST 94 | } 95 | case len(route.Dst.IP) == net.IPv6len: 96 | family = netlink.FAMILY_V6 97 | // Default route needs Dst set to nil 98 | if route.Dst.String() == "::/0" { 99 | find = &netlink.Route{Dst: nil, Gw: route.GW} 100 | routeFilter = netlink.RT_FILTER_DST 101 | } 102 | default: 103 | return fmt.Errorf("Invalid static route found %v", route) 104 | } 105 | 106 | wasFound, err := netlink.RouteListFiltered(family, find, routeFilter) 107 | if err != nil { 108 | return fmt.Errorf("Expected Route %v not route table lookup error %v", route, err) 109 | } 110 | if wasFound == nil { 111 | return fmt.Errorf("Expected Route %v not found in routing table", route) 112 | } 113 | } 114 | 115 | return nil 116 | } 117 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/ns/README.md: -------------------------------------------------------------------------------- 1 | ### Namespaces, Threads, and Go 2 | On Linux each OS thread can have a different network namespace. Go's thread scheduling model switches goroutines between OS threads based on OS thread load and whether the goroutine would block other goroutines. This can result in a goroutine switching network namespaces without notice and lead to errors in your code. 3 | 4 | ### Namespace Switching 5 | Switching namespaces with the `ns.Set()` method is not recommended without additional strategies to prevent unexpected namespace changes when your goroutines switch OS threads. 6 | 7 | Go provides the `runtime.LockOSThread()` function to ensure a specific goroutine executes on its current OS thread and prevents any other goroutine from running in that thread until the locked one exits. Careful usage of `LockOSThread()` and goroutines can provide good control over which network namespace a given goroutine executes in. 8 | 9 | For example, you cannot rely on the `ns.Set()` namespace being the current namespace after the `Set()` call unless you do two things. First, the goroutine calling `Set()` must have previously called `LockOSThread()`. Second, you must ensure `runtime.UnlockOSThread()` is not called somewhere in-between. You also cannot rely on the initial network namespace remaining the current network namespace if any other code in your program switches namespaces, unless you have already called `LockOSThread()` in that goroutine. Note that `LockOSThread()` prevents the Go scheduler from optimally scheduling goroutines for best performance, so `LockOSThread()` should only be used in small, isolated goroutines that release the lock quickly. 10 | 11 | ### Do() The Recommended Thing 12 | The `ns.Do()` method provides **partial** control over network namespaces for you by implementing these strategies. All code dependent on a particular network namespace (including the root namespace) should be wrapped in the `ns.Do()` method to ensure the correct namespace is selected for the duration of your code. For example: 13 | 14 | ```go 15 | err = targetNs.Do(func(hostNs ns.NetNS) error { 16 | dummy := &netlink.Dummy{ 17 | LinkAttrs: netlink.LinkAttrs{ 18 | Name: "dummy0", 19 | }, 20 | } 21 | return netlink.LinkAdd(dummy) 22 | }) 23 | ``` 24 | 25 | Note this requirement to wrap every network call is very onerous - any libraries you call might call out to network services such as DNS, and all such calls need to be protected after you call `ns.Do()`. All goroutines spawned from within the `ns.Do` will not inherit the new namespace. The CNI plugins all exit very soon after calling `ns.Do()` which helps to minimize the problem. 26 | 27 | When a new thread is spawned in Linux, it inherits the namespace of its parent. In versions of go **prior to 1.10**, if the runtime spawns a new OS thread, it picks the parent randomly. If the chosen parent thread has been moved to a new namespace (even temporarily), the new OS thread will be permanently "stuck in the wrong namespace", and goroutines will non-deterministically switch namespaces as they are rescheduled. 28 | 29 | In short, **there was no safe way to change network namespaces, even temporarily, from within a long-lived, multithreaded Go process**. If you wish to do this, you must use go 1.10 or greater. 30 | 31 | 32 | ### Creating network namespaces 33 | Earlier versions of this library managed namespace creation, but as CNI does not actually utilize this feature (and it was essentially unmaintained), it was removed. If you're writing a container runtime, you should implement namespace management yourself. However, there are some gotchas when doing so, especially around handling `/var/run/netns`. A reasonably correct reference implementation, borrowed from `rkt`, can be found in `pkg/testutils/netns_linux.go` if you're in need of a source of inspiration. 34 | 35 | 36 | ### Further Reading 37 | - https://github.com/golang/go/wiki/LockOSThread 38 | - http://morsmachine.dk/go-scheduler 39 | - https://github.com/containernetworking/cni/issues/262 40 | - https://golang.org/pkg/runtime/ 41 | - https://www.weave.works/blog/linux-namespaces-and-go-don-t-mix 42 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/ip/ipmasq_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ip 16 | 17 | import ( 18 | "fmt" 19 | "net" 20 | 21 | "github.com/coreos/go-iptables/iptables" 22 | ) 23 | 24 | // SetupIPMasq installs iptables rules to masquerade traffic 25 | // coming from ip of ipn and going outside of ipn 26 | func SetupIPMasq(ipn *net.IPNet, chain string, comment string) error { 27 | isV6 := ipn.IP.To4() == nil 28 | 29 | var ipt *iptables.IPTables 30 | var err error 31 | var multicastNet string 32 | 33 | if isV6 { 34 | ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv6) 35 | multicastNet = "ff00::/8" 36 | } else { 37 | ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4) 38 | multicastNet = "224.0.0.0/4" 39 | } 40 | if err != nil { 41 | return fmt.Errorf("failed to locate iptables: %v", err) 42 | } 43 | 44 | // Create chain if doesn't exist 45 | exists := false 46 | chains, err := ipt.ListChains("nat") 47 | if err != nil { 48 | return fmt.Errorf("failed to list chains: %v", err) 49 | } 50 | for _, ch := range chains { 51 | if ch == chain { 52 | exists = true 53 | break 54 | } 55 | } 56 | if !exists { 57 | if err = ipt.NewChain("nat", chain); err != nil { 58 | return err 59 | } 60 | } 61 | 62 | // Packets to this network should not be touched 63 | if err := ipt.AppendUnique("nat", chain, "-d", ipn.String(), "-j", "ACCEPT", "-m", "comment", "--comment", comment); err != nil { 64 | return err 65 | } 66 | 67 | // Don't masquerade multicast - pods should be able to talk to other pods 68 | // on the local network via multicast. 69 | if err := ipt.AppendUnique("nat", chain, "!", "-d", multicastNet, "-j", "MASQUERADE", "-m", "comment", "--comment", comment); err != nil { 70 | return err 71 | } 72 | 73 | // Packets from the specific IP of this network will hit the chain 74 | return ipt.AppendUnique("nat", "POSTROUTING", "-s", ipn.IP.String(), "-j", chain, "-m", "comment", "--comment", comment) 75 | } 76 | 77 | // TeardownIPMasq undoes the effects of SetupIPMasq 78 | func TeardownIPMasq(ipn *net.IPNet, chain string, comment string) error { 79 | isV6 := ipn.IP.To4() == nil 80 | 81 | var ipt *iptables.IPTables 82 | var err error 83 | 84 | if isV6 { 85 | ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv6) 86 | } else { 87 | ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4) 88 | } 89 | if err != nil { 90 | return fmt.Errorf("failed to locate iptables: %v", err) 91 | } 92 | 93 | err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.IP.String(), "-j", chain, "-m", "comment", "--comment", comment) 94 | if err != nil && !isNotExist(err) { 95 | return err 96 | } 97 | 98 | // for downward compatibility 99 | err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.String(), "-j", chain, "-m", "comment", "--comment", comment) 100 | if err != nil && !isNotExist(err) { 101 | return err 102 | } 103 | 104 | err = ipt.ClearChain("nat", chain) 105 | if err != nil && !isNotExist(err) { 106 | return err 107 | 108 | } 109 | 110 | err = ipt.DeleteChain("nat", chain) 111 | if err != nil && !isNotExist(err) { 112 | return err 113 | } 114 | 115 | return nil 116 | } 117 | 118 | // isNotExist returnst true if the error is from iptables indicating 119 | // that the target does not exist. 120 | func isNotExist(err error) bool { 121 | e, ok := err.(*iptables.Error) 122 | if !ok { 123 | return false 124 | } 125 | return e.IsNotExist() 126 | } 127 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/ipam/ipam_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ipam 16 | 17 | import ( 18 | "fmt" 19 | "net" 20 | "os" 21 | 22 | current "github.com/containernetworking/cni/pkg/types/100" 23 | "github.com/containernetworking/plugins/pkg/ip" 24 | "github.com/containernetworking/plugins/pkg/utils/sysctl" 25 | 26 | "github.com/vishvananda/netlink" 27 | ) 28 | 29 | const ( 30 | // Note: use slash as separator so we can have dots in interface name (VLANs) 31 | DisableIPv6SysctlTemplate = "net/ipv6/conf/%s/disable_ipv6" 32 | ) 33 | 34 | // ConfigureIface takes the result of IPAM plugin and 35 | // applies to the ifName interface 36 | func ConfigureIface(ifName string, res *current.Result) error { 37 | if len(res.Interfaces) == 0 { 38 | return fmt.Errorf("no interfaces to configure") 39 | } 40 | 41 | link, err := netlink.LinkByName(ifName) 42 | if err != nil { 43 | return fmt.Errorf("failed to lookup %q: %v", ifName, err) 44 | } 45 | 46 | var v4gw, v6gw net.IP 47 | var has_enabled_ipv6 bool = false 48 | for _, ipc := range res.IPs { 49 | if ipc.Interface == nil { 50 | continue 51 | } 52 | intIdx := *ipc.Interface 53 | if intIdx < 0 || intIdx >= len(res.Interfaces) || res.Interfaces[intIdx].Name != ifName { 54 | // IP address is for a different interface 55 | return fmt.Errorf("failed to add IP addr %v to %q: invalid interface index", ipc, ifName) 56 | } 57 | 58 | // Make sure sysctl "disable_ipv6" is 0 if we are about to add 59 | // an IPv6 address to the interface 60 | if !has_enabled_ipv6 && ipc.Address.IP.To4() == nil { 61 | // Enabled IPv6 for loopback "lo" and the interface 62 | // being configured 63 | for _, iface := range [2]string{"lo", ifName} { 64 | ipv6SysctlValueName := fmt.Sprintf(DisableIPv6SysctlTemplate, iface) 65 | 66 | // Read current sysctl value 67 | value, err := sysctl.Sysctl(ipv6SysctlValueName) 68 | if err != nil { 69 | fmt.Fprintf(os.Stderr, "ipam_linux: failed to read sysctl %q: %v\n", ipv6SysctlValueName, err) 70 | continue 71 | } 72 | if value == "0" { 73 | continue 74 | } 75 | 76 | // Write sysctl to enable IPv6 77 | _, err = sysctl.Sysctl(ipv6SysctlValueName, "0") 78 | if err != nil { 79 | return fmt.Errorf("failed to enable IPv6 for interface %q (%s=%s): %v", iface, ipv6SysctlValueName, value, err) 80 | } 81 | } 82 | has_enabled_ipv6 = true 83 | } 84 | 85 | addr := &netlink.Addr{IPNet: &ipc.Address, Label: ""} 86 | if err = netlink.AddrAdd(link, addr); err != nil { 87 | return fmt.Errorf("failed to add IP addr %v to %q: %v", ipc, ifName, err) 88 | } 89 | 90 | gwIsV4 := ipc.Gateway.To4() != nil 91 | if gwIsV4 && v4gw == nil { 92 | v4gw = ipc.Gateway 93 | } else if !gwIsV4 && v6gw == nil { 94 | v6gw = ipc.Gateway 95 | } 96 | } 97 | 98 | if err := netlink.LinkSetUp(link); err != nil { 99 | return fmt.Errorf("failed to set %q UP: %v", ifName, err) 100 | } 101 | 102 | if v6gw != nil { 103 | ip.SettleAddresses(ifName, 10) 104 | } 105 | 106 | for _, r := range res.Routes { 107 | routeIsV4 := r.Dst.IP.To4() != nil 108 | gw := r.GW 109 | if gw == nil { 110 | if routeIsV4 && v4gw != nil { 111 | gw = v4gw 112 | } else if !routeIsV4 && v6gw != nil { 113 | gw = v6gw 114 | } 115 | } 116 | route := netlink.Route{ 117 | Dst: &r.Dst, 118 | LinkIndex: link.Attrs().Index, 119 | Gw: gw, 120 | } 121 | 122 | if err = netlink.RouteAddEcmp(&route); err != nil { 123 | return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, ifName, err) 124 | } 125 | } 126 | 127 | return nil 128 | } 129 | -------------------------------------------------------------------------------- /hotupgrade/vendor/k8s.io/klog/klog_file.go: -------------------------------------------------------------------------------- 1 | // Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ 2 | // 3 | // Copyright 2013 Google Inc. All Rights Reserved. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | // File I/O for logs. 18 | 19 | package klog 20 | 21 | import ( 22 | "errors" 23 | "fmt" 24 | "os" 25 | "os/user" 26 | "path/filepath" 27 | "strings" 28 | "sync" 29 | "time" 30 | ) 31 | 32 | // MaxSize is the maximum size of a log file in bytes. 33 | var MaxSize uint64 = 1024 * 1024 * 1800 34 | 35 | // logDirs lists the candidate directories for new log files. 36 | var logDirs []string 37 | 38 | func createLogDirs() { 39 | if logging.logDir != "" { 40 | logDirs = append(logDirs, logging.logDir) 41 | } 42 | logDirs = append(logDirs, os.TempDir()) 43 | } 44 | 45 | var ( 46 | pid = os.Getpid() 47 | program = filepath.Base(os.Args[0]) 48 | host = "unknownhost" 49 | userName = "unknownuser" 50 | ) 51 | 52 | func init() { 53 | h, err := os.Hostname() 54 | if err == nil { 55 | host = shortHostname(h) 56 | } 57 | 58 | current, err := user.Current() 59 | if err == nil { 60 | userName = current.Username 61 | } 62 | 63 | // Sanitize userName since it may contain filepath separators on Windows. 64 | userName = strings.Replace(userName, `\`, "_", -1) 65 | } 66 | 67 | // shortHostname returns its argument, truncating at the first period. 68 | // For instance, given "www.google.com" it returns "www". 69 | func shortHostname(hostname string) string { 70 | if i := strings.Index(hostname, "."); i >= 0 { 71 | return hostname[:i] 72 | } 73 | return hostname 74 | } 75 | 76 | // logName returns a new log file name containing tag, with start time t, and 77 | // the name for the symlink for tag. 78 | func logName(tag string, t time.Time) (name, link string) { 79 | name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d", 80 | program, 81 | host, 82 | userName, 83 | tag, 84 | t.Year(), 85 | t.Month(), 86 | t.Day(), 87 | t.Hour(), 88 | t.Minute(), 89 | t.Second(), 90 | pid) 91 | return name, program + "." + tag 92 | } 93 | 94 | var onceLogDirs sync.Once 95 | 96 | // create creates a new log file and returns the file and its filename, which 97 | // contains tag ("INFO", "FATAL", etc.) and t. If the file is created 98 | // successfully, create also attempts to update the symlink for that tag, ignoring 99 | // errors. 100 | // The startup argument indicates whether this is the initial startup of klog. 101 | // If startup is true, existing files are opened for appending instead of truncated. 102 | func create(tag string, t time.Time, startup bool) (f *os.File, filename string, err error) { 103 | if logging.logFile != "" { 104 | f, err := openOrCreate(logging.logFile, startup) 105 | if err == nil { 106 | return f, logging.logFile, nil 107 | } 108 | return nil, "", fmt.Errorf("log: unable to create log: %v", err) 109 | } 110 | onceLogDirs.Do(createLogDirs) 111 | if len(logDirs) == 0 { 112 | return nil, "", errors.New("log: no log dirs") 113 | } 114 | name, link := logName(tag, t) 115 | var lastErr error 116 | for _, dir := range logDirs { 117 | fname := filepath.Join(dir, name) 118 | f, err := openOrCreate(fname, startup) 119 | if err == nil { 120 | symlink := filepath.Join(dir, link) 121 | os.Remove(symlink) // ignore err 122 | os.Symlink(name, symlink) // ignore err 123 | return f, fname, nil 124 | } 125 | lastErr = err 126 | } 127 | return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr) 128 | } 129 | 130 | // The startup argument indicates whether this is the initial startup of klog. 131 | // If startup is true, existing files are opened for appending instead of truncated. 132 | func openOrCreate(name string, startup bool) (*os.File, error) { 133 | if startup { 134 | f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) 135 | return f, err 136 | } 137 | f, err := os.Create(name) 138 | return f, err 139 | } 140 | -------------------------------------------------------------------------------- /hotupgrade/vendor/k8s.io/klog/README.md: -------------------------------------------------------------------------------- 1 | klog 2 | ==== 3 | 4 | klog is a permanent fork of https://github.com/golang/glog. 5 | 6 | ## Why was klog created? 7 | 8 | The decision to create klog was one that wasn't made lightly, but it was necessary due to some 9 | drawbacks that are present in [glog](https://github.com/golang/glog). Ultimately, the fork was created due to glog not being under active development; this can be seen in the glog README: 10 | 11 | > The code in this repo [...] is not itself under development 12 | 13 | This makes us unable to solve many use cases without a fork. The factors that contributed to needing feature development are listed below: 14 | 15 | * `glog` [presents a lot "gotchas"](https://github.com/kubernetes/kubernetes/issues/61006) and introduces challenges in containerized environments, all of which aren't well documented. 16 | * `glog` doesn't provide an easy way to test logs, which detracts from the stability of software using it 17 | * A long term goal is to implement a logging interface that allows us to add context, change output format, etc. 18 | 19 | Historical context is available here: 20 | 21 | * https://github.com/kubernetes/kubernetes/issues/61006 22 | * https://github.com/kubernetes/kubernetes/issues/70264 23 | * https://groups.google.com/forum/#!msg/kubernetes-sig-architecture/wCWiWf3Juzs/hXRVBH90CgAJ 24 | * https://groups.google.com/forum/#!msg/kubernetes-dev/7vnijOMhLS0/1oRiNtigBgAJ 25 | 26 | ---- 27 | 28 | How to use klog 29 | =============== 30 | - Replace imports for `github.com/golang/glog` with `k8s.io/klog` 31 | - Use `klog.InitFlags(nil)` explicitly for initializing global flags as we no longer use `init()` method to register the flags 32 | - You can now use `log-file` instead of `log-dir` for logging to a single file (See `examples/log_file/usage_log_file.go`) 33 | - If you want to redirect everything logged using klog somewhere else (say syslog!), you can use `klog.SetOutput()` method and supply a `io.Writer`. (See `examples/set_output/usage_set_output.go`) 34 | - For more logging conventions (See [Logging Conventions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md)) 35 | 36 | ### Coexisting with glog 37 | This package can be used side by side with glog. [This example](examples/coexist_glog/coexist_glog.go) shows how to initialize and syncronize flags from the global `flag.CommandLine` FlagSet. In addition, the example makes use of stderr as combined output by setting `alsologtostderr` (or `logtostderr`) to `true`. 38 | 39 | ## Community, discussion, contribution, and support 40 | 41 | Learn how to engage with the Kubernetes community on the [community page](http://kubernetes.io/community/). 42 | 43 | You can reach the maintainers of this project at: 44 | 45 | - [Slack](https://kubernetes.slack.com/messages/sig-architecture) 46 | - [Mailing List](https://groups.google.com/forum/#!forum/kubernetes-sig-architecture) 47 | 48 | ### Code of conduct 49 | 50 | Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md). 51 | 52 | ---- 53 | 54 | glog 55 | ==== 56 | 57 | Leveled execution logs for Go. 58 | 59 | This is an efficient pure Go implementation of leveled logs in the 60 | manner of the open source C++ package 61 | https://github.com/google/glog 62 | 63 | By binding methods to booleans it is possible to use the log package 64 | without paying the expense of evaluating the arguments to the log. 65 | Through the -vmodule flag, the package also provides fine-grained 66 | control over logging at the file level. 67 | 68 | The comment from glog.go introduces the ideas: 69 | 70 | Package glog implements logging analogous to the Google-internal 71 | C++ INFO/ERROR/V setup. It provides functions Info, Warning, 72 | Error, Fatal, plus formatting variants such as Infof. It 73 | also provides V-style logging controlled by the -v and 74 | -vmodule=file=2 flags. 75 | 76 | Basic examples: 77 | 78 | glog.Info("Prepare to repel boarders") 79 | 80 | glog.Fatalf("Initialization failed: %s", err) 81 | 82 | See the documentation for the V function for an explanation 83 | of these examples: 84 | 85 | if glog.V(2) { 86 | glog.Info("Starting transaction...") 87 | } 88 | 89 | glog.V(2).Infoln("Processed", nItems, "elements") 90 | 91 | 92 | The repository contains an open source version of the log package 93 | used inside Google. The master copy of the source lives inside 94 | Google, not here. The code in this repo is for export only and is not itself 95 | under development. Feature requests will be ignored. 96 | 97 | Send bug reports to golang-nuts@googlegroups.com. 98 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/utils/iptables.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "errors" 19 | "fmt" 20 | 21 | "github.com/coreos/go-iptables/iptables" 22 | ) 23 | 24 | const statusChainExists = 1 25 | 26 | // EnsureChain idempotently creates the iptables chain. It does not 27 | // return an error if the chain already exists. 28 | func EnsureChain(ipt *iptables.IPTables, table, chain string) error { 29 | if ipt == nil { 30 | return errors.New("failed to ensure iptable chain: IPTables was nil") 31 | } 32 | exists, err := ChainExists(ipt, table, chain) 33 | if err != nil { 34 | return fmt.Errorf("failed to list iptables chains: %v", err) 35 | } 36 | if !exists { 37 | err = ipt.NewChain(table, chain) 38 | if err != nil { 39 | eerr, eok := err.(*iptables.Error) 40 | if eok && eerr.ExitStatus() != statusChainExists { 41 | return err 42 | } 43 | } 44 | } 45 | return nil 46 | } 47 | 48 | // ChainExists checks whether an iptables chain exists. 49 | func ChainExists(ipt *iptables.IPTables, table, chain string) (bool, error) { 50 | if ipt == nil { 51 | return false, errors.New("failed to check iptable chain: IPTables was nil") 52 | } 53 | chains, err := ipt.ListChains(table) 54 | if err != nil { 55 | return false, err 56 | } 57 | 58 | for _, ch := range chains { 59 | if ch == chain { 60 | return true, nil 61 | } 62 | } 63 | return false, nil 64 | } 65 | 66 | // DeleteRule idempotently delete the iptables rule in the specified table/chain. 67 | // It does not return an error if the referring chain doesn't exist 68 | func DeleteRule(ipt *iptables.IPTables, table, chain string, rulespec ...string) error { 69 | if ipt == nil { 70 | return errors.New("failed to ensure iptable chain: IPTables was nil") 71 | } 72 | if err := ipt.Delete(table, chain, rulespec...); err != nil { 73 | eerr, eok := err.(*iptables.Error) 74 | switch { 75 | case eok && eerr.IsNotExist(): 76 | // swallow here, the chain was already deleted 77 | return nil 78 | case eok && eerr.ExitStatus() == 2: 79 | // swallow here, invalid command line parameter because the referring rule is missing 80 | return nil 81 | default: 82 | return fmt.Errorf("Failed to delete referring rule %s %s: %v", table, chain, err) 83 | } 84 | } 85 | return nil 86 | } 87 | 88 | // DeleteChain idempotently deletes the specified table/chain. 89 | // It does not return an errors if the chain does not exist 90 | func DeleteChain(ipt *iptables.IPTables, table, chain string) error { 91 | if ipt == nil { 92 | return errors.New("failed to ensure iptable chain: IPTables was nil") 93 | } 94 | 95 | err := ipt.DeleteChain(table, chain) 96 | eerr, eok := err.(*iptables.Error) 97 | switch { 98 | case eok && eerr.IsNotExist(): 99 | // swallow here, the chain was already deleted 100 | return nil 101 | default: 102 | return err 103 | } 104 | } 105 | 106 | // ClearChain idempotently clear the iptables rules in the specified table/chain. 107 | // If the chain does not exist, a new one will be created 108 | func ClearChain(ipt *iptables.IPTables, table, chain string) error { 109 | if ipt == nil { 110 | return errors.New("failed to ensure iptable chain: IPTables was nil") 111 | } 112 | err := ipt.ClearChain(table, chain) 113 | eerr, eok := err.(*iptables.Error) 114 | switch { 115 | case eok && eerr.IsNotExist(): 116 | // swallow here, the chain was already deleted 117 | return EnsureChain(ipt, table, chain) 118 | default: 119 | return err 120 | } 121 | } 122 | 123 | // InsertUnique will add a rule to a chain if it does not already exist. 124 | // By default the rule is appended, unless prepend is true. 125 | func InsertUnique(ipt *iptables.IPTables, table, chain string, prepend bool, rule []string) error { 126 | exists, err := ipt.Exists(table, chain, rule...) 127 | if err != nil { 128 | return err 129 | } 130 | if exists { 131 | return nil 132 | } 133 | 134 | if prepend { 135 | return ipt.Insert(table, chain, 1, rule...) 136 | } else { 137 | return ipt.Append(table, chain, rule...) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/backend/allocator/range.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package allocator 16 | 17 | import ( 18 | "fmt" 19 | "net" 20 | 21 | "github.com/containernetworking/cni/pkg/types" 22 | "github.com/containernetworking/plugins/pkg/ip" 23 | ) 24 | 25 | // Canonicalize takes a given range and ensures that all information is consistent, 26 | // filling out Start, End, and Gateway with sane values if missing 27 | func (r *Range) Canonicalize() error { 28 | if err := canonicalizeIP(&r.Subnet.IP); err != nil { 29 | return err 30 | } 31 | 32 | // Can't create an allocator for a network with no addresses, eg 33 | // a /32 or /31 34 | ones, masklen := r.Subnet.Mask.Size() 35 | if ones > masklen-2 { 36 | return fmt.Errorf("Network %s too small to allocate from", (*net.IPNet)(&r.Subnet).String()) 37 | } 38 | 39 | if len(r.Subnet.IP) != len(r.Subnet.Mask) { 40 | return fmt.Errorf("IPNet IP and Mask version mismatch") 41 | } 42 | 43 | // Ensure Subnet IP is the network address, not some other address 44 | networkIP := r.Subnet.IP.Mask(r.Subnet.Mask) 45 | if !r.Subnet.IP.Equal(networkIP) { 46 | return fmt.Errorf("Network has host bits set. For a subnet mask of length %d the network address is %s", ones, networkIP.String()) 47 | } 48 | 49 | // If the gateway is nil, claim .1 50 | if r.Gateway == nil { 51 | r.Gateway = ip.NextIP(r.Subnet.IP) 52 | } else { 53 | if err := canonicalizeIP(&r.Gateway); err != nil { 54 | return err 55 | } 56 | } 57 | 58 | // RangeStart: If specified, make sure it's sane (inside the subnet), 59 | // otherwise use the first free IP (i.e. .1) - this will conflict with the 60 | // gateway but we skip it in the iterator 61 | if r.RangeStart != nil { 62 | if err := canonicalizeIP(&r.RangeStart); err != nil { 63 | return err 64 | } 65 | 66 | if !r.Contains(r.RangeStart) { 67 | return fmt.Errorf("RangeStart %s not in network %s", r.RangeStart.String(), (*net.IPNet)(&r.Subnet).String()) 68 | } 69 | } else { 70 | r.RangeStart = ip.NextIP(r.Subnet.IP) 71 | } 72 | 73 | // RangeEnd: If specified, verify sanity. Otherwise, add a sensible default 74 | // (e.g. for a /24: .254 if IPv4, ::255 if IPv6) 75 | if r.RangeEnd != nil { 76 | if err := canonicalizeIP(&r.RangeEnd); err != nil { 77 | return err 78 | } 79 | 80 | if !r.Contains(r.RangeEnd) { 81 | return fmt.Errorf("RangeEnd %s not in network %s", r.RangeEnd.String(), (*net.IPNet)(&r.Subnet).String()) 82 | } 83 | } else { 84 | r.RangeEnd = lastIP(r.Subnet) 85 | } 86 | 87 | return nil 88 | } 89 | 90 | // IsValidIP checks if a given ip is a valid, allocatable address in a given Range 91 | func (r *Range) Contains(addr net.IP) bool { 92 | if err := canonicalizeIP(&addr); err != nil { 93 | return false 94 | } 95 | 96 | subnet := (net.IPNet)(r.Subnet) 97 | 98 | // Not the same address family 99 | if len(addr) != len(r.Subnet.IP) { 100 | return false 101 | } 102 | 103 | // Not in network 104 | if !subnet.Contains(addr) { 105 | return false 106 | } 107 | 108 | // We ignore nils here so we can use this function as we initialize the range. 109 | if r.RangeStart != nil { 110 | // Before the range start 111 | if ip.Cmp(addr, r.RangeStart) < 0 { 112 | return false 113 | } 114 | } 115 | 116 | if r.RangeEnd != nil { 117 | if ip.Cmp(addr, r.RangeEnd) > 0 { 118 | // After the range end 119 | return false 120 | } 121 | } 122 | 123 | return true 124 | } 125 | 126 | // Overlaps returns true if there is any overlap between ranges 127 | func (r *Range) Overlaps(r1 *Range) bool { 128 | // different familes 129 | if len(r.RangeStart) != len(r1.RangeStart) { 130 | return false 131 | } 132 | 133 | return r.Contains(r1.RangeStart) || 134 | r.Contains(r1.RangeEnd) || 135 | r1.Contains(r.RangeStart) || 136 | r1.Contains(r.RangeEnd) 137 | } 138 | 139 | func (r *Range) String() string { 140 | return fmt.Sprintf("%s-%s", r.RangeStart.String(), r.RangeEnd.String()) 141 | } 142 | 143 | // canonicalizeIP makes sure a provided ip is in standard form 144 | func canonicalizeIP(ip *net.IP) error { 145 | if ip.To4() != nil { 146 | *ip = ip.To4() 147 | return nil 148 | } else if ip.To16() != nil { 149 | *ip = ip.To16() 150 | return nil 151 | } 152 | return fmt.Errorf("IP %s not v4 nor v6", *ip) 153 | } 154 | 155 | // Determine the last IP of a subnet, excluding the broadcast if IPv4 156 | func lastIP(subnet types.IPNet) net.IP { 157 | var end net.IP 158 | for i := 0; i < len(subnet.IP); i++ { 159 | end = append(end, subnet.IP[i]|^subnet.Mask[i]) 160 | } 161 | if subnet.IP.To4() != nil { 162 | end[3]-- 163 | } 164 | 165 | return end 166 | } 167 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/backend/allocator/config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package allocator 16 | 17 | import ( 18 | "encoding/json" 19 | "fmt" 20 | "net" 21 | 22 | "github.com/containernetworking/cni/pkg/types" 23 | "github.com/containernetworking/cni/pkg/version" 24 | 25 | "github.com/containernetworking/plugins/pkg/ip" 26 | ) 27 | 28 | // The top-level network config - IPAM plugins are passed the full configuration 29 | // of the calling plugin, not just the IPAM section. 30 | type Net struct { 31 | Name string `json:"name"` 32 | CNIVersion string `json:"cniVersion"` 33 | IPAM *IPAMConfig `json:"ipam"` 34 | RuntimeConfig struct { 35 | // The capability arg 36 | IPRanges []RangeSet `json:"ipRanges,omitempty"` 37 | IPs []*ip.IP `json:"ips,omitempty"` 38 | Annotations map[string]string `json:"io.kubernetes.cri.pod-annotations,omitempty"` 39 | } `json:"runtimeConfig,omitempty"` 40 | Args *struct { 41 | A *IPAMArgs `json:"cni"` 42 | } `json:"args"` 43 | } 44 | 45 | // IPAMConfig represents the IP related network configuration. 46 | // This nests Range because we initially only supported a single 47 | // range directly, and wish to preserve backwards compatability 48 | type IPAMConfig struct { 49 | *Range 50 | Name string 51 | Type string `json:"type"` 52 | Routes []*types.Route `json:"routes"` 53 | DataDir string `json:"dataDir"` 54 | ResolvConf string `json:"resolvConf"` 55 | Ranges []RangeSet `json:"ranges"` 56 | IPArgs []net.IP `json:"-"` // Requested IPs from CNI_ARGS, args and capabilities 57 | // reserved ip 58 | PodAnnotations map[string]string `json:"annotations,omitempty"` 59 | } 60 | 61 | type IPAMEnvArgs struct { 62 | types.CommonArgs 63 | IP ip.IP `json:"ip,omitempty"` 64 | } 65 | 66 | type IPAMArgs struct { 67 | IPs []*ip.IP `json:"ips"` 68 | } 69 | 70 | type RangeSet []Range 71 | 72 | type Range struct { 73 | RangeStart net.IP `json:"rangeStart,omitempty"` // The first ip, inclusive 74 | RangeEnd net.IP `json:"rangeEnd,omitempty"` // The last ip, inclusive 75 | Subnet types.IPNet `json:"subnet"` 76 | Gateway net.IP `json:"gateway,omitempty"` 77 | } 78 | 79 | // NewIPAMConfig creates a NetworkConfig from the given network name. 80 | func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) { 81 | n := Net{} 82 | if err := json.Unmarshal(bytes, &n); err != nil { 83 | return nil, "", err 84 | } 85 | 86 | if n.IPAM == nil { 87 | return nil, "", fmt.Errorf("IPAM config missing 'ipam' key") 88 | } 89 | n.IPAM.PodAnnotations = n.RuntimeConfig.Annotations 90 | // parse custom IP from env args 91 | if envArgs != "" { 92 | e := IPAMEnvArgs{} 93 | err := types.LoadArgs(envArgs, &e) 94 | if err != nil { 95 | return nil, "", err 96 | } 97 | 98 | if e.IP.ToIP() != nil { 99 | n.IPAM.IPArgs = []net.IP{e.IP.ToIP()} 100 | } 101 | } 102 | 103 | // parse custom IPs from CNI args in network config 104 | if n.Args != nil && n.Args.A != nil && len(n.Args.A.IPs) != 0 { 105 | for _, i := range n.Args.A.IPs { 106 | n.IPAM.IPArgs = append(n.IPAM.IPArgs, i.ToIP()) 107 | } 108 | } 109 | 110 | // parse custom IPs from runtime configuration 111 | if len(n.RuntimeConfig.IPs) > 0 { 112 | for _, i := range n.RuntimeConfig.IPs { 113 | n.IPAM.IPArgs = append(n.IPAM.IPArgs, i.ToIP()) 114 | } 115 | } 116 | 117 | for idx := range n.IPAM.IPArgs { 118 | if err := canonicalizeIP(&n.IPAM.IPArgs[idx]); err != nil { 119 | return nil, "", fmt.Errorf("cannot understand ip: %v", err) 120 | } 121 | } 122 | 123 | // If a single range (old-style config) is specified, prepend it to 124 | // the Ranges array 125 | if n.IPAM.Range != nil && n.IPAM.Range.Subnet.IP != nil { 126 | n.IPAM.Ranges = append([]RangeSet{{*n.IPAM.Range}}, n.IPAM.Ranges...) 127 | } 128 | n.IPAM.Range = nil 129 | 130 | // If a range is supplied as a runtime config, prepend it to the Ranges 131 | if len(n.RuntimeConfig.IPRanges) > 0 { 132 | n.IPAM.Ranges = append(n.RuntimeConfig.IPRanges, n.IPAM.Ranges...) 133 | } 134 | 135 | if len(n.IPAM.Ranges) == 0 { 136 | return nil, "", fmt.Errorf("no IP ranges specified") 137 | } 138 | 139 | // Validate all ranges 140 | numV4 := 0 141 | numV6 := 0 142 | for i := range n.IPAM.Ranges { 143 | if err := n.IPAM.Ranges[i].Canonicalize(); err != nil { 144 | return nil, "", fmt.Errorf("invalid range set %d: %s", i, err) 145 | } 146 | 147 | if n.IPAM.Ranges[i][0].RangeStart.To4() != nil { 148 | numV4++ 149 | } else { 150 | numV6++ 151 | } 152 | } 153 | 154 | // CNI spec 0.2.0 and below supported only one v4 and v6 address 155 | if numV4 > 1 || numV6 > 1 { 156 | if ok, _ := version.GreaterThanOrEqualTo(n.CNIVersion, "0.3.0"); !ok { 157 | return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion) 158 | } 159 | } 160 | 161 | // Check for overlaps 162 | l := len(n.IPAM.Ranges) 163 | for i, p1 := range n.IPAM.Ranges[:l-1] { 164 | for j, p2 := range n.IPAM.Ranges[i+1:] { 165 | if p1.Overlaps(&p2) { 166 | return nil, "", fmt.Errorf("range set %d overlaps with %d", i, (i + j + 1)) 167 | } 168 | } 169 | } 170 | 171 | // Copy net name into IPAM so not to drag Net struct around 172 | n.IPAM.Name = n.Name 173 | 174 | return n.IPAM, n.CNIVersion, nil 175 | } 176 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/reserved.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "encoding/json" 21 | "io/ioutil" 22 | "os" 23 | "path" 24 | "path/filepath" 25 | "strconv" 26 | "strings" 27 | "time" 28 | 29 | "github.com/containernetworking/cni/pkg/skel" 30 | current "github.com/containernetworking/cni/pkg/types/100" 31 | "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator" 32 | "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/disk" 33 | ) 34 | 35 | const ( 36 | PodReservedIpAnnotationKey = "io.kubernetes.cri/reserved-ip-duration" 37 | ) 38 | 39 | var defaultDataDir = "/var/lib/cni/networks" 40 | 41 | type ReservedInfo struct { 42 | ContainerId string `json:"containerId"` 43 | IfName string `json:"ifName"` 44 | IPConf *current.IPConfig `json:"IPConf"` 45 | //reserved time duration 46 | Duration time.Duration `json:"duration"` 47 | ReleaseTime *time.Time `json:"releaseTime,omitempty"` 48 | } 49 | 50 | func isReservedIp(conf *allocator.IPAMConfig) (bool, time.Duration) { 51 | val,ok := conf.PodAnnotations[PodReservedIpAnnotationKey] 52 | if !ok { 53 | return false, 0 54 | } 55 | duration,err := strconv.Atoi(val) 56 | if err!=nil { 57 | return false, 0 58 | } 59 | return true, time.Duration(duration) 60 | } 61 | 62 | func getReservedIpFile(args,dir string) string { 63 | // specify Ip 64 | var ns, name string 65 | items := strings.Split(args, ";") 66 | for _,item := range items { 67 | if strings.Contains(item, "K8S_POD_NAME=") { 68 | kv := strings.Split(item, "=") 69 | name = kv[1] 70 | } 71 | if strings.Contains(item, "K8S_POD_NAMESPACE=") { 72 | kv := strings.Split(item, "=") 73 | ns = kv[1] 74 | } 75 | } 76 | if dir == "" { 77 | dir = defaultDataDir 78 | } 79 | return path.Join(dir, "reserved", ns, name) 80 | } 81 | 82 | func getReservedIp(args, dir string) (*current.IPConfig, error) { 83 | file := getReservedIpFile(args, dir) 84 | by, err := ioutil.ReadFile(file) 85 | if err != nil { 86 | if os.IsNotExist(err) { 87 | return nil, nil 88 | } 89 | return nil, err 90 | } 91 | var reserved *ReservedInfo 92 | err = json.Unmarshal(by, &reserved) 93 | if err!=nil { 94 | return nil, err 95 | } 96 | return reserved.IPConf, nil 97 | } 98 | 99 | func allocateReservedIp(args *skel.CmdArgs, ipConf *current.IPConfig) error { 100 | ipamConf, _, err := allocator.LoadIPAMConfig(args.StdinData, args.Args) 101 | if err != nil { 102 | return err 103 | } 104 | _,duration := isReservedIp(ipamConf) 105 | file := getReservedIpFile(args.Args, ipamConf.DataDir) 106 | // mkdir file 107 | err = os.MkdirAll(path.Dir(file), os.ModeDir) 108 | if err != nil { 109 | return err 110 | } 111 | reservedInfo := ReservedInfo{ 112 | ContainerId: args.ContainerID, 113 | IfName: args.IfName, 114 | IPConf: ipConf, 115 | Duration: duration, 116 | ReleaseTime: nil, 117 | } 118 | by,_ := json.Marshal(reservedInfo) 119 | ioutil.WriteFile(file, by, 0644) 120 | return err 121 | } 122 | 123 | func releaseReservedIp(args, dir string) error { 124 | file := getReservedIpFile(args, dir) 125 | by, err := ioutil.ReadFile(file) 126 | if err != nil { 127 | if os.IsNotExist(err) { 128 | return nil 129 | } 130 | return err 131 | } 132 | var reserved *ReservedInfo 133 | err = json.Unmarshal(by, &reserved) 134 | if err!=nil { 135 | return nil 136 | } 137 | now := time.Now() 138 | reserved.ReleaseTime = &now 139 | by,_ = json.Marshal(reserved) 140 | return ioutil.WriteFile(file, by, 0644) 141 | } 142 | 143 | func clearExpiredReservedIp(conf *allocator.IPAMConfig){ 144 | dir := conf.DataDir 145 | if dir == "" { 146 | dir = defaultDataDir 147 | } 148 | // mkdir file 149 | err := os.MkdirAll(path.Join(dir, "reserved"), os.ModeDir) 150 | if err != nil { 151 | return 152 | } 153 | files,err := walkDir(path.Join(dir, "reserved")) 154 | if err!=nil { 155 | return 156 | } 157 | 158 | for _,file :=range files { 159 | by, err := ioutil.ReadFile(file) 160 | if err != nil { 161 | continue 162 | } 163 | var reserved *ReservedInfo 164 | err = json.Unmarshal(by, &reserved) 165 | if err!=nil { 166 | continue 167 | } 168 | if reserved.ReleaseTime == nil { 169 | continue 170 | } 171 | expiredTime := reserved.ReleaseTime.Add(time.Minute * reserved.Duration) 172 | if time.Now().After(expiredTime) { 173 | err = releaseIpInStore(reserved.ContainerId, reserved.IfName, conf) 174 | if err!=nil { 175 | continue 176 | } 177 | os.Remove(file) 178 | } 179 | } 180 | } 181 | 182 | func walkDir(dirPth string) (files []string, err error) { 183 | files = make([]string, 0, 30) 184 | err = filepath.Walk(dirPth, func(filename string, fi os.FileInfo, err error) error { //遍历目录 185 | if fi.IsDir() { // 忽略目录 186 | return nil 187 | } 188 | files = append(files, filename) 189 | return nil 190 | }) 191 | 192 | return files, err 193 | } 194 | 195 | func releaseIpInStore(containerId, ifName string, conf *allocator.IPAMConfig) error { 196 | store, err := disk.New(conf.Name, conf.DataDir) 197 | if err != nil { 198 | return err 199 | } 200 | defer store.Close() 201 | 202 | // Loop through all ranges, releasing all IPs, even if an error occurs 203 | for idx, rangeset := range conf.Ranges { 204 | ipAllocator := allocator.NewIPAllocator(&rangeset, store, idx) 205 | err = ipAllocator.Release(containerId, ifName) 206 | if err != nil { 207 | return err 208 | } 209 | } 210 | return nil 211 | } 212 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "fmt" 19 | "net" 20 | "strings" 21 | 22 | bv "github.com/containernetworking/plugins/pkg/utils/buildversion" 23 | "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator" 24 | "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/disk" 25 | 26 | "github.com/containernetworking/cni/pkg/skel" 27 | "github.com/containernetworking/cni/pkg/types" 28 | current "github.com/containernetworking/cni/pkg/types/100" 29 | "github.com/containernetworking/cni/pkg/version" 30 | ) 31 | 32 | func main() { 33 | skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("host-local")) 34 | } 35 | 36 | func cmdCheck(args *skel.CmdArgs) error { 37 | 38 | ipamConf, _, err := allocator.LoadIPAMConfig(args.StdinData, args.Args) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | // Look to see if there is at least one IP address allocated to the container 44 | // in the data dir, irrespective of what that address actually is 45 | store, err := disk.New(ipamConf.Name, ipamConf.DataDir) 46 | if err != nil { 47 | return err 48 | } 49 | defer store.Close() 50 | 51 | containerIpFound := store.FindByID(args.ContainerID, args.IfName) 52 | if containerIpFound == false { 53 | return fmt.Errorf("host-local: Failed to find address added by container %v", args.ContainerID) 54 | } 55 | 56 | return nil 57 | } 58 | 59 | func cmdAdd(args *skel.CmdArgs) error { 60 | ipamConf, confVersion, err := allocator.LoadIPAMConfig(args.StdinData, args.Args) 61 | if err != nil { 62 | return err 63 | } 64 | 65 | result := ¤t.Result{CNIVersion: current.ImplementedSpecVersion} 66 | 67 | if ipamConf.ResolvConf != "" { 68 | dns, err := parseResolvConf(ipamConf.ResolvConf) 69 | if err != nil { 70 | return err 71 | } 72 | result.DNS = *dns 73 | } 74 | 75 | store, err := disk.New(ipamConf.Name, ipamConf.DataDir) 76 | if err != nil { 77 | return err 78 | } 79 | defer store.Close() 80 | 81 | // Keep the allocators we used, so we can release all IPs if an error 82 | // occurs after we start allocating 83 | allocs := []*allocator.IPAllocator{} 84 | 85 | // Store all requested IPs in a map, so we can easily remove ones we use 86 | // and error if some remain 87 | requestedIPs := map[string]net.IP{} //net.IP cannot be a key 88 | 89 | for _, ip := range ipamConf.IPArgs { 90 | requestedIPs[ip.String()] = ip 91 | } 92 | 93 | var reservedIp *current.IPConfig 94 | // clear expired reserved ip 95 | clearExpiredReservedIp(ipamConf) 96 | // determine if IP reservation is required 97 | isReserved,_ := isReservedIp(ipamConf) 98 | // reserved ip 99 | if isReserved { 100 | reservedIp,err = getReservedIp(args.Args, ipamConf.DataDir) 101 | if err != nil { 102 | return err 103 | }else if reservedIp != nil { 104 | result.IPs = append(result.IPs, reservedIp) 105 | result.Routes = ipamConf.Routes 106 | allocateReservedIp(args, reservedIp) 107 | return types.PrintResult(result, confVersion) 108 | } 109 | } 110 | 111 | for idx, rangeset := range ipamConf.Ranges { 112 | allocator := allocator.NewIPAllocator(&rangeset, store, idx) 113 | 114 | // Check to see if there are any custom IPs requested in this range. 115 | var requestedIP net.IP 116 | for k, ip := range requestedIPs { 117 | if rangeset.Contains(ip) { 118 | requestedIP = ip 119 | delete(requestedIPs, k) 120 | break 121 | } 122 | } 123 | 124 | ipConf, err := allocator.Get(args.ContainerID, args.IfName, requestedIP) 125 | if err != nil { 126 | // Deallocate all already allocated IPs 127 | for _, alloc := range allocs { 128 | _ = alloc.Release(args.ContainerID, args.IfName) 129 | } 130 | return fmt.Errorf("failed to allocate for range %d: %v", idx, err) 131 | } 132 | if isReserved { 133 | allocateReservedIp(args, ipConf) 134 | } 135 | allocs = append(allocs, allocator) 136 | result.IPs = append(result.IPs, ipConf) 137 | } 138 | 139 | // If an IP was requested that wasn't fulfilled, fail 140 | if len(requestedIPs) != 0 { 141 | for _, alloc := range allocs { 142 | _ = alloc.Release(args.ContainerID, args.IfName) 143 | } 144 | errstr := "failed to allocate all requested IPs:" 145 | for _, ip := range requestedIPs { 146 | errstr = errstr + " " + ip.String() 147 | } 148 | return fmt.Errorf(errstr) 149 | } 150 | result.Routes = ipamConf.Routes 151 | return types.PrintResult(result, confVersion) 152 | } 153 | 154 | func cmdDel(args *skel.CmdArgs) error { 155 | ipamConf, _, err := allocator.LoadIPAMConfig(args.StdinData, args.Args) 156 | if err != nil { 157 | return err 158 | } 159 | 160 | // reserved ip 161 | if isReserved,_ := isReservedIp(ipamConf); isReserved { 162 | return releaseReservedIp(args.Args, ipamConf.DataDir) 163 | } 164 | 165 | store, err := disk.New(ipamConf.Name, ipamConf.DataDir) 166 | if err != nil { 167 | return err 168 | } 169 | defer store.Close() 170 | 171 | // Loop through all ranges, releasing all IPs, even if an error occurs 172 | var errors []string 173 | for idx, rangeset := range ipamConf.Ranges { 174 | ipAllocator := allocator.NewIPAllocator(&rangeset, store, idx) 175 | 176 | err := ipAllocator.Release(args.ContainerID, args.IfName) 177 | if err != nil { 178 | errors = append(errors, err.Error()) 179 | } 180 | } 181 | 182 | if errors != nil { 183 | return fmt.Errorf(strings.Join(errors, ";")) 184 | } 185 | return nil 186 | } 187 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/testutils/netns_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package testutils 16 | 17 | import ( 18 | "crypto/rand" 19 | "fmt" 20 | "os" 21 | "path" 22 | "runtime" 23 | "strings" 24 | "sync" 25 | "syscall" 26 | 27 | "github.com/containernetworking/plugins/pkg/ns" 28 | "golang.org/x/sys/unix" 29 | ) 30 | 31 | func getNsRunDir() string { 32 | xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR") 33 | 34 | /// If XDG_RUNTIME_DIR is set, check if the current user owns /var/run. If 35 | // the owner is different, we are most likely running in a user namespace. 36 | // In that case use $XDG_RUNTIME_DIR/netns as runtime dir. 37 | if xdgRuntimeDir != "" { 38 | if s, err := os.Stat("/var/run"); err == nil { 39 | st, ok := s.Sys().(*syscall.Stat_t) 40 | if ok && int(st.Uid) != os.Geteuid() { 41 | return path.Join(xdgRuntimeDir, "netns") 42 | } 43 | } 44 | } 45 | 46 | return "/var/run/netns" 47 | } 48 | 49 | // Creates a new persistent (bind-mounted) network namespace and returns an object 50 | // representing that namespace, without switching to it. 51 | func NewNS() (ns.NetNS, error) { 52 | 53 | nsRunDir := getNsRunDir() 54 | 55 | b := make([]byte, 16) 56 | _, err := rand.Read(b) 57 | if err != nil { 58 | return nil, fmt.Errorf("failed to generate random netns name: %v", err) 59 | } 60 | 61 | // Create the directory for mounting network namespaces 62 | // This needs to be a shared mountpoint in case it is mounted in to 63 | // other namespaces (containers) 64 | err = os.MkdirAll(nsRunDir, 0755) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | // Remount the namespace directory shared. This will fail if it is not 70 | // already a mountpoint, so bind-mount it on to itself to "upgrade" it 71 | // to a mountpoint. 72 | err = unix.Mount("", nsRunDir, "none", unix.MS_SHARED|unix.MS_REC, "") 73 | if err != nil { 74 | if err != unix.EINVAL { 75 | return nil, fmt.Errorf("mount --make-rshared %s failed: %q", nsRunDir, err) 76 | } 77 | 78 | // Recursively remount /var/run/netns on itself. The recursive flag is 79 | // so that any existing netns bindmounts are carried over. 80 | err = unix.Mount(nsRunDir, nsRunDir, "none", unix.MS_BIND|unix.MS_REC, "") 81 | if err != nil { 82 | return nil, fmt.Errorf("mount --rbind %s %s failed: %q", nsRunDir, nsRunDir, err) 83 | } 84 | 85 | // Now we can make it shared 86 | err = unix.Mount("", nsRunDir, "none", unix.MS_SHARED|unix.MS_REC, "") 87 | if err != nil { 88 | return nil, fmt.Errorf("mount --make-rshared %s failed: %q", nsRunDir, err) 89 | } 90 | 91 | } 92 | 93 | nsName := fmt.Sprintf("cnitest-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) 94 | 95 | // create an empty file at the mount point 96 | nsPath := path.Join(nsRunDir, nsName) 97 | mountPointFd, err := os.Create(nsPath) 98 | if err != nil { 99 | return nil, err 100 | } 101 | mountPointFd.Close() 102 | 103 | // Ensure the mount point is cleaned up on errors; if the namespace 104 | // was successfully mounted this will have no effect because the file 105 | // is in-use 106 | defer os.RemoveAll(nsPath) 107 | 108 | var wg sync.WaitGroup 109 | wg.Add(1) 110 | 111 | // do namespace work in a dedicated goroutine, so that we can safely 112 | // Lock/Unlock OSThread without upsetting the lock/unlock state of 113 | // the caller of this function 114 | go (func() { 115 | defer wg.Done() 116 | runtime.LockOSThread() 117 | // Don't unlock. By not unlocking, golang will kill the OS thread when the 118 | // goroutine is done (for go1.10+) 119 | 120 | var origNS ns.NetNS 121 | origNS, err = ns.GetNS(getCurrentThreadNetNSPath()) 122 | if err != nil { 123 | return 124 | } 125 | defer origNS.Close() 126 | 127 | // create a new netns on the current thread 128 | err = unix.Unshare(unix.CLONE_NEWNET) 129 | if err != nil { 130 | return 131 | } 132 | 133 | // Put this thread back to the orig ns, since it might get reused (pre go1.10) 134 | defer origNS.Set() 135 | 136 | // bind mount the netns from the current thread (from /proc) onto the 137 | // mount point. This causes the namespace to persist, even when there 138 | // are no threads in the ns. 139 | err = unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", unix.MS_BIND, "") 140 | if err != nil { 141 | err = fmt.Errorf("failed to bind mount ns at %s: %v", nsPath, err) 142 | } 143 | })() 144 | wg.Wait() 145 | 146 | if err != nil { 147 | return nil, fmt.Errorf("failed to create namespace: %v", err) 148 | } 149 | 150 | return ns.GetNS(nsPath) 151 | } 152 | 153 | // UnmountNS unmounts the NS held by the netns object 154 | func UnmountNS(ns ns.NetNS) error { 155 | nsPath := ns.Path() 156 | // Only unmount if it's been bind-mounted (don't touch namespaces in /proc...) 157 | if strings.HasPrefix(nsPath, getNsRunDir()) { 158 | if err := unix.Unmount(nsPath, 0); err != nil { 159 | return fmt.Errorf("failed to unmount NS: at %s: %v", nsPath, err) 160 | } 161 | 162 | if err := os.Remove(nsPath); err != nil { 163 | return fmt.Errorf("failed to remove ns path %s: %v", nsPath, err) 164 | } 165 | } 166 | 167 | return nil 168 | } 169 | 170 | // getCurrentThreadNetNSPath copied from pkg/ns 171 | func getCurrentThreadNetNSPath() string { 172 | // /proc/self/ns/net returns the namespace of the main thread, not 173 | // of whatever thread this goroutine is running on. Make sure we 174 | // use the thread's net namespace since the thread is switching around 175 | return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid()) 176 | } 177 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/backend/disk/backend.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package disk 16 | 17 | import ( 18 | "io/ioutil" 19 | "net" 20 | "os" 21 | "path/filepath" 22 | "runtime" 23 | "strings" 24 | 25 | "github.com/containernetworking/plugins/plugins/ipam/host-local/backend" 26 | ) 27 | 28 | const lastIPFilePrefix = "last_reserved_ip." 29 | const LineBreak = "\r\n" 30 | 31 | var defaultDataDir = "/var/lib/cni/networks" 32 | 33 | // Store is a simple disk-backed store that creates one file per IP 34 | // address in a given directory. The contents of the file are the container ID. 35 | type Store struct { 36 | *FileLock 37 | dataDir string 38 | } 39 | 40 | // Store implements the Store interface 41 | var _ backend.Store = &Store{} 42 | 43 | func New(network, dataDir string) (*Store, error) { 44 | if dataDir == "" { 45 | dataDir = defaultDataDir 46 | } 47 | dir := filepath.Join(dataDir, network) 48 | if err := os.MkdirAll(dir, 0755); err != nil { 49 | return nil, err 50 | } 51 | 52 | lk, err := NewFileLock(dir) 53 | if err != nil { 54 | return nil, err 55 | } 56 | return &Store{lk, dir}, nil 57 | } 58 | 59 | func (s *Store) Reserve(id string, ifname string, ip net.IP, rangeID string) (bool, error) { 60 | fname := GetEscapedPath(s.dataDir, ip.String()) 61 | 62 | f, err := os.OpenFile(fname, os.O_RDWR|os.O_EXCL|os.O_CREATE, 0644) 63 | if os.IsExist(err) { 64 | return false, nil 65 | } 66 | if err != nil { 67 | return false, err 68 | } 69 | if _, err := f.WriteString(strings.TrimSpace(id) + LineBreak + ifname); err != nil { 70 | f.Close() 71 | os.Remove(f.Name()) 72 | return false, err 73 | } 74 | if err := f.Close(); err != nil { 75 | os.Remove(f.Name()) 76 | return false, err 77 | } 78 | // store the reserved ip in lastIPFile 79 | ipfile := GetEscapedPath(s.dataDir, lastIPFilePrefix+rangeID) 80 | err = ioutil.WriteFile(ipfile, []byte(ip.String()), 0644) 81 | if err != nil { 82 | return false, err 83 | } 84 | return true, nil 85 | } 86 | 87 | // LastReservedIP returns the last reserved IP if exists 88 | func (s *Store) LastReservedIP(rangeID string) (net.IP, error) { 89 | ipfile := GetEscapedPath(s.dataDir, lastIPFilePrefix+rangeID) 90 | data, err := ioutil.ReadFile(ipfile) 91 | if err != nil { 92 | return nil, err 93 | } 94 | return net.ParseIP(string(data)), nil 95 | } 96 | 97 | func (s *Store) Release(ip net.IP) error { 98 | return os.Remove(GetEscapedPath(s.dataDir, ip.String())) 99 | } 100 | 101 | func (s *Store) FindByKey(id string, ifname string, match string) (bool, error) { 102 | found := false 103 | 104 | err := filepath.Walk(s.dataDir, func(path string, info os.FileInfo, err error) error { 105 | if err != nil || info.IsDir() { 106 | return nil 107 | } 108 | data, err := ioutil.ReadFile(path) 109 | if err != nil { 110 | return nil 111 | } 112 | if strings.TrimSpace(string(data)) == match { 113 | found = true 114 | } 115 | return nil 116 | }) 117 | return found, err 118 | 119 | } 120 | 121 | func (s *Store) FindByID(id string, ifname string) bool { 122 | s.Lock() 123 | defer s.Unlock() 124 | 125 | found := false 126 | match := strings.TrimSpace(id) + LineBreak + ifname 127 | found, err := s.FindByKey(id, ifname, match) 128 | 129 | // Match anything created by this id 130 | if !found && err == nil { 131 | match := strings.TrimSpace(id) 132 | found, err = s.FindByKey(id, ifname, match) 133 | } 134 | 135 | return found 136 | } 137 | 138 | func (s *Store) ReleaseByKey(id string, ifname string, match string) (bool, error) { 139 | found := false 140 | err := filepath.Walk(s.dataDir, func(path string, info os.FileInfo, err error) error { 141 | if err != nil || info.IsDir() { 142 | return nil 143 | } 144 | data, err := ioutil.ReadFile(path) 145 | if err != nil { 146 | return nil 147 | } 148 | if strings.TrimSpace(string(data)) == match { 149 | if err := os.Remove(path); err != nil { 150 | return nil 151 | } 152 | found = true 153 | } 154 | return nil 155 | }) 156 | return found, err 157 | 158 | } 159 | 160 | // N.B. This function eats errors to be tolerant and 161 | // release as much as possible 162 | func (s *Store) ReleaseByID(id string, ifname string) error { 163 | found := false 164 | match := strings.TrimSpace(id) + LineBreak + ifname 165 | found, err := s.ReleaseByKey(id, ifname, match) 166 | 167 | // For backwards compatibility, look for files written by a previous version 168 | if !found && err == nil { 169 | match := strings.TrimSpace(id) 170 | found, err = s.ReleaseByKey(id, ifname, match) 171 | } 172 | return err 173 | } 174 | 175 | // GetByID returns the IPs which have been allocated to the specific ID 176 | func (s *Store) GetByID(id string, ifname string) []net.IP { 177 | var ips []net.IP 178 | 179 | match := strings.TrimSpace(id) + LineBreak + ifname 180 | // matchOld for backwards compatibility 181 | matchOld := strings.TrimSpace(id) 182 | 183 | // walk through all ips in this network to get the ones which belong to a specific ID 184 | _ = filepath.Walk(s.dataDir, func(path string, info os.FileInfo, err error) error { 185 | if err != nil || info.IsDir() { 186 | return nil 187 | } 188 | data, err := ioutil.ReadFile(path) 189 | if err != nil { 190 | return nil 191 | } 192 | if strings.TrimSpace(string(data)) == match || strings.TrimSpace(string(data)) == matchOld { 193 | _, ipString := filepath.Split(path) 194 | if ip := net.ParseIP(ipString); ip != nil { 195 | ips = append(ips, ip) 196 | } 197 | } 198 | return nil 199 | }) 200 | 201 | return ips 202 | } 203 | 204 | func GetEscapedPath(dataDir string, fname string) string { 205 | if runtime.GOOS == "windows" { 206 | fname = strings.Replace(fname, ":", "_", -1) 207 | } 208 | return filepath.Join(dataDir, fname) 209 | } 210 | -------------------------------------------------------------------------------- /containernetworking/plugins/pkg/utils/utils_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "fmt" 19 | "strings" 20 | 21 | . "github.com/onsi/ginkgo" 22 | . "github.com/onsi/gomega" 23 | ) 24 | 25 | var _ = Describe("Utils", func() { 26 | Describe("FormatChainName", func() { 27 | It("must format a short name", func() { 28 | chain := FormatChainName("test", "1234") 29 | Expect(len(chain)).To(Equal(maxChainLength)) 30 | Expect(chain).To(Equal("CNI-2bbe0c48b91a7d1b8a6753a8")) 31 | }) 32 | 33 | It("must truncate a long name", func() { 34 | chain := FormatChainName("testalongnamethatdoesnotmakesense", "1234") 35 | Expect(len(chain)).To(Equal(maxChainLength)) 36 | Expect(chain).To(Equal("CNI-374f33fe84ab0ed84dcdebe3")) 37 | }) 38 | 39 | It("must be predictable", func() { 40 | chain1 := FormatChainName("testalongnamethatdoesnotmakesense", "1234") 41 | chain2 := FormatChainName("testalongnamethatdoesnotmakesense", "1234") 42 | Expect(len(chain1)).To(Equal(maxChainLength)) 43 | Expect(len(chain2)).To(Equal(maxChainLength)) 44 | Expect(chain1).To(Equal(chain2)) 45 | }) 46 | 47 | It("must change when a character changes", func() { 48 | chain1 := FormatChainName("testalongnamethatdoesnotmakesense", "1234") 49 | chain2 := FormatChainName("testalongnamethatdoesnotmakesense", "1235") 50 | Expect(len(chain1)).To(Equal(maxChainLength)) 51 | Expect(len(chain2)).To(Equal(maxChainLength)) 52 | Expect(chain1).To(Equal("CNI-374f33fe84ab0ed84dcdebe3")) 53 | Expect(chain1).NotTo(Equal(chain2)) 54 | }) 55 | }) 56 | 57 | Describe("MustFormatChainNameWithPrefix", func() { 58 | It("generates a chain name with a prefix", func() { 59 | chain := MustFormatChainNameWithPrefix("test", "1234", "PREFIX-") 60 | Expect(len(chain)).To(Equal(maxChainLength)) 61 | Expect(chain).To(Equal("CNI-PREFIX-2bbe0c48b91a7d1b8")) 62 | }) 63 | 64 | It("must format a short name", func() { 65 | chain := MustFormatChainNameWithPrefix("test", "1234", "PREFIX-") 66 | Expect(len(chain)).To(Equal(maxChainLength)) 67 | Expect(chain).To(Equal("CNI-PREFIX-2bbe0c48b91a7d1b8")) 68 | }) 69 | 70 | It("must truncate a long name", func() { 71 | chain := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-") 72 | Expect(len(chain)).To(Equal(maxChainLength)) 73 | Expect(chain).To(Equal("CNI-PREFIX-374f33fe84ab0ed84")) 74 | }) 75 | 76 | It("must be predictable", func() { 77 | chain1 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-") 78 | chain2 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-") 79 | Expect(len(chain1)).To(Equal(maxChainLength)) 80 | Expect(len(chain2)).To(Equal(maxChainLength)) 81 | Expect(chain1).To(Equal(chain2)) 82 | }) 83 | 84 | It("must change when a character changes", func() { 85 | chain1 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-") 86 | chain2 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1235", "PREFIX-") 87 | Expect(len(chain1)).To(Equal(maxChainLength)) 88 | Expect(len(chain2)).To(Equal(maxChainLength)) 89 | Expect(chain1).To(Equal("CNI-PREFIX-374f33fe84ab0ed84")) 90 | Expect(chain1).NotTo(Equal(chain2)) 91 | }) 92 | 93 | It("panics when prefix is too large", func() { 94 | longPrefix := strings.Repeat("PREFIX-", 4) 95 | Expect(func() { 96 | MustFormatChainNameWithPrefix("test", "1234", longPrefix) 97 | }).To(Panic()) 98 | }) 99 | }) 100 | 101 | Describe("MustFormatHashWithPrefix", func() { 102 | It("always returns a string with the given prefix", func() { 103 | Expect(MustFormatHashWithPrefix(10, "AAA", "some string")).To(HavePrefix("AAA")) 104 | Expect(MustFormatHashWithPrefix(10, "foo", "some string")).To(HavePrefix("foo")) 105 | Expect(MustFormatHashWithPrefix(10, "bar", "some string")).To(HavePrefix("bar")) 106 | }) 107 | 108 | It("always returns a string of the given length", func() { 109 | Expect(MustFormatHashWithPrefix(10, "AAA", "some string")).To(HaveLen(10)) 110 | Expect(MustFormatHashWithPrefix(15, "AAA", "some string")).To(HaveLen(15)) 111 | Expect(MustFormatHashWithPrefix(5, "AAA", "some string")).To(HaveLen(5)) 112 | }) 113 | 114 | It("is deterministic", func() { 115 | val1 := MustFormatHashWithPrefix(10, "AAA", "some string") 116 | val2 := MustFormatHashWithPrefix(10, "AAA", "some string") 117 | val3 := MustFormatHashWithPrefix(10, "AAA", "some string") 118 | Expect(val1).To(Equal(val2)) 119 | Expect(val1).To(Equal(val3)) 120 | }) 121 | 122 | It("is (nearly) perfect (injective function)", func() { 123 | hashes := map[string]int{} 124 | 125 | for i := 0; i < 1000; i++ { 126 | name := fmt.Sprintf("string %d", i) 127 | hashes[MustFormatHashWithPrefix(8, "", name)]++ 128 | } 129 | 130 | for key, count := range hashes { 131 | Expect(count).To(Equal(1), "for key "+key+" got non-unique correspondence") 132 | } 133 | }) 134 | 135 | assertPanicWith := func(f func(), expectedErrorMessage string) { 136 | defer func() { 137 | Expect(recover()).To(Equal(expectedErrorMessage)) 138 | }() 139 | f() 140 | Fail("function should have panicked but did not") 141 | } 142 | 143 | It("panics when prefix is longer than the length", func() { 144 | assertPanicWith( 145 | func() { MustFormatHashWithPrefix(3, "AAA", "some string") }, 146 | "invalid length", 147 | ) 148 | }) 149 | 150 | It("panics when length is not positive", func() { 151 | assertPanicWith( 152 | func() { MustFormatHashWithPrefix(0, "", "some string") }, 153 | "invalid length", 154 | ) 155 | }) 156 | 157 | It("panics when length is larger than MaxLen", func() { 158 | assertPanicWith( 159 | func() { MustFormatHashWithPrefix(MaxHashLen+1, "", "some string") }, 160 | "invalid length", 161 | ) 162 | }) 163 | }) 164 | 165 | }) 166 | -------------------------------------------------------------------------------- /hotupgrade/README.md: -------------------------------------------------------------------------------- 1 | # sidecarset hotupgrade demo 2 | ## Migration 3 | The SidecarSet hot upgrade mechanism not only completes the switching of mesh containers and provides a coordination mechanism for old and new versions, but this is only the first step in a long journey. 4 | The mesh container also needs to provide a PostStartHook script to complete the hot migration of the mesh service itself (the above Migration process), such as Envoy hot restart and Mosn lossless restart. 5 | 6 | The migration process of such mesh containers can be summarized as follows: send ListenFD via UDS and stop accepting new requests, start draining. 7 | For mesh containers that do not support migration you can refer to this process to complete the rebuilding. The logic diagram is as follows: 8 | 9 | ![migration](img/migration.png) 10 | 11 | ## Examples 12 | ### 1. create sidecarSet 13 | kubectl apply -f config/sidecarset.yaml 14 | ``` 15 | apiVersion: apps.kruise.io/v1alpha1 16 | kind: SidecarSet 17 | metadata: 18 | name: hotupgrade-sidecarset 19 | spec: 20 | selector: 21 | matchLabels: 22 | app: hotupgrade 23 | containers: 24 | - name: sidecar 25 | image: openkruise/hotupgrade-sample:sidecarv1 26 | imagePullPolicy: Always 27 | lifecycle: 28 | postStart: 29 | exec: 30 | command: 31 | - /bin/sh 32 | - /migrate.sh 33 | upgradeStrategy: 34 | upgradeType: HotUpgrade 35 | hotUpgradeEmptyImage: openkruise/hotupgrade-sample:empty 36 | ``` 37 | 38 | ### 2. create cloneset and the Pod has been injected with sidecar-1 and sidecar-2 containers 39 | ``` 40 | // create cloneset 41 | # kubectl apply -f config/cloneset.yaml 42 | 43 | # kubectl describe pods busybox-j9h72 44 | Name: busybox-j9h72 45 | Namespace: default 46 | Labels: app=hotupgrade 47 | Annotations: kruise.io/sidecarset-hash: 48 | {"hotupgrade-sidecarset":{"updateTimestamp":"2021-06-04T05:44:26Z","hash":"2v7vv4w426v2dw47248f966w4wd2vw9fz872bvvv42cdwz2b8ff689w7v4wb4wz... 49 | kruise.io/sidecarset-hash-without-image: 50 | {"hotupgrade-sidecarset":{"updateTimestamp":"2021-06-04T05:44:26Z","hash":"xfc8x5449v668w2662v64c8476c2v772f4658ddcddxf75dwc5zbfbf5bf5wxb5... 51 | kruise.io/sidecarset-injected-list: hotupgrade-sidecarset 52 | kruise.io/sidecarset-working-hotupgrade-container: {"sidecar":"sidecar-1"} 53 | kubernetes.io/psp: ack.privileged 54 | lifecycle.apps.kruise.io/timestamp: 2021-06-04T05:44:26Z 55 | version.sidecarset.kruise.io/sidecar-1: 1 56 | version.sidecarset.kruise.io/sidecar-2: 0 57 | versionalt.sidecarset.kruise.io/sidecar-1: 0 58 | versionalt.sidecarset.kruise.io/sidecar-2: 1 59 | Status: Running 60 | Containers: 61 | sidecar-1: 62 | Container ID: docker://3e792156d727e3f6d77b4b15db2d94efa598bfabd57ab80d9ecaccaef4bfcda0 63 | Image: openkruise/hotupgrade-sample:sidecarv1 64 | Environment: 65 | IS_INJECTED: true 66 | SIDECARSET_VERSION: (v1:metadata.annotations['version.sidecarset.kruise.io/sidecar-1']) 67 | SIDECARSET_VERSION_ALT: (v1:metadata.annotations['versionalt.sidecarset.kruise.io/sidecar-1']) 68 | sidecar-2: 69 | Container ID: docker://ee21bfb0ffd64489e502346c2f5ca3484e45e39218f7f66c5212c2cc2cad86a9 70 | Image: openkruise/hotupgrade-sample:empty 71 | Environment: 72 | IS_INJECTED: true 73 | SIDECARSET_VERSION: (v1:metadata.annotations['version.sidecarset.kruise.io/sidecar-2']) 74 | SIDECARSET_VERSION_ALT: (v1:metadata.annotations['versionalt.sidecarset.kruise.io/sidecar-2']) 75 | busybox: 76 | Container ID: docker://14ce4ea40e5ef864db75c6b4ad3febc6b11e4a1d992eb9078d943baac7d28b42 77 | Image: openkruise/hotupgrade-sample:busybox 78 | ``` 79 | 80 | ### 4. busybox requests the sidecar(version=v1) service once every 100 milliseconds 81 | ``` 82 | # kubectl logs busybox-j9h72 busybox 83 | I0604 05:23:53.345239 1 main.go:39] request sidecar server success, and response(body=This is version(v1) sidecar) 84 | I0604 05:23:53.455792 1 main.go:39] request sidecar server success, and response(body=This is version(v1) sidecar) 85 | I0604 05:23:53.566292 1 main.go:39] request sidecar server success, and response(body=This is version(v1) sidecar) 86 | I0604 05:23:53.676816 1 main.go:39] request sidecar server success, and response(body=This is version(v1) sidecar) 87 | I0604 05:23:53.787334 1 main.go:39] request sidecar server success, and response(body=This is version(v1) sidecar) 88 | I0604 05:23:53.897849 1 main.go:39] request sidecar server success, and response(body=This is version(v1) sidecar) 89 | ``` 90 | 91 | ### 5. upgrade sidecar container, image=openkruise/hotupgrade-sample:sidecarv2 92 | ``` 93 | //edit sidecarset.yaml and changed image=openkruise/hotupgrade-sample:sidecarv2 94 | # kubectl apply -f config/sidecarset.yaml 95 | 96 | # kubectl get pods busybox-j9h72 97 | NAME READY STATUS RESTARTS AGE 98 | busybox-j9h72 1/1 Running 1 6d21h 99 | 100 | # kubectl describe pods busybox-j9h72 101 | Events: 102 | Type Reason Age Message 103 | ---- ------ ---- ------- 104 | Normal Killing 5m47s Container sidecar-2 definition changed, will be restarted 105 | Normal Pulling 5m17s Pulling image "openkruise/hotupgrade-sample:sidecarv2" 106 | Normal Created 5m5s (x2 over 12m) Created container sidecar-2 107 | Normal Started 5m5s (x2 over 12m) Started container sidecar-2 108 | Normal Pulled 5m5s Successfully pulled image "openkruise/hotupgrade-sample:sidecarv2" 109 | ``` 110 | 111 | ### 6. busybox continuously requests the sidecar service and there are no failed requests 112 | ``` 113 | # kubectl logs busybox-j9h72 busybox 114 | I0604 05:53:40.725662 1 main.go:39] request sidecar server success, and response(body=This is version(v1) sidecar) 115 | I0604 05:53:40.836212 1 main.go:39] request sidecar server success, and response(body=This is version(v1) sidecar) 116 | I0604 05:53:40.946739 1 main.go:39] request sidecar server success, and response(body=This is version(v1) sidecar) 117 | I0604 05:53:41.057366 1 main.go:39] request sidecar server success, and response(body=This is version(v2) sidecar) 118 | I0604 05:53:41.167977 1 main.go:39] request sidecar server success, and response(body=This is version(v2) sidecar) 119 | I0604 05:53:41.278621 1 main.go:39] request sidecar server success, and response(body=This is version(v2) sidecar) 120 | ``` 121 | -------------------------------------------------------------------------------- /containernetworking/plugins/plugins/ipam/host-local/backend/allocator/allocator.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CNI authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package allocator 16 | 17 | import ( 18 | "fmt" 19 | "log" 20 | "net" 21 | "os" 22 | "strconv" 23 | 24 | current "github.com/containernetworking/cni/pkg/types/100" 25 | 26 | "github.com/containernetworking/plugins/pkg/ip" 27 | "github.com/containernetworking/plugins/plugins/ipam/host-local/backend" 28 | ) 29 | 30 | type IPAllocator struct { 31 | rangeset *RangeSet 32 | store backend.Store 33 | rangeID string // Used for tracking last reserved ip 34 | } 35 | 36 | func NewIPAllocator(s *RangeSet, store backend.Store, id int) *IPAllocator { 37 | return &IPAllocator{ 38 | rangeset: s, 39 | store: store, 40 | rangeID: strconv.Itoa(id), 41 | } 42 | } 43 | 44 | // Get allocates an IP 45 | func (a *IPAllocator) Get(id string, ifname string, requestedIP net.IP) (*current.IPConfig, error) { 46 | a.store.Lock() 47 | defer a.store.Unlock() 48 | 49 | var reservedIP *net.IPNet 50 | var gw net.IP 51 | 52 | if requestedIP != nil { 53 | if err := canonicalizeIP(&requestedIP); err != nil { 54 | return nil, err 55 | } 56 | 57 | r, err := a.rangeset.RangeFor(requestedIP) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | if requestedIP.Equal(r.Gateway) { 63 | return nil, fmt.Errorf("requested ip %s is subnet's gateway", requestedIP.String()) 64 | } 65 | 66 | reserved, err := a.store.Reserve(id, ifname, requestedIP, a.rangeID) 67 | if err != nil { 68 | return nil, err 69 | } 70 | if !reserved { 71 | return nil, fmt.Errorf("requested IP address %s is not available in range set %s", requestedIP, a.rangeset.String()) 72 | } 73 | reservedIP = &net.IPNet{IP: requestedIP, Mask: r.Subnet.Mask} 74 | gw = r.Gateway 75 | 76 | } else { 77 | // try to get allocated IPs for this given id, if exists, just return error 78 | // because duplicate allocation is not allowed in SPEC 79 | // https://github.com/containernetworking/cni/blob/master/SPEC.md 80 | allocatedIPs := a.store.GetByID(id, ifname) 81 | for _, allocatedIP := range allocatedIPs { 82 | // check whether the existing IP belong to this range set 83 | if _, err := a.rangeset.RangeFor(allocatedIP); err == nil { 84 | return nil, fmt.Errorf("%s has been allocated to %s, duplicate allocation is not allowed", allocatedIP.String(), id) 85 | } 86 | } 87 | 88 | iter, err := a.GetIter() 89 | if err != nil { 90 | return nil, err 91 | } 92 | for { 93 | reservedIP, gw = iter.Next() 94 | if reservedIP == nil { 95 | break 96 | } 97 | 98 | reserved, err := a.store.Reserve(id, ifname, reservedIP.IP, a.rangeID) 99 | if err != nil { 100 | return nil, err 101 | } 102 | 103 | if reserved { 104 | break 105 | } 106 | } 107 | } 108 | 109 | if reservedIP == nil { 110 | return nil, fmt.Errorf("no IP addresses available in range set: %s", a.rangeset.String()) 111 | } 112 | 113 | return ¤t.IPConfig{ 114 | Address: *reservedIP, 115 | Gateway: gw, 116 | }, nil 117 | } 118 | 119 | // Release clears all IPs allocated for the container with given ID 120 | func (a *IPAllocator) Release(id string, ifname string) error { 121 | a.store.Lock() 122 | defer a.store.Unlock() 123 | 124 | return a.store.ReleaseByID(id, ifname) 125 | } 126 | 127 | type RangeIter struct { 128 | rangeset *RangeSet 129 | 130 | // The current range id 131 | rangeIdx int 132 | 133 | // Our current position 134 | cur net.IP 135 | 136 | // The IP where we started iterating; if we hit this again, we're done. 137 | startIP net.IP 138 | } 139 | 140 | // GetIter encapsulates the strategy for this allocator. 141 | // We use a round-robin strategy, attempting to evenly use the whole set. 142 | // More specifically, a crash-looping container will not see the same IP until 143 | // the entire range has been run through. 144 | // We may wish to consider avoiding recently-released IPs in the future. 145 | func (a *IPAllocator) GetIter() (*RangeIter, error) { 146 | iter := RangeIter{ 147 | rangeset: a.rangeset, 148 | } 149 | 150 | // Round-robin by trying to allocate from the last reserved IP + 1 151 | startFromLastReservedIP := false 152 | 153 | // We might get a last reserved IP that is wrong if the range indexes changed. 154 | // This is not critical, we just lose round-robin this one time. 155 | lastReservedIP, err := a.store.LastReservedIP(a.rangeID) 156 | if err != nil && !os.IsNotExist(err) { 157 | log.Printf("Error retrieving last reserved ip: %v", err) 158 | } else if lastReservedIP != nil { 159 | startFromLastReservedIP = a.rangeset.Contains(lastReservedIP) 160 | } 161 | 162 | // Find the range in the set with this IP 163 | if startFromLastReservedIP { 164 | for i, r := range *a.rangeset { 165 | if r.Contains(lastReservedIP) { 166 | iter.rangeIdx = i 167 | 168 | // We advance the cursor on every Next(), so the first call 169 | // to next() will return lastReservedIP + 1 170 | iter.cur = lastReservedIP 171 | break 172 | } 173 | } 174 | } else { 175 | iter.rangeIdx = 0 176 | iter.startIP = (*a.rangeset)[0].RangeStart 177 | } 178 | return &iter, nil 179 | } 180 | 181 | // Next returns the next IP, its mask, and its gateway. Returns nil 182 | // if the iterator has been exhausted 183 | func (i *RangeIter) Next() (*net.IPNet, net.IP) { 184 | r := (*i.rangeset)[i.rangeIdx] 185 | 186 | // If this is the first time iterating and we're not starting in the middle 187 | // of the range, then start at rangeStart, which is inclusive 188 | if i.cur == nil { 189 | i.cur = r.RangeStart 190 | i.startIP = i.cur 191 | if i.cur.Equal(r.Gateway) { 192 | return i.Next() 193 | } 194 | return &net.IPNet{IP: i.cur, Mask: r.Subnet.Mask}, r.Gateway 195 | } 196 | 197 | // If we've reached the end of this range, we need to advance the range 198 | // RangeEnd is inclusive as well 199 | if i.cur.Equal(r.RangeEnd) { 200 | i.rangeIdx += 1 201 | i.rangeIdx %= len(*i.rangeset) 202 | r = (*i.rangeset)[i.rangeIdx] 203 | 204 | i.cur = r.RangeStart 205 | } else { 206 | i.cur = ip.NextIP(i.cur) 207 | } 208 | 209 | if i.startIP == nil { 210 | i.startIP = i.cur 211 | } else if i.cur.Equal(i.startIP) { 212 | // IF we've looped back to where we started, give up 213 | return nil, nil 214 | } 215 | 216 | if i.cur.Equal(r.Gateway) { 217 | return i.Next() 218 | } 219 | 220 | return &net.IPNet{IP: i.cur, Mask: r.Subnet.Mask}, r.Gateway 221 | } 222 | --------------------------------------------------------------------------------