From 572437ce2d2fa7dbbec4030674efb5f2993d51b4 Mon Sep 17 00:00:00 2001 From: gunjan5 Date: Mon, 7 Aug 2017 20:58:28 -0700 Subject: [PATCH] Migrate from TPR to CRD for the Kubernetes backend --- Makefile | 25 ++- lib/backend/k8s/conversion.go | 8 +- lib/backend/k8s/conversion_test.go | 18 +- .../{thirdparty => custom}/globalbgpconfig.go | 51 ++--- .../{thirdparty => custom}/globalbgppeer.go | 47 +++-- .../globalfelixconfig.go} | 51 ++--- .../globalnetworkpolicy.go} | 36 ++-- .../k8s/{thirdparty => custom}/ippool.go | 52 +++-- lib/backend/k8s/k8s.go | 187 ++++++------------ lib/backend/k8s/k8s_fv_test.go | 156 +++++++-------- lib/backend/k8s/resources/customresource.go | 127 ++++++------ lib/backend/k8s/resources/globalbgpconfig.go | 44 +++-- .../k8s/resources/globalbgpconfig_test.go | 12 +- lib/backend/k8s/resources/globalbgppeer.go | 44 +++-- .../k8s/resources/globalbgppeer_test.go | 36 ++-- .../{globalconfig.go => globalfelixconfig.go} | 46 ++--- ...nfig_test.go => globalfelixconfig_test.go} | 12 +- ...rkpolicies.go => globalnetworkpolicies.go} | 63 +++--- ..._test.go => globalnetworkpolicies_test.go} | 30 ++- lib/backend/k8s/resources/ippool.go | 68 ++++--- lib/backend/k8s/resources/ippool_test.go | 46 ++--- lib/backend/k8s/resources/names.go | 23 ++- lib/backend/k8s/resources/names_test.go | 18 +- lib/backend/k8s/resources/nodebgppeer_test.go | 4 +- lib/backend/k8s/syncer.go | 158 +++++++-------- lib/backend/k8s/syncer_test.go | 10 +- lib/backend/model/bgpconfig.go | 2 +- lib/backend/model/felixconfig.go | 2 +- lib/backend/model/keys.go | 2 +- lib/backend/model/keys_test.go | 2 +- lib/client/config.go | 4 +- lib/client/ippool.go | 60 +----- lib/converter/ippool.go | 92 +++++++++ test/README.md | 20 ++ test/crds.yaml | 69 +++++++ test/mock-node.yaml | 14 ++ 36 files changed, 879 insertions(+), 760 deletions(-) rename lib/backend/k8s/{thirdparty => custom}/globalbgpconfig.go (58%) rename lib/backend/k8s/{thirdparty => custom}/globalbgppeer.go (64%) rename lib/backend/k8s/{thirdparty/globalconfig.go => custom/globalfelixconfig.go} (57%) rename lib/backend/k8s/{thirdparty/systemnetworkpolicy.go => custom/globalnetworkpolicy.go} (67%) rename lib/backend/k8s/{thirdparty => custom}/ippool.go (64%) rename lib/backend/k8s/resources/{globalconfig.go => globalfelixconfig.go} (53%) rename lib/backend/k8s/resources/{globalconfig_test.go => globalfelixconfig_test.go} (89%) rename lib/backend/k8s/resources/{systemnetworkpolicies.go => globalnetworkpolicies.go} (52%) rename lib/backend/k8s/resources/{systemnetworkpolicies_test.go => globalnetworkpolicies_test.go} (85%) create mode 100644 lib/converter/ippool.go create mode 100644 test/README.md create mode 100644 test/crds.yaml create mode 100644 test/mock-node.yaml diff --git a/Makefile b/Makefile index 286b6787f..7795ec9ca 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ default: all all: test test: ut -K8S_VERSION=1.7.3 +K8S_VERSION=v1.7.3 CALICO_BUILD?=calico/go-build PACKAGE_NAME?=projectcalico/libcalico-go LOCAL_USER_ID?=$(shell id -u $$USER) @@ -51,7 +51,7 @@ run-kubernetes-master: stop-kubernetes-master docker run \ --net=host --name st-apiserver \ --detach \ - gcr.io/google_containers/hyperkube-amd64:v${K8S_VERSION} \ + gcr.io/google_containers/hyperkube-amd64:${K8S_VERSION} \ /hyperkube apiserver \ --bind-address=0.0.0.0 \ --insecure-bind-address=0.0.0.0 \ @@ -69,7 +69,7 @@ run-kubernetes-master: stop-kubernetes-master docker run \ --net=host --name st-controller-manager \ --detach \ - gcr.io/google_containers/hyperkube-amd64:v${K8S_VERSION} \ + gcr.io/google_containers/hyperkube-amd64:${K8S_VERSION} \ /hyperkube controller-manager \ --master=127.0.0.1:8080 \ --min-resync-period=3m \ @@ -77,6 +77,25 @@ run-kubernetes-master: stop-kubernetes-master --cluster-cidr=10.10.0.0/16 \ --v=5 + # Create CustomResourceDefinition (CRD) for Calico resources + # from the manifest crds.yaml + docker run \ + --net=host \ + --rm \ + -v $(CURDIR):/manifests \ + lachlanevenson/k8s-kubectl:${K8S_VERSION} \ + --server=http://localhost:8080 \ + apply -f manifests/test/crds.yaml + + # Create a Node in the API for the tests to use. + docker run \ + --net=host \ + --rm \ + -v $(CURDIR):/manifests \ + lachlanevenson/k8s-kubectl:${K8S_VERSION} \ + --server=http://localhost:8080 \ + apply -f manifests/test/mock-node.yaml + ## Stop the local kubernetes master stop-kubernetes-master: # Delete the cluster role binding. diff --git a/lib/backend/k8s/conversion.go b/lib/backend/k8s/conversion.go index bf12a903f..27a7c96ed 100644 --- a/lib/backend/k8s/conversion.go +++ b/lib/backend/k8s/conversion.go @@ -68,13 +68,13 @@ func (c converter) parsePolicyNameNamespace(name string) (string, error) { // parsePolicyNameNetworkPolicy extracts the Kubernetes Namespace and NetworkPolicy that backs the given Policy. func (c converter) parsePolicyNameNetworkPolicy(name string) (string, string, error) { - // Policies backed by NetworkPolicies have form "np.projectcalico.org/." - if !strings.HasPrefix(name, "np.projectcalico.org/") { + // Policies backed by NetworkPolicies have form "knp.default.." + if !strings.HasPrefix(name, "knp.default.") { // This is not backed by a Kubernetes NetworkPolicy. return "", "", fmt.Errorf("Policy %s not backed by a NetworkPolicy", name) } - splits := strings.SplitN(strings.TrimPrefix(name, "np.projectcalico.org/"), ".", 2) + splits := strings.SplitN(strings.TrimPrefix(name, "knp.default."), ".", 2) if len(splits) != 2 { return "", "", fmt.Errorf("Name does not include both Namespace and NetworkPolicy: %s", name) } @@ -201,7 +201,7 @@ func (c converter) podToWorkloadEndpoint(pod *kapiv1.Pod) (*model.KVPair, error) // networkPolicyToPolicy converts a k8s NetworkPolicy to a model.KVPair. func (c converter) networkPolicyToPolicy(np *extensions.NetworkPolicy) (*model.KVPair, error) { // Pull out important fields. - policyName := fmt.Sprintf("np.projectcalico.org/%s.%s", np.ObjectMeta.Namespace, np.ObjectMeta.Name) + policyName := fmt.Sprintf("knp.default.%s.%s", np.ObjectMeta.Namespace, np.ObjectMeta.Name) // We insert all the NetworkPolicy Policies at order 1000.0 after conversion. // This order might change in future. diff --git a/lib/backend/k8s/conversion_test.go b/lib/backend/k8s/conversion_test.go index 52ca88347..84d6d9438 100644 --- a/lib/backend/k8s/conversion_test.go +++ b/lib/backend/k8s/conversion_test.go @@ -41,7 +41,7 @@ var _ = Describe("Test parsing strings", func() { It("should parse valid policy names", func() { // Parse a NetworkPolicy backed Policy. - name := "np.projectcalico.org/Namespace.policyName" + name := "knp.default.Namespace.policyName" ns, polName, err := c.parsePolicyNameNetworkPolicy(name) Expect(err).NotTo(HaveOccurred()) Expect(ns).To(Equal("Namespace")) @@ -248,7 +248,7 @@ var _ = Describe("Test NetworkPolicy conversion", func() { Expect(err).NotTo(HaveOccurred()) // Assert key fields are correct. - Expect(pol.Key.(model.PolicyKey).Name).To(Equal("np.projectcalico.org/default.testPolicy")) + Expect(pol.Key.(model.PolicyKey).Name).To(Equal("knp.default.default.testPolicy")) // Assert value fields are correct. Expect(int(*pol.Value.(*model.Policy).Order)).To(Equal(1000)) @@ -286,7 +286,7 @@ var _ = Describe("Test NetworkPolicy conversion", func() { Expect(err).NotTo(HaveOccurred()) // Assert key fields are correct. - Expect(pol.Key.(model.PolicyKey).Name).To(Equal("np.projectcalico.org/default.testPolicy")) + Expect(pol.Key.(model.PolicyKey).Name).To(Equal("knp.default.default.testPolicy")) // Assert value fields are correct. Expect(int(*pol.Value.(*model.Policy).Order)).To(Equal(1000)) @@ -339,7 +339,7 @@ var _ = Describe("Test NetworkPolicy conversion", func() { By("parsing the policy", func() { pol, err = c.networkPolicyToPolicy(&np) Expect(err).NotTo(HaveOccurred()) - Expect(pol.Key.(model.PolicyKey).Name).To(Equal("np.projectcalico.org/default.testPolicy")) + Expect(pol.Key.(model.PolicyKey).Name).To(Equal("knp.default.default.testPolicy")) Expect(int(*pol.Value.(*model.Policy).Order)).To(Equal(1000)) }) @@ -412,7 +412,7 @@ var _ = Describe("Test NetworkPolicy conversion", func() { By("parsing the policy", func() { pol, err = c.networkPolicyToPolicy(&np) Expect(err).NotTo(HaveOccurred()) - Expect(pol.Key.(model.PolicyKey).Name).To(Equal("np.projectcalico.org/default.testPolicy")) + Expect(pol.Key.(model.PolicyKey).Name).To(Equal("knp.default.default.testPolicy")) Expect(int(*pol.Value.(*model.Policy).Order)).To(Equal(1000)) }) @@ -459,7 +459,7 @@ var _ = Describe("Test NetworkPolicy conversion", func() { Expect(err).NotTo(HaveOccurred()) // Assert key fields are correct. - Expect(pol.Key.(model.PolicyKey).Name).To(Equal("np.projectcalico.org/default.testPolicy")) + Expect(pol.Key.(model.PolicyKey).Name).To(Equal("knp.default.default.testPolicy")) // Assert value fields are correct. Expect(int(*pol.Value.(*model.Policy).Order)).To(Equal(1000)) @@ -500,7 +500,7 @@ var _ = Describe("Test NetworkPolicy conversion", func() { Expect(err).NotTo(HaveOccurred()) // Assert key fields are correct. - Expect(pol.Key.(model.PolicyKey).Name).To(Equal("np.projectcalico.org/default.testPolicy")) + Expect(pol.Key.(model.PolicyKey).Name).To(Equal("knp.default.default.testPolicy")) // Assert value fields are correct. Expect(int(*pol.Value.(*model.Policy).Order)).To(Equal(1000)) @@ -537,7 +537,7 @@ var _ = Describe("Test NetworkPolicy conversion", func() { Expect(err).NotTo(HaveOccurred()) // Assert key fields are correct. - Expect(pol.Key.(model.PolicyKey).Name).To(Equal("np.projectcalico.org/default.testPolicy")) + Expect(pol.Key.(model.PolicyKey).Name).To(Equal("knp.default.default.testPolicy")) // Assert value fields are correct. Expect(int(*pol.Value.(*model.Policy).Order)).To(Equal(1000)) @@ -577,7 +577,7 @@ var _ = Describe("Test NetworkPolicy conversion", func() { Expect(err).NotTo(HaveOccurred()) // Assert key fields are correct. - Expect(pol.Key.(model.PolicyKey).Name).To(Equal("np.projectcalico.org/default.testPolicy")) + Expect(pol.Key.(model.PolicyKey).Name).To(Equal("knp.default.default.testPolicy")) // Assert value fields are correct. Expect(int(*pol.Value.(*model.Policy).Order)).To(Equal(1000)) diff --git a/lib/backend/k8s/thirdparty/globalbgpconfig.go b/lib/backend/k8s/custom/globalbgpconfig.go similarity index 58% rename from lib/backend/k8s/thirdparty/globalbgpconfig.go rename to lib/backend/k8s/custom/globalbgpconfig.go index 30b57b2c2..61b8fb481 100644 --- a/lib/backend/k8s/thirdparty/globalbgpconfig.go +++ b/lib/backend/k8s/custom/globalbgpconfig.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package thirdparty +package custom import ( "encoding/json" @@ -21,42 +21,43 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" ) -type GlobalBgpConfigSpec struct { - Name string `json:"name"` - Value string `json:"value"` -} - -type GlobalBgpConfig struct { +type GlobalBGPConfig struct { metav1.TypeMeta `json:",inline"` - Metadata metav1.ObjectMeta `json:"metadata"` + Metadata metav1.ObjectMeta `json:"metadata"` + Spec GlobalBGPConfigSpec `json:"spec"` +} - Spec GlobalBgpConfigSpec `json:"spec"` +type GlobalBGPConfigSpec struct { + // The reason we have Name field in Spec is because k8s metadata + // name field requires the string to be lowercase, so Name field + // in Spec is to preserve the casing. + Name string `json:"name"` + Value string `json:"value"` } -type GlobalBgpConfigList struct { +type GlobalBGPConfigList struct { metav1.TypeMeta `json:",inline"` - Metadata metav1.ListMeta `json:"metadata"` - - Items []GlobalBgpConfig `json:"items"` + Metadata metav1.ListMeta `json:"metadata"` + Items []GlobalBGPConfig `json:"items"` } // Required to satisfy Object interface -func (e *GlobalBgpConfig) GetObjectKind() schema.ObjectKind { +func (e *GlobalBGPConfig) GetObjectKind() schema.ObjectKind { return &e.TypeMeta } // Required to satisfy ObjectMetaAccessor interface -func (e *GlobalBgpConfig) GetObjectMeta() metav1.Object { +func (e *GlobalBGPConfig) GetObjectMeta() metav1.Object { return &e.Metadata } // Required to satisfy Object interface -func (el *GlobalBgpConfigList) GetObjectKind() schema.ObjectKind { +func (el *GlobalBGPConfigList) GetObjectKind() schema.ObjectKind { return &el.TypeMeta } // Required to satisfy ListMetaAccessor interface -func (el *GlobalBgpConfigList) GetListMeta() metav1.List { +func (el *GlobalBGPConfigList) GetListMeta() metav1.List { return &el.Metadata } @@ -64,27 +65,27 @@ func (el *GlobalBgpConfigList) GetListMeta() metav1.List { // resources and ugorji. If/when these issues are resolved, the code below // should no longer be required. -type GlobalBgpConfigListCopy GlobalBgpConfigList -type GlobalBgpConfigCopy GlobalBgpConfig +type GlobalBGPConfigListCopy GlobalBGPConfigList +type GlobalBGPConfigCopy GlobalBGPConfig -func (g *GlobalBgpConfig) UnmarshalJSON(data []byte) error { - tmp := GlobalBgpConfigCopy{} +func (g *GlobalBGPConfig) UnmarshalJSON(data []byte) error { + tmp := GlobalBGPConfigCopy{} err := json.Unmarshal(data, &tmp) if err != nil { return err } - tmp2 := GlobalBgpConfig(tmp) + tmp2 := GlobalBGPConfig(tmp) *g = tmp2 return nil } -func (l *GlobalBgpConfigList) UnmarshalJSON(data []byte) error { - tmp := GlobalBgpConfigListCopy{} +func (l *GlobalBGPConfigList) UnmarshalJSON(data []byte) error { + tmp := GlobalBGPConfigListCopy{} err := json.Unmarshal(data, &tmp) if err != nil { return err } - tmp2 := GlobalBgpConfigList(tmp) + tmp2 := GlobalBGPConfigList(tmp) *l = tmp2 return nil } diff --git a/lib/backend/k8s/thirdparty/globalbgppeer.go b/lib/backend/k8s/custom/globalbgppeer.go similarity index 64% rename from lib/backend/k8s/thirdparty/globalbgppeer.go rename to lib/backend/k8s/custom/globalbgppeer.go index 759d74409..ca4e32c55 100644 --- a/lib/backend/k8s/thirdparty/globalbgppeer.go +++ b/lib/backend/k8s/custom/globalbgppeer.go @@ -12,48 +12,57 @@ // See the License for the specific language governing permissions and // limitations under the License. -package thirdparty +package custom import ( "encoding/json" "github.com/projectcalico/libcalico-go/lib/api" + "github.com/projectcalico/libcalico-go/lib/net" + "github.com/projectcalico/libcalico-go/lib/scope" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" ) -// GlobalBgpPeer is the ThirdPartyResource definition of a Calico Global BGP Peer resource in +// BGPPeer is the CustomResourceDefinition of a Calico BGP Peer resource in // the Kubernetes API. -type GlobalBgpPeer struct { +type BGPPeer struct { metav1.TypeMeta `json:",inline"` Metadata metav1.ObjectMeta `json:"metadata"` - Spec api.BGPPeerSpec `json:"spec"` + Spec BGPPeerSpec `json:"spec"` } -// GlobalBgpPeerList is a list of Calico Global BGP Peer resources. -type GlobalBgpPeerList struct { +type BGPPeerSpec struct { + api.BGPPeerSpec + Scope scope.Scope `json:"scope"` + Node string `json:"node,omitempty"` + PeerIP net.IP `json:"peerIP"` +} + +// BGPPeerList is a list of Calico Global BGP Peer resources. +type BGPPeerList struct { metav1.TypeMeta `json:",inline"` Metadata metav1.ListMeta `json:"metadata"` - Items []GlobalBgpPeer `json:"items"` + Items []BGPPeer `json:"items"` } // GetObjectKind returns the kind of this object. Required to satisfy Object interface -func (e *GlobalBgpPeer) GetObjectKind() schema.ObjectKind { +func (e *BGPPeer) GetObjectKind() schema.ObjectKind { return &e.TypeMeta } // GetObjectMeta returns the object metadata of this object. Required to satisfy ObjectMetaAccessor interface -func (e *GlobalBgpPeer) GetObjectMeta() metav1.Object { +func (e *BGPPeer) GetObjectMeta() metav1.Object { return &e.Metadata } // GetObjectKind returns the kind of this object. Required to satisfy Object interface -func (el *GlobalBgpPeerList) GetObjectKind() schema.ObjectKind { +func (el *BGPPeerList) GetObjectKind() schema.ObjectKind { return &el.TypeMeta } // GetListMeta returns the list metadata of this object. Required to satisfy ListMetaAccessor interface -func (el *GlobalBgpPeerList) GetListMeta() metav1.List { +func (el *BGPPeerList) GetListMeta() metav1.List { return &el.Metadata } @@ -61,27 +70,27 @@ func (el *GlobalBgpPeerList) GetListMeta() metav1.List { // resources and ugorji. If/when these issues are resolved, the code below // should no longer be required. -type GlobalBgpPeerListCopy GlobalBgpPeerList -type GlobalBgpPeerCopy GlobalBgpPeer +type BGPPeerListCopy BGPPeerList +type BGPPeerCopy BGPPeer -func (g *GlobalBgpPeer) UnmarshalJSON(data []byte) error { - tmp := GlobalBgpPeerCopy{} +func (g *BGPPeer) UnmarshalJSON(data []byte) error { + tmp := BGPPeerCopy{} err := json.Unmarshal(data, &tmp) if err != nil { return err } - tmp2 := GlobalBgpPeer(tmp) + tmp2 := BGPPeer(tmp) *g = tmp2 return nil } -func (l *GlobalBgpPeerList) UnmarshalJSON(data []byte) error { - tmp := GlobalBgpPeerListCopy{} +func (l *BGPPeerList) UnmarshalJSON(data []byte) error { + tmp := BGPPeerListCopy{} err := json.Unmarshal(data, &tmp) if err != nil { return err } - tmp2 := GlobalBgpPeerList(tmp) + tmp2 := BGPPeerList(tmp) *l = tmp2 return nil } diff --git a/lib/backend/k8s/thirdparty/globalconfig.go b/lib/backend/k8s/custom/globalfelixconfig.go similarity index 57% rename from lib/backend/k8s/thirdparty/globalconfig.go rename to lib/backend/k8s/custom/globalfelixconfig.go index 70563bb2d..a875191d3 100644 --- a/lib/backend/k8s/thirdparty/globalconfig.go +++ b/lib/backend/k8s/custom/globalfelixconfig.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package thirdparty +package custom import ( "encoding/json" @@ -21,42 +21,43 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" ) -type GlobalConfigSpec struct { - Name string `json:"name"` - Value string `json:"value"` -} - -type GlobalConfig struct { +type GlobalFelixConfig struct { metav1.TypeMeta `json:",inline"` - Metadata metav1.ObjectMeta `json:"metadata"` + Metadata metav1.ObjectMeta `json:"metadata"` + Spec GlobalFelixConfigSpec `json:"spec"` +} - Spec GlobalConfigSpec `json:"spec"` +type GlobalFelixConfigSpec struct { + // The reason we have Name field in Spec is because k8s metadata + // name field requires the string to be lowercase, so Name field + // in Spec is to preserve the casing. + Name string `json:"name"` + Value string `json:"value"` } -type GlobalConfigList struct { +type GlobalFelixConfigList struct { metav1.TypeMeta `json:",inline"` - Metadata metav1.ListMeta `json:"metadata"` - - Items []GlobalConfig `json:"items"` + Metadata metav1.ListMeta `json:"metadata"` + Items []GlobalFelixConfig `json:"items"` } // Required to satisfy Object interface -func (e *GlobalConfig) GetObjectKind() schema.ObjectKind { +func (e *GlobalFelixConfig) GetObjectKind() schema.ObjectKind { return &e.TypeMeta } // Required to satisfy ObjectMetaAccessor interface -func (e *GlobalConfig) GetObjectMeta() metav1.Object { +func (e *GlobalFelixConfig) GetObjectMeta() metav1.Object { return &e.Metadata } // Required to satisfy Object interface -func (el *GlobalConfigList) GetObjectKind() schema.ObjectKind { +func (el *GlobalFelixConfigList) GetObjectKind() schema.ObjectKind { return &el.TypeMeta } // Required to satisfy ListMetaAccessor interface -func (el *GlobalConfigList) GetListMeta() metav1.List { +func (el *GlobalFelixConfigList) GetListMeta() metav1.List { return &el.Metadata } @@ -64,27 +65,27 @@ func (el *GlobalConfigList) GetListMeta() metav1.List { // resources and ugorji. If/when these issues are resolved, the code below // should no longer be required. -type GlobalConfigListCopy GlobalConfigList -type GlobalConfigCopy GlobalConfig +type GlobalFelixConfigListCopy GlobalFelixConfigList +type GlobalFelixConfigCopy GlobalFelixConfig -func (g *GlobalConfig) UnmarshalJSON(data []byte) error { - tmp := GlobalConfigCopy{} +func (g *GlobalFelixConfig) UnmarshalJSON(data []byte) error { + tmp := GlobalFelixConfigCopy{} err := json.Unmarshal(data, &tmp) if err != nil { return err } - tmp2 := GlobalConfig(tmp) + tmp2 := GlobalFelixConfig(tmp) *g = tmp2 return nil } -func (l *GlobalConfigList) UnmarshalJSON(data []byte) error { - tmp := GlobalConfigListCopy{} +func (l *GlobalFelixConfigList) UnmarshalJSON(data []byte) error { + tmp := GlobalFelixConfigListCopy{} err := json.Unmarshal(data, &tmp) if err != nil { return err } - tmp2 := GlobalConfigList(tmp) + tmp2 := GlobalFelixConfigList(tmp) *l = tmp2 return nil } diff --git a/lib/backend/k8s/thirdparty/systemnetworkpolicy.go b/lib/backend/k8s/custom/globalnetworkpolicy.go similarity index 67% rename from lib/backend/k8s/thirdparty/systemnetworkpolicy.go rename to lib/backend/k8s/custom/globalnetworkpolicy.go index 326961853..c1b07bcab 100644 --- a/lib/backend/k8s/thirdparty/systemnetworkpolicy.go +++ b/lib/backend/k8s/custom/globalnetworkpolicy.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package thirdparty +package custom import ( "encoding/json" @@ -22,38 +22,38 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" ) -// SystemNetworkPolicy is the ThirdPartyResource definition of a Calico Policy resource in +// GlobalNetworkPolicy is the CustomResourceDefinition of a Calico Policy resource in // the Kubernetes API. -type SystemNetworkPolicy struct { +type GlobalNetworkPolicy struct { metav1.TypeMeta `json:",inline"` Metadata metav1.ObjectMeta `json:"metadata"` Spec api.PolicySpec `json:"spec"` } -// SystemNetworkPolicyList is a list of SystemNetworkPolicy resources. -type SystemNetworkPolicyList struct { +// GlobalNetworkPolicyList is a list of GlobalNetworkPolicy resources. +type GlobalNetworkPolicyList struct { metav1.TypeMeta `json:",inline"` Metadata metav1.ListMeta `json:"metadata"` - Items []SystemNetworkPolicy `json:"items"` + Items []GlobalNetworkPolicy `json:"items"` } // GetObjectKind returns the kind of this object. Required to satisfy Object interface -func (e *SystemNetworkPolicy) GetObjectKind() schema.ObjectKind { +func (e *GlobalNetworkPolicy) GetObjectKind() schema.ObjectKind { return &e.TypeMeta } // GetOjbectMeta returns the object metadata of this object. Required to satisfy ObjectMetaAccessor interface -func (e *SystemNetworkPolicy) GetObjectMeta() metav1.Object { +func (e *GlobalNetworkPolicy) GetObjectMeta() metav1.Object { return &e.Metadata } // GetObjectKind returns the kind of this object. Required to satisfy Object interface -func (el *SystemNetworkPolicyList) GetObjectKind() schema.ObjectKind { +func (el *GlobalNetworkPolicyList) GetObjectKind() schema.ObjectKind { return &el.TypeMeta } // GetListMeta returns the list metadata of this object. Required to satisfy ListMetaAccessor interface -func (el *SystemNetworkPolicyList) GetListMeta() metav1.List { +func (el *GlobalNetworkPolicyList) GetListMeta() metav1.List { return &el.Metadata } @@ -61,27 +61,27 @@ func (el *SystemNetworkPolicyList) GetListMeta() metav1.List { // resources and ugorji. If/when these issues are resolved, the code below // should no longer be required. -type SystemNetworkPolicyListCopy SystemNetworkPolicyList -type SystemNetworkPolicyCopy SystemNetworkPolicy +type GlobalNetworkPolicyListCopy GlobalNetworkPolicyList +type GlobalNetworkPolicyCopy GlobalNetworkPolicy -func (g *SystemNetworkPolicy) UnmarshalJSON(data []byte) error { - tmp := SystemNetworkPolicyCopy{} +func (g *GlobalNetworkPolicy) UnmarshalJSON(data []byte) error { + tmp := GlobalNetworkPolicyCopy{} err := json.Unmarshal(data, &tmp) if err != nil { return err } - tmp2 := SystemNetworkPolicy(tmp) + tmp2 := GlobalNetworkPolicy(tmp) *g = tmp2 return nil } -func (l *SystemNetworkPolicyList) UnmarshalJSON(data []byte) error { - tmp := SystemNetworkPolicyListCopy{} +func (l *GlobalNetworkPolicyList) UnmarshalJSON(data []byte) error { + tmp := GlobalNetworkPolicyListCopy{} err := json.Unmarshal(data, &tmp) if err != nil { return err } - tmp2 := SystemNetworkPolicyList(tmp) + tmp2 := GlobalNetworkPolicyList(tmp) *l = tmp2 return nil } diff --git a/lib/backend/k8s/thirdparty/ippool.go b/lib/backend/k8s/custom/ippool.go similarity index 64% rename from lib/backend/k8s/thirdparty/ippool.go rename to lib/backend/k8s/custom/ippool.go index a31bfefed..d08267e48 100644 --- a/lib/backend/k8s/thirdparty/ippool.go +++ b/lib/backend/k8s/custom/ippool.go @@ -12,55 +12,53 @@ // See the License for the specific language governing permissions and // limitations under the License. -package thirdparty +package custom import ( "encoding/json" + "github.com/projectcalico/libcalico-go/lib/api" + "github.com/projectcalico/libcalico-go/lib/net" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" ) -// IpPoolSpec is the specification of an IP Pool as represented in the Kubernetes -// ThirdPartyResource API. -type IpPoolSpec struct { - // Value is a json encoded string which can be unmarshalled into a model.IPPool struct. - Value string `json:"value"` -} - -// IpPool is the ThirdPartyResource definition of an IPPool in the Kubernetes API. -type IpPool struct { +// IPPool is the CustomResourceDefinition definition of an IPPool in the Kubernetes API. +type IPPool struct { metav1.TypeMeta `json:",inline"` Metadata metav1.ObjectMeta `json:"metadata"` + Spec IPPoolSpec `json:"spec"` +} - Spec IpPoolSpec `json:"spec"` +type IPPoolSpec struct { + api.IPPoolSpec + CIDR net.IPNet `json:"cidr"` } -// IpPoolList is a list of IpPool resources. -type IpPoolList struct { +// IPPoolList is a list of IPPool resources. +type IPPoolList struct { metav1.TypeMeta `json:",inline"` Metadata metav1.ListMeta `json:"metadata"` - - Items []IpPool `json:"items"` + Items []IPPool `json:"items"` } // GetObjectKind returns the kind of this object. Required to satisfy Object interface -func (e *IpPool) GetObjectKind() schema.ObjectKind { +func (e *IPPool) GetObjectKind() schema.ObjectKind { return &e.TypeMeta } // GetOjbectMeta returns the object metadata of this object. Required to satisfy ObjectMetaAccessor interface -func (e *IpPool) GetObjectMeta() metav1.Object { +func (e *IPPool) GetObjectMeta() metav1.Object { return &e.Metadata } // GetObjectKind returns the kind of this object. Required to satisfy Object interface -func (el *IpPoolList) GetObjectKind() schema.ObjectKind { +func (el *IPPoolList) GetObjectKind() schema.ObjectKind { return &el.TypeMeta } // GetListMeta returns the list metadata of this object. Required to satisfy ListMetaAccessor interface -func (el *IpPoolList) GetListMeta() metav1.List { +func (el *IPPoolList) GetListMeta() metav1.List { return &el.Metadata } @@ -68,27 +66,27 @@ func (el *IpPoolList) GetListMeta() metav1.List { // resources and ugorji. If/when these issues are resolved, the code below // should no longer be required. -type IpPoolListCopy IpPoolList -type IpPoolCopy IpPool +type IPPoolListCopy IPPoolList +type IPPoolCopy IPPool -func (g *IpPool) UnmarshalJSON(data []byte) error { - tmp := IpPoolCopy{} +func (g *IPPool) UnmarshalJSON(data []byte) error { + tmp := IPPoolCopy{} err := json.Unmarshal(data, &tmp) if err != nil { return err } - tmp2 := IpPool(tmp) + tmp2 := IPPool(tmp) *g = tmp2 return nil } -func (l *IpPoolList) UnmarshalJSON(data []byte) error { - tmp := IpPoolListCopy{} +func (l *IPPoolList) UnmarshalJSON(data []byte) error { + tmp := IPPoolListCopy{} err := json.Unmarshal(data, &tmp) if err != nil { return err } - tmp2 := IpPoolList(tmp) + tmp2 := IPPoolList(tmp) *l = tmp2 return nil } diff --git a/lib/backend/k8s/k8s.go b/lib/backend/k8s/k8s.go index 30248b96d..ced79b22f 100644 --- a/lib/backend/k8s/k8s.go +++ b/lib/backend/k8s/k8s.go @@ -26,8 +26,8 @@ import ( capi "github.com/projectcalico/libcalico-go/lib/api" "github.com/projectcalico/libcalico-go/lib/backend/api" + "github.com/projectcalico/libcalico-go/lib/backend/k8s/custom" "github.com/projectcalico/libcalico-go/lib/backend/k8s/resources" - "github.com/projectcalico/libcalico-go/lib/backend/k8s/thirdparty" "github.com/projectcalico/libcalico-go/lib/backend/model" "github.com/projectcalico/libcalico-go/lib/errors" "github.com/projectcalico/libcalico-go/lib/net" @@ -49,9 +49,8 @@ type KubeClient struct { // Main Kubernetes clients. clientSet *kubernetes.Clientset - // Client for interacting with ThirdPartyResources. - tprClientV1 *rest.RESTClient - tprClientV1alpha *rest.RESTClient + // Client for interacting with CustomResourceDefinition. + crdClientV1 *rest.RESTClient disableNodePoll bool @@ -60,15 +59,15 @@ type KubeClient struct { converter converter // Clients for interacting with Calico resources. - globalBgpPeerClient resources.K8sResourceClient - nodeBgpPeerClient resources.K8sResourceClient - globalBgpConfigClient resources.K8sResourceClient - nodeBgpConfigClient resources.K8sResourceClient - globalConfigClient resources.K8sResourceClient - nodeConfigClient resources.K8sResourceClient - ipPoolClient resources.K8sResourceClient - snpClient resources.K8sResourceClient - nodeClient resources.K8sResourceClient + globalBgpPeerClient resources.K8sResourceClient + nodeBgpPeerClient resources.K8sResourceClient + globalBgpConfigClient resources.K8sResourceClient + nodeBgpConfigClient resources.K8sResourceClient + globalFelixConfigClient resources.K8sResourceClient + nodeConfigClient resources.K8sResourceClient + ipPoolClient resources.K8sResourceClient + gnpClient resources.K8sResourceClient + nodeClient resources.K8sResourceClient } func NewKubeClient(kc *capi.KubeConfig) (*KubeClient, error) { @@ -119,46 +118,34 @@ func NewKubeClient(kc *capi.KubeConfig) (*KubeClient, error) { } log.Debugf("Created k8s clientSet: %+v", cs) - tprClientV1, err := buildTPRClientV1(*config) + crdClientV1, err := buildCRDClientV1(*config) if err != nil { - return nil, fmt.Errorf("Failed to build V1 TPR client: %s", err) - } - tprClientV1alpha, err := buildTPRClientV1alpha(*config) - if err != nil { - return nil, fmt.Errorf("Failed to build V1alpha TPR client: %s", err) + return nil, fmt.Errorf("Failed to build V1 CRD client: %s", err) } + kubeClient := &KubeClient{ - clientSet: cs, - tprClientV1: tprClientV1, - tprClientV1alpha: tprClientV1alpha, - disableNodePoll: kc.K8sDisableNodePoll, + clientSet: cs, + crdClientV1: crdClientV1, + disableNodePoll: kc.K8sDisableNodePoll, } // Create the Calico sub-clients. - kubeClient.ipPoolClient = resources.NewIPPoolClient(cs, tprClientV1) - kubeClient.nodeClient = resources.NewNodeClient(cs, tprClientV1) - kubeClient.snpClient = resources.NewSystemNetworkPolicyClient(cs, tprClientV1alpha) - kubeClient.globalBgpPeerClient = resources.NewGlobalBGPPeerClient(cs, tprClientV1) + kubeClient.ipPoolClient = resources.NewIPPoolClient(cs, crdClientV1) + kubeClient.nodeClient = resources.NewNodeClient(cs, crdClientV1) + kubeClient.gnpClient = resources.NewGlobalNetworkPolicyClient(cs, crdClientV1) + kubeClient.globalBgpPeerClient = resources.NewGlobalBGPPeerClient(cs, crdClientV1) kubeClient.nodeBgpPeerClient = resources.NewNodeBGPPeerClient(cs) - kubeClient.globalBgpConfigClient = resources.NewGlobalBGPConfigClient(cs, tprClientV1) + kubeClient.globalBgpConfigClient = resources.NewGlobalBGPConfigClient(cs, crdClientV1) kubeClient.nodeBgpConfigClient = resources.NewNodeBGPConfigClient(cs) - kubeClient.globalConfigClient = resources.NewGlobalConfigClient(cs, tprClientV1) + kubeClient.globalFelixConfigClient = resources.NewGlobalFelixConfigClient(cs, crdClientV1) return kubeClient, nil } func (c *KubeClient) EnsureInitialized() error { - // Ensure the necessary ThirdPartyResources exist in the API. - log.Info("Ensuring ThirdPartyResources exist") - err := c.ensureThirdPartyResources() - if err != nil { - return fmt.Errorf("Failed to ensure ThirdPartyResources exist: %s", err) - } - log.Info("ThirdPartyResources exist") - // Ensure ClusterType is set. log.Info("Ensuring ClusterType is set") - err = c.waitForClusterType() + err := c.waitForClusterType() if err != nil { return fmt.Errorf("Failed to ensure ClusterType is set: %s", err) } @@ -171,42 +158,7 @@ func (c *KubeClient) EnsureCalicoNodeInitialized(node string) error { return nil } -// ensureThirdPartyResources ensures the necessary thirdparty resources are created -// and will retry every second for 30 seconds or until they exist. -func (c *KubeClient) ensureThirdPartyResources() error { - return wait.PollImmediate(1*time.Second, 30*time.Second, func() (bool, error) { - if err := c.createThirdPartyResources(); err != nil { - return false, err - } - return true, nil - }) -} - -// createThirdPartyResources creates the necessary third party resources if they -// do not already exist. -func (c *KubeClient) createThirdPartyResources() error { - // We can check registration of the different custom resources in - // parallel. - done := make(chan error) - go func() { done <- c.ipPoolClient.EnsureInitialized() }() - go func() { done <- c.snpClient.EnsureInitialized() }() - go func() { done <- c.globalBgpPeerClient.EnsureInitialized() }() - go func() { done <- c.globalConfigClient.EnsureInitialized() }() - go func() { done <- c.globalBgpConfigClient.EnsureInitialized() }() - - // Wait for all registrations to complete and keep track of the last - // error to return. - var lastErr error - for i := 0; i < 5; i++ { - if err := <-done; err != nil { - log.WithError(err).Error("Hit error initializing TPR") - lastErr = err - } - } - return lastErr -} - -// waitForClusterType polls until GlobalConfig is ready, or until 30 seconds have passed. +// waitForClusterType polls until GlobalFelixConfig is ready, or until 30 seconds have passed. func (c *KubeClient) waitForClusterType() error { return wait.PollImmediate(1*time.Second, 30*time.Second, func() (bool, error) { return c.ensureClusterType() @@ -230,17 +182,20 @@ func (c *KubeClient) ensureClusterType() (bool, error) { } // Resource does not exist. } + rv := "" if ct != nil { existingValue := ct.Value.(string) if !strings.Contains(existingValue, "KDD") { existingValue = fmt.Sprintf("%s,KDD", existingValue) } value = existingValue + rv = ct.Revision.(string) } log.WithField("value", value).Debug("Setting ClusterType") _, err = c.Apply(&model.KVPair{ - Key: k, - Value: value, + Key: k, + Value: value, + Revision: rv, }) if err != nil { // Don't return an error, but indicate that we need @@ -251,11 +206,11 @@ func (c *KubeClient) ensureClusterType() (bool, error) { return true, nil } -// buildTPRClientV1 builds a RESTClient configured to interact with Calico ThirdPartyResources -func buildTPRClientV1(cfg rest.Config) (*rest.RESTClient, error) { +// buildCRDClientV1 builds a RESTClient configured to interact with Calico CustomResourceDefinitions +func buildCRDClientV1(cfg rest.Config) (*rest.RESTClient, error) { // Generate config using the base config. cfg.GroupVersion = &schema.GroupVersion{ - Group: "projectcalico.org", + Group: "crd.projectcalico.org", Version: "v1", } cfg.APIPath = "/apis" @@ -272,43 +227,14 @@ func buildTPRClientV1(cfg rest.Config) (*rest.RESTClient, error) { func(scheme *runtime.Scheme) error { scheme.AddKnownTypes( *cfg.GroupVersion, - &thirdparty.GlobalConfig{}, - &thirdparty.GlobalConfigList{}, - &thirdparty.IpPool{}, - &thirdparty.IpPoolList{}, - &thirdparty.GlobalBgpPeer{}, - &thirdparty.GlobalBgpPeerList{}, - ) - return nil - }) - schemeBuilder.AddToScheme(clientapi.Scheme) - - return cli, nil -} - -// buildTPRClientV1alpha builds a RESTClient configured to interact with Calico ThirdPartyResources -func buildTPRClientV1alpha(cfg rest.Config) (*rest.RESTClient, error) { - // Generate config using the base config. - cfg.GroupVersion = &schema.GroupVersion{ - Group: "alpha.projectcalico.org", - Version: "v1", - } - cfg.APIPath = "/apis" - cfg.ContentType = runtime.ContentTypeJSON - cfg.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: clientapi.Codecs} - - cli, err := rest.RESTClientFor(&cfg) - if err != nil { - return nil, err - } - - // We also need to register resources. - schemeBuilder := runtime.NewSchemeBuilder( - func(scheme *runtime.Scheme) error { - scheme.AddKnownTypes( - *cfg.GroupVersion, - &thirdparty.SystemNetworkPolicy{}, - &thirdparty.SystemNetworkPolicyList{}, + &custom.GlobalFelixConfig{}, + &custom.GlobalFelixConfigList{}, + &custom.IPPool{}, + &custom.IPPoolList{}, + &custom.BGPPeer{}, + &custom.BGPPeerList{}, + &custom.GlobalNetworkPolicy{}, + &custom.GlobalNetworkPolicyList{}, ) return nil }) @@ -326,7 +252,7 @@ func (c *KubeClient) Create(d *model.KVPair) (*model.KVPair, error) { log.Debugf("Performing 'Create' for %+v", d) switch d.Key.(type) { case model.GlobalConfigKey: - return c.globalConfigClient.Create(d) + return c.globalFelixConfigClient.Create(d) case model.IPPoolKey: return c.ipPoolClient.Create(d) case model.NodeKey: @@ -354,7 +280,7 @@ func (c *KubeClient) Update(d *model.KVPair) (*model.KVPair, error) { log.Debugf("Performing 'Update' for %+v", d) switch d.Key.(type) { case model.GlobalConfigKey: - return c.globalConfigClient.Update(d) + return c.globalFelixConfigClient.Update(d) case model.IPPoolKey: return c.ipPoolClient.Update(d) case model.NodeKey: @@ -384,7 +310,7 @@ func (c *KubeClient) Apply(d *model.KVPair) (*model.KVPair, error) { case model.WorkloadEndpointKey: return c.applyWorkloadEndpoint(d) case model.GlobalConfigKey: - return c.globalConfigClient.Apply(d) + return c.globalFelixConfigClient.Apply(d) case model.IPPoolKey: return c.ipPoolClient.Apply(d) case model.NodeKey: @@ -417,7 +343,7 @@ func (c *KubeClient) Delete(d *model.KVPair) error { log.Debugf("Performing 'Delete' for %+v", d) switch d.Key.(type) { case model.GlobalConfigKey: - return c.globalConfigClient.Delete(d) + return c.globalFelixConfigClient.Delete(d) case model.IPPoolKey: return c.ipPoolClient.Delete(d) case model.NodeKey: @@ -452,7 +378,7 @@ func (c *KubeClient) Get(k model.Key) (*model.KVPair, error) { case model.HostConfigKey: return c.getHostConfig(k.(model.HostConfigKey)) case model.GlobalConfigKey: - return c.globalConfigClient.Get(k) + return c.globalFelixConfigClient.Get(k) case model.ReadyFlagKey: return c.getReadyStatus(k.(model.ReadyFlagKey)) case model.IPPoolKey: @@ -501,7 +427,7 @@ func (c *KubeClient) List(l model.ListInterface) ([]*model.KVPair, error) { k, _, err := c.nodeBgpPeerClient.List(l) return k, err case model.GlobalConfigListOptions: - k, _, err := c.globalConfigClient.List(l) + k, _, err := c.globalFelixConfigClient.List(l) return k, err case model.GlobalBGPConfigListOptions: k, _, err := c.globalBgpConfigClient.List(l) @@ -687,12 +613,12 @@ func (c *KubeClient) listPolicies(l model.PolicyListOptions) ([]*model.KVPair, e ret = append(ret, kvp) } - // List all System Network Policies. - snps, _, err := c.snpClient.List(l) + // List all Global Network Policies. + gnps, _, err := c.gnpClient.List(l) if err != nil { return nil, err } - ret = append(ret, snps...) + ret = append(ret, gnps...) return ret, nil } @@ -703,8 +629,8 @@ func (c *KubeClient) getPolicy(k model.PolicyKey) (*model.KVPair, error) { return nil, goerrors.New("Missing policy name") } - // Check to see if this is backed by a NetworkPolicy or a Namespace. - if strings.HasPrefix(k.Name, "np.projectcalico.org/") { + // Check to see if this is backed by a NetworkPolicy. + if strings.HasPrefix(k.Name, "knp.default.") { // Backed by a NetworkPolicy. Parse out the namespace / name. namespace, policyName, err := c.converter.parsePolicyNameNetworkPolicy(k.Name) if err != nil { @@ -724,12 +650,9 @@ func (c *KubeClient) getPolicy(k model.PolicyKey) (*model.KVPair, error) { return nil, resources.K8sErrorToCalico(err, k) } return c.converter.networkPolicyToPolicy(&networkPolicy) - } else if strings.HasPrefix(k.Name, resources.SystemNetworkPolicyNamePrefix) { - // This is backed by a System Network Policy TPR. - return c.snpClient.Get(k) } else { - // Received a Get() for a Policy that doesn't exist. - return nil, errors.ErrorResourceDoesNotExist{Identifier: k} + // This is backed by a Global Network Policy CRD. + return c.gnpClient.Get(k) } } diff --git a/lib/backend/k8s/k8s_fv_test.go b/lib/backend/k8s/k8s_fv_test.go index 001682195..58849bec3 100644 --- a/lib/backend/k8s/k8s_fv_test.go +++ b/lib/backend/k8s/k8s_fv_test.go @@ -251,9 +251,7 @@ func CreateClientAndSyncer(cfg capi.KubeConfig) (*KubeClient, *cb, api.Syncer) { // Ensure the backend is initialized. err = c.EnsureInitialized() - if err != nil { - panic(err) - } + Expect(err).NotTo(HaveOccurred(), "Failed to initialize the backend.") // Start the syncer. updateChan := make(chan api.Update) @@ -281,34 +279,11 @@ var _ = Describe("Test Syncer API for Kubernetes backend", func() { cfg := capi.KubeConfig{K8sAPIEndpoint: "http://localhost:8080"} c, cb, syncer = CreateClientAndSyncer(cfg) - // Create a Node in the API to be used by the tests. - n := k8sapi.Node{ - ObjectMeta: metav1.ObjectMeta{Name: "127.0.0.1"}, - Spec: k8sapi.NodeSpec{PodCIDR: "10.10.10.0/24"}, - Status: k8sapi.NodeStatus{ - Addresses: []k8sapi.NodeAddress{ - { - Type: k8sapi.NodeInternalIP, - Address: "127.0.0.1/32", - }, - { - Type: k8sapi.NodeExternalIP, - Address: "5.6.7.8/32", - }, - }, - }, - } - - // Delete the node to ensure a clean start. - c.clientSet.Nodes().Delete(n.Name, &metav1.DeleteOptions{}) - - // Re-create the node. - _, err := c.clientSet.Nodes().Create(&n) - Expect(err).NotTo(HaveOccurred(), "Failed to create node in k8s API for test") - // Start the syncer. syncer.Start() + // Node object is created by applying the mock-node.yaml manifest in advance. + // Start processing updates. go cb.ProcessUpdates() }) @@ -480,7 +455,7 @@ var _ = Describe("Test Syncer API for Kubernetes backend", func() { Expect(err).NotTo(HaveOccurred()) // Perform a Get and ensure no error in the Calico API. - _, err = c.Get(model.PolicyKey{Name: fmt.Sprintf("np.projectcalico.org/default.%s", np.ObjectMeta.Name)}) + _, err = c.Get(model.PolicyKey{Name: fmt.Sprintf("knp.default.default.%s", np.ObjectMeta.Name)}) Expect(err).NotTo(HaveOccurred()) }) @@ -516,28 +491,25 @@ var _ = Describe("Test Syncer API for Kubernetes backend", func() { }) }() - It("should handle a CRUD of System Network Policy", func() { - // In the backend, the Policy name is prepended to indicate where - // the policy is derived from in KDD. The System Network Policy - // is a TPR and in the backend the name is prepended with - // "snp.projectcalico.org/". The SNP CRUD operations assume that - // the name is of the correct format (it's up to the calling code - // to fan-out Policy CRUD operations to the appropriate KDD client - // based on the prefix). - kvp1Name := "snp.projectcalico.org/my-test-snp" + It("should handle a CRUD of Global Network Policy", func() { + + kvp1Name := "my-test-gnp" kvp1a := &model.KVPair{ Key: model.PolicyKey{Name: kvp1Name}, Value: &calicoAllowPolicyModel, } + kvp1b := &model.KVPair{ Key: model.PolicyKey{Name: kvp1Name}, Value: &calicoDisallowPolicyModel, } - kvp2Name := "snp.projectcalico.org/my-test-snp2" + + kvp2Name := "my-test-gnp2" kvp2a := &model.KVPair{ Key: model.PolicyKey{Name: kvp2Name}, Value: &calicoAllowPolicyModel, } + kvp2b := &model.KVPair{ Key: model.PolicyKey{Name: kvp2Name}, Value: &calicoDisallowPolicyModel, @@ -546,110 +518,110 @@ var _ = Describe("Test Syncer API for Kubernetes backend", func() { // Make sure we clean up after ourselves. We allow this to fail because // part of our explicit testing below is to delete the resource. defer func() { - c.snpClient.Delete(kvp1a) - c.snpClient.Delete(kvp2a) + c.gnpClient.Delete(kvp1a) + c.gnpClient.Delete(kvp2a) }() - // Check our syncer has the correct SNP entries for the two + // Check our syncer has the correct GNP entries for the two // System Network Protocols that this test manipulates. Neither // have been created yet. - By("Checking cache does not have System Network Policy entries", func() { + By("Checking cache does not have Global Network Policy entries", func() { Eventually(cb.GetSyncerValuePresentFunc(kvp1a.Key)).Should(BeFalse()) Eventually(cb.GetSyncerValuePresentFunc(kvp2a.Key)).Should(BeFalse()) }) - By("Creating a System Network Policy", func() { - _, err := c.snpClient.Create(kvp1a) + By("Creating a Global Network Policy", func() { + _, err := c.gnpClient.Create(kvp1a) Expect(err).NotTo(HaveOccurred()) }) - By("Checking cache has correct System Network Policy entries", func() { + By("Checking cache has correct Global Network Policy entries", func() { Eventually(cb.GetSyncerValueFunc(kvp1a.Key)).Should(Equal(kvp1a.Value)) Eventually(cb.GetSyncerValuePresentFunc(kvp2a.Key)).Should(BeFalse()) }) - By("Attempting to recreate an existing System Network Policy", func() { - _, err := c.snpClient.Create(kvp1a) + By("Attempting to recreate an existing Global Network Policy", func() { + _, err := c.gnpClient.Create(kvp1a) Expect(err).To(HaveOccurred()) }) - By("Updating an existing System Network Policy", func() { - _, err := c.snpClient.Update(kvp1b) + By("Updating an existing Global Network Policy", func() { + _, err := c.gnpClient.Update(kvp1b) Expect(err).NotTo(HaveOccurred()) }) - By("Checking cache has correct System Network Policy entries", func() { + By("Checking cache has correct Global Network Policy entries", func() { Eventually(cb.GetSyncerValueFunc(kvp1a.Key)).Should(Equal(kvp1b.Value)) Eventually(cb.GetSyncerValuePresentFunc(kvp2a.Key)).Should(BeFalse()) }) - By("Applying a non-existent System Network Policy", func() { - _, err := c.snpClient.Apply(kvp2a) + By("Applying a non-existent Global Network Policy", func() { + _, err := c.gnpClient.Apply(kvp2a) Expect(err).NotTo(HaveOccurred()) }) - By("Checking cache has correct System Network Policy entries", func() { + By("Checking cache has correct Global Network Policy entries", func() { Eventually(cb.GetSyncerValueFunc(kvp1a.Key)).Should(Equal(kvp1b.Value)) Eventually(cb.GetSyncerValueFunc(kvp2a.Key)).Should(Equal(kvp2a.Value)) }) - By("Updating the System Network Policy created by Apply", func() { - _, err := c.snpClient.Apply(kvp2b) + By("Updating the Global Network Policy created by Apply", func() { + _, err := c.gnpClient.Apply(kvp2b) Expect(err).NotTo(HaveOccurred()) }) - By("Checking cache has correct System Network Policy entries", func() { + By("Checking cache has correct Global Network Policy entries", func() { Eventually(cb.GetSyncerValueFunc(kvp1a.Key)).Should(Equal(kvp1b.Value)) Eventually(cb.GetSyncerValueFunc(kvp2a.Key)).Should(Equal(kvp2b.Value)) }) - By("Deleted the System Network Policy created by Apply", func() { - err := c.snpClient.Delete(kvp2a) + By("Deleted the Global Network Policy created by Apply", func() { + err := c.gnpClient.Delete(kvp2a) Expect(err).NotTo(HaveOccurred()) }) - By("Checking cache has correct System Network Policy entries", func() { + By("Checking cache has correct Global Network Policy entries", func() { Eventually(cb.GetSyncerValueFunc(kvp1a.Key)).Should(Equal(kvp1b.Value)) Eventually(cb.GetSyncerValuePresentFunc(kvp2a.Key)).Should(BeFalse()) }) // Perform Get operations directly on the main client - this // will fan out requests to the appropriate Policy client - // (including the System Network Policy client). - By("Getting a missing System Network Policy", func() { - _, err := c.Get(model.PolicyKey{Name: "my-test-snp"}) + // (including the Global Network Policy client). + By("Getting a Global Network Policy that does noe exist", func() { + _, err := c.Get(model.PolicyKey{Name: "my-non-existent-test-gnp"}) Expect(err).To(HaveOccurred()) }) - By("Listing a missing System Network Policy", func() { - kvps, err := c.List(model.PolicyListOptions{Name: "my-test-snp"}) + By("Listing a missing Global Network Policy", func() { + kvps, err := c.List(model.PolicyListOptions{Name: "my-non-existent-test-gnp"}) Expect(err).ToNot(HaveOccurred()) Expect(kvps).To(HaveLen(0)) }) - By("Getting an existing System Network Policy", func() { - kvp, err := c.Get(model.PolicyKey{Name: "snp.projectcalico.org/my-test-snp"}) + By("Getting an existing Global Network Policy", func() { + kvp, err := c.Get(model.PolicyKey{Name: "my-test-gnp"}) Expect(err).ToNot(HaveOccurred()) - Expect(kvp.Key.(model.PolicyKey).Name).To(Equal("snp.projectcalico.org/my-test-snp")) + Expect(kvp.Key.(model.PolicyKey).Name).To(Equal("my-test-gnp")) Expect(kvp.Value.(*model.Policy)).To(Equal(kvp1b.Value)) }) - By("Listing all policies (including a System Network Policy)", func() { + By("Listing all policies (including a Global Network Policy)", func() { // We expect namespace entries for kube-system, kube-public // and default. kvps, err := c.List(model.PolicyListOptions{}) Expect(err).ToNot(HaveOccurred()) Expect(kvps).To(HaveLen(1)) - Expect(kvps[len(kvps)-1].Key.(model.PolicyKey).Name).To(Equal("snp.projectcalico.org/my-test-snp")) + Expect(kvps[len(kvps)-1].Key.(model.PolicyKey).Name).To(Equal("my-test-gnp")) Expect(kvps[len(kvps)-1].Value.(*model.Policy)).To(Equal(kvp1b.Value)) }) - By("Deleting an existing System Network Policy", func() { - err := c.snpClient.Delete(kvp1a) + By("Deleting an existing Global Network Policy", func() { + err := c.gnpClient.Delete(kvp1a) Expect(err).NotTo(HaveOccurred()) }) - By("Checking cache has no System Network Policy entries", func() { + By("Checking cache has no Global Network Policy entries", func() { Eventually(cb.GetSyncerValuePresentFunc(kvp1a.Key)).Should(BeFalse()) Eventually(cb.GetSyncerValuePresentFunc(kvp2a.Key)).Should(BeFalse()) }) @@ -665,6 +637,7 @@ var _ = Describe("Test Syncer API for Kubernetes backend", func() { ASNum: numorstring.ASNumber(6512), }, } + kvp1b := &model.KVPair{ Key: model.GlobalBGPPeerKey{ PeerIP: cnet.MustParseIP("10.0.0.1"), @@ -674,6 +647,7 @@ var _ = Describe("Test Syncer API for Kubernetes backend", func() { ASNum: numorstring.ASNumber(6513), }, } + kvp2a := &model.KVPair{ Key: model.GlobalBGPPeerKey{ PeerIP: cnet.MustParseIP("aa:bb::cc"), @@ -683,6 +657,7 @@ var _ = Describe("Test Syncer API for Kubernetes backend", func() { ASNum: numorstring.ASNumber(6514), }, } + kvp2b := &model.KVPair{ Key: model.GlobalBGPPeerKey{ PeerIP: cnet.MustParseIP("aa:bb::cc"), @@ -747,10 +722,17 @@ var _ = Describe("Test Syncer API for Kubernetes backend", func() { kvps, err := c.List(model.GlobalBGPPeerListOptions{}) Expect(err).ToNot(HaveOccurred()) Expect(kvps).To(HaveLen(2)) - Expect(kvps[0].Key).To(Equal(kvp1b.Key)) - Expect(kvps[0].Value).To(Equal(kvp1b.Value)) - Expect(kvps[1].Key).To(Equal(kvp2b.Key)) - Expect(kvps[01].Value).To(Equal(kvp2b.Value)) + keys := []model.Key{} + vals := []interface{}{} + for _, k := range kvps { + keys = append(keys, k.Key) + vals = append(vals, k.Value) + } + Expect(keys).To(ContainElement(kvp1b.Key)) + Expect(keys).To(ContainElement(kvp2b.Key)) + Expect(vals).To(ContainElement(kvp1b.Value)) + Expect(vals).To(ContainElement(kvp2b.Value)) + }) By("Deleting the Global BGP Peer created by Apply", func() { @@ -904,10 +886,16 @@ var _ = Describe("Test Syncer API for Kubernetes backend", func() { kvps, err := c.List(model.NodeBGPPeerListOptions{}) Expect(err).ToNot(HaveOccurred()) Expect(kvps).To(HaveLen(2)) - Expect(kvps[0].Key).To(Equal(kvp1b.Key)) - Expect(kvps[0].Value).To(Equal(kvp1b.Value)) - Expect(kvps[1].Key).To(Equal(kvp2b.Key)) - Expect(kvps[1].Value).To(Equal(kvp2b.Value)) + keys := []model.Key{} + vals := []interface{}{} + for _, k := range kvps { + keys = append(keys, k.Key) + vals = append(vals, k.Value) + } + Expect(keys).To(ContainElement(kvp1b.Key)) + Expect(keys).To(ContainElement(kvp2b.Key)) + Expect(vals).To(ContainElement(kvp1b.Value)) + Expect(vals).To(ContainElement(kvp2b.Value)) }) By("Deleting the Node BGP Peer created by Apply", func() { @@ -1080,7 +1068,7 @@ var _ = Describe("Test Syncer API for Kubernetes backend", func() { Expect(err).To(BeAssignableToTypeOf(errors.ErrorResourceDoesNotExist{})) }) - It("should support setting and getting GlobalConfig", func() { + It("should support setting and getting GlobalFelixConfig", func() { gc := &model.KVPair{ Key: model.GlobalConfigKey{ Name: "ClusterGUID", @@ -1196,7 +1184,7 @@ var _ = Describe("Test Syncer API for Kubernetes backend", func() { CIDR: *cidr, IPIPInterface: "tunl0", Masquerade: true, - IPAM: true, + IPAM: false, Disabled: true, }, } @@ -1208,7 +1196,7 @@ var _ = Describe("Test Syncer API for Kubernetes backend", func() { Expect(receivedPool.Value.(*model.IPPool).CIDR).To(Equal(*cidr)) Expect(receivedPool.Value.(*model.IPPool).IPIPInterface).To(Equal("tunl0")) Expect(receivedPool.Value.(*model.IPPool).Masquerade).To(Equal(true)) - Expect(receivedPool.Value.(*model.IPPool).IPAM).To(Equal(true)) + Expect(receivedPool.Value.(*model.IPPool).IPAM).To(Equal(false)) Expect(receivedPool.Value.(*model.IPPool).Disabled).To(Equal(true)) }) diff --git a/lib/backend/k8s/resources/customresource.go b/lib/backend/k8s/resources/customresource.go index 635196874..00d4cdfb1 100644 --- a/lib/backend/k8s/resources/customresource.go +++ b/lib/backend/k8s/resources/customresource.go @@ -25,7 +25,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" - extensions "k8s.io/client-go/pkg/apis/extensions/v1beta1" "k8s.io/client-go/rest" ) @@ -95,7 +94,6 @@ func (c *customK8sResourceClient) Create(kvp *model.KVPair) (*model.KVPair, erro resOut := reflect.New(c.k8sResourceType).Interface().(CustomK8sResource) err = c.restClient.Post(). Resource(c.resource). - Namespace("kube-system"). Body(resIn). Do().Into(resOut) if err != nil { @@ -117,32 +115,68 @@ func (c *customK8sResourceClient) Update(kvp *model.KVPair) (*model.KVPair, erro }) logContext.Debug("Update custom Kubernetes resource") - // Convert the KVPair to the K8s resource. - resIn, err := c.converter.FromKVPair(kvp) - if err != nil { - logContext.WithError(err).Info("Error updating resource") - return nil, err + // Create storage for the updated resource. + resOut := reflect.New(c.k8sResourceType).Interface().(CustomK8sResource) + + providedRV := "" + if kvp.Revision != nil { + if rv, ok := kvp.Revision.(string); ok { + providedRV = rv + } } - // Send the update request using the name. - resOut := reflect.New(c.k8sResourceType).Interface().(CustomK8sResource) - name := resIn.GetObjectMeta().GetName() - logContext = logContext.WithField("Name", name) - logContext.Debug("Update resource by name") - err = c.restClient.Put(). - Resource(c.resource). - Namespace("kube-system"). - Body(resIn). - Name(name). - Do().Into(resOut) - if err != nil { - logContext.WithError(err).Info("Error updating resource") - return nil, K8sErrorToCalico(err, kvp.Key) + var updateError error + for i := 0; i < 5; i++ { + // If no revision was passed, get the object to use its latest Revision number. + // If a revision was passed, then we should just use that. + if providedRV == "" { + logContext.Debug("Querying for resource version") + k, err := c.Get(kvp.Key) + if err != nil { + return nil, err + } + + if k.Revision != nil { + kvp.Revision = k.Revision.(string) + logContext.Debugf("Set resource version to %s", kvp.Revision) + } + } + + // Convert the KVPair to a K8s resource. + resIn, err := c.converter.FromKVPair(kvp) + if err != nil { + logContext.WithError(err).Info("Error updating resource") + return nil, err + } + + // Send the update request using the name. + name := resIn.GetObjectMeta().GetName() + logContext = logContext.WithField("Name", name) + logContext.Debug("Update resource by name") + updateError = c.restClient.Put(). + Resource(c.resource). + Body(resIn). + Name(name). + Do().Into(resOut) + if updateError == nil { + // Success. + // Update the revision information from the response. + kvp.Revision = resOut.GetObjectMeta().GetResourceVersion() + return kvp, nil + } else if _, ok := updateError.(errors.ErrorResourceUpdateConflict); ok && providedRV == "" { + // We only want to retry if there was no Revision provided with + // the KVP AND there was a CAS error while updating. + logContext.WithError(updateError).Warnf("Update failed for %s, retrying", kvp.Key.String()) + continue + } else { + // We don't retry for any other errors or if the revision number is provided. + break + } } - // Update the revision information from the response. - kvp.Revision = resOut.GetObjectMeta().GetResourceVersion() - return kvp, nil + // Failed to update the resource. + logContext.WithError(updateError).Error("Error updating resource") + return nil, K8sErrorToCalico(updateError, kvp.Key) } // Apply either creates a new Custom K8s Resource instance or updates an existing Custom K8s Resource @@ -155,19 +189,23 @@ func (c *customK8sResourceClient) Apply(kvp *model.KVPair) (*model.KVPair, error }) logContext.Debug("Apply custom Kubernetes resource") - // Attempt and Update and roll back to a Create if the resource does - // not exist. We only log debug here since the Update and Create will - // also log. - updated, err := c.Update(kvp) + // Attempt to Create and do an Update if the resource already exists. + // We only log debug here since the Create and Update will also log. + // Can't set Revision while creating a resource. + updated, err := c.Create(&model.KVPair{ + Key: kvp.Key, + Value: kvp.Value, + }) if err != nil { - if _, ok := err.(errors.ErrorResourceDoesNotExist); !ok { - logContext.Debug("Error applying resource (using Update)") + if _, ok := err.(errors.ErrorResourceAlreadyExists); !ok { + logContext.Debug("Error applying resource (using Create)") return nil, err } - updated, err = c.Create(kvp) + // Try to Update if the resource already exists. + updated, err = c.Update(kvp) if err != nil { - logContext.Debug("Error applying resource (using Create)") + logContext.Debug("Error applying resource (using Update)") return nil, err } } @@ -194,7 +232,6 @@ func (c *customK8sResourceClient) Delete(kvp *model.KVPair) error { logContext.Debug("Send delete request by name") err = c.restClient.Delete(). Resource(c.resource). - Namespace("kube-system"). Name(name). Do().Error() if err != nil { @@ -224,7 +261,6 @@ func (c *customK8sResourceClient) Get(key model.Key) (*model.KVPair, error) { resOut := reflect.New(c.k8sResourceType).Interface().(CustomK8sResource) err = c.restClient.Get(). Resource(c.resource). - Namespace("kube-system"). Name(name). Do().Into(resOut) if err != nil { @@ -272,7 +308,6 @@ func (c *customK8sResourceClient) List(list model.ListInterface) ([]*model.KVPai // Perform the request. err := c.restClient.Get(). Resource(c.resource). - Namespace("kube-system"). Do().Into(reslOut) if err != nil { // Don't return errors for "not found". This just @@ -301,26 +336,8 @@ func (c *customK8sResourceClient) List(list model.ListInterface) ([]*model.KVPai return kvps, reslOut.GetListMeta().GetResourceVersion(), nil } -// EnsureInitialized performs client initialization required for a specific -// Custom K8s Resource type. +// EnsureInitialized is a no-op since the CRD should be +// initialized in advance. func (c *customK8sResourceClient) EnsureInitialized() error { - logContext := log.WithField("Resource", c.resource) - logContext.Debug("Ensuring ThirdPartyResource exists") - res := extensions.ThirdPartyResource{ - ObjectMeta: metav1.ObjectMeta{ - Name: c.name, - Namespace: "kube-system", - }, - Description: c.description, - Versions: []extensions.APIVersion{{Name: "v1"}}, - } - _, err := c.clientSet.Extensions().ThirdPartyResources().Create(&res) - if err != nil { - // Don't care if it already exists. - if !kerrors.IsAlreadyExists(err) { - logContext.WithError(err).Info("Error initializing ThirdParyResource") - return K8sErrorToCalico(err, res) - } - } return nil } diff --git a/lib/backend/k8s/resources/globalbgpconfig.go b/lib/backend/k8s/resources/globalbgpconfig.go index 911668627..8f767bf16 100644 --- a/lib/backend/k8s/resources/globalbgpconfig.go +++ b/lib/backend/k8s/resources/globalbgpconfig.go @@ -19,7 +19,7 @@ import ( "reflect" "strings" - "github.com/projectcalico/libcalico-go/lib/backend/k8s/thirdparty" + "github.com/projectcalico/libcalico-go/lib/backend/k8s/custom" "github.com/projectcalico/libcalico-go/lib/backend/model" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -28,44 +28,44 @@ import ( ) const ( - GlobalBgpConfigResourceName = "GlobalBgpConfigs" - GlobalBgpConfigTPRName = "global-bgp-config.projectcalico.org" + GlobalBGPConfigResourceName = "GlobalBGPConfigs" + GlobalBGPConfigCRDName = "globalbgpconfigs.projectcalico.org" ) func NewGlobalBGPConfigClient(c *kubernetes.Clientset, r *rest.RESTClient) K8sResourceClient { return &customK8sResourceClient{ clientSet: c, restClient: r, - name: GlobalBgpConfigTPRName, - resource: GlobalBgpConfigResourceName, + name: GlobalBGPConfigCRDName, + resource: GlobalBGPConfigResourceName, description: "Calico Global BGP Configuration", - k8sResourceType: reflect.TypeOf(thirdparty.GlobalBgpConfig{}), - k8sListType: reflect.TypeOf(thirdparty.GlobalBgpConfigList{}), - converter: GlobalBgpConfigConverter{}, + k8sResourceType: reflect.TypeOf(custom.GlobalBGPConfig{}), + k8sListType: reflect.TypeOf(custom.GlobalBGPConfigList{}), + converter: GlobalBGPConfigConverter{}, } } -// GlobalBgpConfigConverter implements the K8sResourceConverter interface. -type GlobalBgpConfigConverter struct { +// GlobalBGPConfigConverter implements the K8sResourceConverter interface. +type GlobalBGPConfigConverter struct { } -func (_ GlobalBgpConfigConverter) ListInterfaceToKey(l model.ListInterface) model.Key { +func (_ GlobalBGPConfigConverter) ListInterfaceToKey(l model.ListInterface) model.Key { if name := l.(model.GlobalBGPConfigListOptions).Name; name != "" { return model.GlobalBGPConfigKey{Name: name} } return nil } -func (_ GlobalBgpConfigConverter) KeyToName(k model.Key) (string, error) { +func (_ GlobalBGPConfigConverter) KeyToName(k model.Key) (string, error) { return strings.ToLower(k.(model.GlobalBGPConfigKey).Name), nil } -func (_ GlobalBgpConfigConverter) NameToKey(name string) (model.Key, error) { +func (_ GlobalBGPConfigConverter) NameToKey(name string) (model.Key, error) { return nil, fmt.Errorf("Mapping of Name to Key is not possible for global BGP config") } -func (c GlobalBgpConfigConverter) ToKVPair(r CustomK8sResource) (*model.KVPair, error) { - t := r.(*thirdparty.GlobalBgpConfig) +func (c GlobalBGPConfigConverter) ToKVPair(r CustomK8sResource) (*model.KVPair, error) { + t := r.(*custom.GlobalBGPConfig) return &model.KVPair{ Key: model.GlobalBGPConfigKey{ Name: t.Spec.Name, @@ -75,22 +75,26 @@ func (c GlobalBgpConfigConverter) ToKVPair(r CustomK8sResource) (*model.KVPair, }, nil } -func (c GlobalBgpConfigConverter) FromKVPair(kvp *model.KVPair) (CustomK8sResource, error) { +func (c GlobalBGPConfigConverter) FromKVPair(kvp *model.KVPair) (CustomK8sResource, error) { name, err := c.KeyToName(kvp.Key) if err != nil { return nil, err } - tpr := thirdparty.GlobalBgpConfig{ + crd := custom.GlobalBGPConfig{ + TypeMeta: metav1.TypeMeta{ + Kind: "GlobalBGPConfig", + APIVersion: "crd.projectcalico.org/v1", + }, Metadata: metav1.ObjectMeta{ Name: name, }, - Spec: thirdparty.GlobalBgpConfigSpec{ + Spec: custom.GlobalBGPConfigSpec{ Name: kvp.Key.(model.GlobalBGPConfigKey).Name, Value: kvp.Value.(string), }, } if kvp.Revision != nil { - tpr.Metadata.ResourceVersion = kvp.Revision.(string) + crd.Metadata.ResourceVersion = kvp.Revision.(string) } - return &tpr, nil + return &crd, nil } diff --git a/lib/backend/k8s/resources/globalbgpconfig_test.go b/lib/backend/k8s/resources/globalbgpconfig_test.go index 69085b3d2..c4792ed0e 100644 --- a/lib/backend/k8s/resources/globalbgpconfig_test.go +++ b/lib/backend/k8s/resources/globalbgpconfig_test.go @@ -15,8 +15,8 @@ package resources_test import ( + "github.com/projectcalico/libcalico-go/lib/backend/k8s/custom" "github.com/projectcalico/libcalico-go/lib/backend/k8s/resources" - "github.com/projectcalico/libcalico-go/lib/backend/k8s/thirdparty" "github.com/projectcalico/libcalico-go/lib/backend/model" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -27,7 +27,7 @@ import ( var _ = Describe("Global BGP config conversion methods", func() { - converter := resources.GlobalBgpConfigConverter{} + converter := resources.GlobalBGPConfigConverter{} // Define some useful test data. listIncomplete := model.GlobalBGPConfigListOptions{} @@ -48,12 +48,12 @@ var _ = Describe("Global BGP config conversion methods", func() { Value: value1, Revision: "rv", } - res1 := &thirdparty.GlobalBgpConfig{ + res1 := &custom.GlobalBGPConfig{ Metadata: metav1.ObjectMeta{ Name: name1, ResourceVersion: "rv", }, - Spec: thirdparty.GlobalBgpConfigSpec{ + Spec: custom.GlobalBGPConfigSpec{ Name: key1.Name, Value: value1, }, @@ -83,8 +83,8 @@ var _ = Describe("Global BGP config conversion methods", func() { Expect(err).NotTo(HaveOccurred()) Expect(r.GetObjectMeta().GetName()).To(Equal(res1.Metadata.Name)) Expect(r.GetObjectMeta().GetResourceVersion()).To(Equal(res1.Metadata.ResourceVersion)) - Expect(r).To(BeAssignableToTypeOf(&thirdparty.GlobalBgpConfig{})) - Expect(r.(*thirdparty.GlobalBgpConfig).Spec).To(Equal(res1.Spec)) + Expect(r).To(BeAssignableToTypeOf(&custom.GlobalBGPConfig{})) + Expect(r.(*custom.GlobalBGPConfig).Spec).To(Equal(res1.Spec)) }) It("should convert between a Kubernetes resource and the equivalent KVPair", func() { diff --git a/lib/backend/k8s/resources/globalbgppeer.go b/lib/backend/k8s/resources/globalbgppeer.go index 090e2aef3..254662600 100644 --- a/lib/backend/k8s/resources/globalbgppeer.go +++ b/lib/backend/k8s/resources/globalbgppeer.go @@ -18,7 +18,7 @@ import ( "reflect" "github.com/projectcalico/libcalico-go/lib/api" - "github.com/projectcalico/libcalico-go/lib/backend/k8s/thirdparty" + "github.com/projectcalico/libcalico-go/lib/backend/k8s/custom" "github.com/projectcalico/libcalico-go/lib/backend/model" "github.com/projectcalico/libcalico-go/lib/converter" "github.com/projectcalico/libcalico-go/lib/scope" @@ -29,19 +29,19 @@ import ( ) const ( - GlobalBGPPeerResourceName = "globalbgppeers" - GlobalBGPPeerTPRName = "global-bgp-peer.projectcalico.org" + BGPPeerResourceName = "BGPPeers" + BGPPeerCRDName = "bgppeers.crd.projectcalico.org" ) func NewGlobalBGPPeerClient(c *kubernetes.Clientset, r *rest.RESTClient) K8sResourceClient { return &customK8sResourceClient{ clientSet: c, restClient: r, - name: GlobalBGPPeerTPRName, - resource: GlobalBGPPeerResourceName, - description: "Calico Global BGP Peers", - k8sResourceType: reflect.TypeOf(thirdparty.GlobalBgpPeer{}), - k8sListType: reflect.TypeOf(thirdparty.GlobalBgpPeerList{}), + name: BGPPeerCRDName, + resource: BGPPeerResourceName, + description: "Calico BGP Peers", + k8sResourceType: reflect.TypeOf(custom.BGPPeer{}), + k8sListType: reflect.TypeOf(custom.BGPPeerList{}), converter: GlobalBGPPeerConverter{}, } } @@ -83,18 +83,15 @@ func (_ GlobalBGPPeerConverter) NameToKey(name string) (model.Key, error) { func (c GlobalBGPPeerConverter) ToKVPair(r CustomK8sResource) (*model.KVPair, error) { // Since we are using the Calico API Spec definition to store the Calico // BGP Peer, use the client conversion helper to convert between KV and API. - t := r.(*thirdparty.GlobalBgpPeer) - ip, err := ResourceNameToIP(t.Metadata.Name) - if err != nil { - return nil, err - } + t := r.(*custom.BGPPeer) peer := api.BGPPeer{ Metadata: api.BGPPeerMetadata{ - PeerIP: *ip, + PeerIP: t.Spec.PeerIP, Scope: scope.Global, + Node: "", }, - Spec: t.Spec, + Spec: t.Spec.BGPPeerSpec, } kvp, err := c.ConvertAPIToKVPair(peer) if err != nil { @@ -111,19 +108,24 @@ func (c GlobalBGPPeerConverter) FromKVPair(kvp *model.KVPair) (CustomK8sResource return nil, err } - tprName, err := c.KeyToName(kvp.Key) + crdName, err := c.KeyToName(kvp.Key) if err != nil { return nil, err } - tpr := thirdparty.GlobalBgpPeer{ + crd := custom.BGPPeer{ Metadata: metav1.ObjectMeta{ - Name: tprName, + Name: crdName, + }, + Spec: custom.BGPPeerSpec{ + BGPPeerSpec: r.(*api.BGPPeer).Spec, + Scope: scope.Global, + PeerIP: r.(*api.BGPPeer).Metadata.PeerIP, + Node: "", }, - Spec: r.(*api.BGPPeer).Spec, } if kvp.Revision != nil { - tpr.Metadata.ResourceVersion = kvp.Revision.(string) + crd.Metadata.ResourceVersion = kvp.Revision.(string) } - return &tpr, nil + return &crd, nil } diff --git a/lib/backend/k8s/resources/globalbgppeer_test.go b/lib/backend/k8s/resources/globalbgppeer_test.go index 321e86d33..3b0755c91 100644 --- a/lib/backend/k8s/resources/globalbgppeer_test.go +++ b/lib/backend/k8s/resources/globalbgppeer_test.go @@ -16,10 +16,11 @@ package resources_test import ( "github.com/projectcalico/libcalico-go/lib/api" + "github.com/projectcalico/libcalico-go/lib/backend/k8s/custom" "github.com/projectcalico/libcalico-go/lib/backend/k8s/resources" - "github.com/projectcalico/libcalico-go/lib/backend/k8s/thirdparty" "github.com/projectcalico/libcalico-go/lib/backend/model" "github.com/projectcalico/libcalico-go/lib/net" + "github.com/projectcalico/libcalico-go/lib/scope" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -48,7 +49,7 @@ var _ = Describe("Global BGP conversion methods", func() { key2 := model.GlobalBGPPeerKey{ PeerIP: net.MustParseIP("11:22::"), } - name2 := "11-22--" + name2 := "0011-0022-0000-0000-0000-0000-0000-0000" // Compatible set of KVPair and Kubernetes Resource. kvp1 := &model.KVPair{ @@ -59,24 +60,18 @@ var _ = Describe("Global BGP conversion methods", func() { }, Revision: "rv", } - res1 := &thirdparty.GlobalBgpPeer{ + res1 := &custom.BGPPeer{ Metadata: metav1.ObjectMeta{ Name: name2, ResourceVersion: "rv", }, - Spec: api.BGPPeerSpec{ - ASNumber: 1212, - }, - } - - // Invalid Kubernetes resource, invalid name - resInvalid := &thirdparty.GlobalBgpPeer{ - Metadata: metav1.ObjectMeta{ - Name: nameInvalid, - ResourceVersion: "test", - }, - Spec: api.BGPPeerSpec{ - ASNumber: 1234, + Spec: custom.BGPPeerSpec{ + BGPPeerSpec: api.BGPPeerSpec{ + ASNumber: 1212, + }, + Scope: scope.Global, + PeerIP: key2.PeerIP, + Node: "", }, } @@ -111,8 +106,8 @@ var _ = Describe("Global BGP conversion methods", func() { Expect(err).NotTo(HaveOccurred()) Expect(r.GetObjectMeta().GetName()).To(Equal(res1.Metadata.Name)) Expect(r.GetObjectMeta().GetResourceVersion()).To(Equal(res1.Metadata.ResourceVersion)) - Expect(r).To(BeAssignableToTypeOf(&thirdparty.GlobalBgpPeer{})) - Expect(r.(*thirdparty.GlobalBgpPeer).Spec).To(Equal(res1.Spec)) + Expect(r).To(BeAssignableToTypeOf(&custom.BGPPeer{})) + Expect(r.(*custom.BGPPeer).Spec).To(Equal(res1.Spec)) }) It("should convert between a Kuberenetes resource and the equivalent KVPair", func() { @@ -123,9 +118,4 @@ var _ = Describe("Global BGP conversion methods", func() { Expect(kvp.Value).To(BeAssignableToTypeOf(&model.BGPPeer{})) Expect(kvp.Value).To(Equal(kvp1.Value)) }) - - It("should fail to convert an invalid Kuberenetes resource", func() { - _, err := converter.ToKVPair(resInvalid) - Expect(err).To(HaveOccurred()) - }) }) diff --git a/lib/backend/k8s/resources/globalconfig.go b/lib/backend/k8s/resources/globalfelixconfig.go similarity index 53% rename from lib/backend/k8s/resources/globalconfig.go rename to lib/backend/k8s/resources/globalfelixconfig.go index 56133fc69..2a2c9c424 100644 --- a/lib/backend/k8s/resources/globalconfig.go +++ b/lib/backend/k8s/resources/globalfelixconfig.go @@ -19,7 +19,7 @@ import ( "reflect" "strings" - "github.com/projectcalico/libcalico-go/lib/backend/k8s/thirdparty" + "github.com/projectcalico/libcalico-go/lib/backend/k8s/custom" "github.com/projectcalico/libcalico-go/lib/backend/model" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -28,28 +28,28 @@ import ( ) const ( - GlobalConfigResourceName = "globalconfigs" - GlobalConfigTPRName = "global-config.projectcalico.org" + GlobalFelixConfigResourceName = "GlobalFelixConfigs" + GlobalFelixConfigCRDName = "globalconfigs.crd.projectcalico.org" ) -func NewGlobalConfigClient(c *kubernetes.Clientset, r *rest.RESTClient) K8sResourceClient { +func NewGlobalFelixConfigClient(c *kubernetes.Clientset, r *rest.RESTClient) K8sResourceClient { return &customK8sResourceClient{ clientSet: c, restClient: r, - name: GlobalConfigTPRName, - resource: GlobalConfigResourceName, - description: "Calico Global Configuration", - k8sResourceType: reflect.TypeOf(thirdparty.GlobalConfig{}), - k8sListType: reflect.TypeOf(thirdparty.GlobalConfigList{}), - converter: GlobalConfigConverter{}, + name: GlobalFelixConfigCRDName, + resource: GlobalFelixConfigResourceName, + description: "Calico Global Felix Configuration", + k8sResourceType: reflect.TypeOf(custom.GlobalFelixConfig{}), + k8sListType: reflect.TypeOf(custom.GlobalFelixConfigList{}), + converter: GlobalFelixConfigConverter{}, } } -// GlobalConfigConverter implements the K8sResourceConverter interface. -type GlobalConfigConverter struct { +// GlobalFelixConfigConverter implements the K8sResourceConverter interface. +type GlobalFelixConfigConverter struct { } -func (_ GlobalConfigConverter) ListInterfaceToKey(l model.ListInterface) model.Key { +func (_ GlobalFelixConfigConverter) ListInterfaceToKey(l model.ListInterface) model.Key { pl := l.(model.GlobalConfigListOptions) if pl.Name != "" { return model.GlobalConfigKey{Name: pl.Name} @@ -57,16 +57,16 @@ func (_ GlobalConfigConverter) ListInterfaceToKey(l model.ListInterface) model.K return nil } -func (_ GlobalConfigConverter) KeyToName(k model.Key) (string, error) { +func (_ GlobalFelixConfigConverter) KeyToName(k model.Key) (string, error) { return strings.ToLower(k.(model.GlobalConfigKey).Name), nil } -func (_ GlobalConfigConverter) NameToKey(name string) (model.Key, error) { - return nil, fmt.Errorf("Mapping of Name to Key is not possible for global config") +func (_ GlobalFelixConfigConverter) NameToKey(name string) (model.Key, error) { + return nil, fmt.Errorf("Mapping of Name to Key is not possible for global felix config") } -func (c GlobalConfigConverter) ToKVPair(r CustomK8sResource) (*model.KVPair, error) { - t := r.(*thirdparty.GlobalConfig) +func (c GlobalFelixConfigConverter) ToKVPair(r CustomK8sResource) (*model.KVPair, error) { + t := r.(*custom.GlobalFelixConfig) return &model.KVPair{ Key: model.GlobalConfigKey{ Name: t.Spec.Name, @@ -76,22 +76,22 @@ func (c GlobalConfigConverter) ToKVPair(r CustomK8sResource) (*model.KVPair, err }, nil } -func (c GlobalConfigConverter) FromKVPair(kvp *model.KVPair) (CustomK8sResource, error) { +func (c GlobalFelixConfigConverter) FromKVPair(kvp *model.KVPair) (CustomK8sResource, error) { name, err := c.KeyToName(kvp.Key) if err != nil { return nil, err } - tpr := thirdparty.GlobalConfig{ + crd := custom.GlobalFelixConfig{ Metadata: metav1.ObjectMeta{ Name: name, }, - Spec: thirdparty.GlobalConfigSpec{ + Spec: custom.GlobalFelixConfigSpec{ Name: kvp.Key.(model.GlobalConfigKey).Name, Value: kvp.Value.(string), }, } if kvp.Revision != nil { - tpr.Metadata.ResourceVersion = kvp.Revision.(string) + crd.Metadata.ResourceVersion = kvp.Revision.(string) } - return &tpr, nil + return &crd, nil } diff --git a/lib/backend/k8s/resources/globalconfig_test.go b/lib/backend/k8s/resources/globalfelixconfig_test.go similarity index 89% rename from lib/backend/k8s/resources/globalconfig_test.go rename to lib/backend/k8s/resources/globalfelixconfig_test.go index 1ed528168..e8e67bc4e 100644 --- a/lib/backend/k8s/resources/globalconfig_test.go +++ b/lib/backend/k8s/resources/globalfelixconfig_test.go @@ -15,8 +15,8 @@ package resources_test import ( + "github.com/projectcalico/libcalico-go/lib/backend/k8s/custom" "github.com/projectcalico/libcalico-go/lib/backend/k8s/resources" - "github.com/projectcalico/libcalico-go/lib/backend/k8s/thirdparty" "github.com/projectcalico/libcalico-go/lib/backend/model" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -27,7 +27,7 @@ import ( var _ = Describe("Global Felix config conversion methods", func() { - converter := resources.GlobalConfigConverter{} + converter := resources.GlobalFelixConfigConverter{} // Define some useful test data. listIncomplete := model.GlobalConfigListOptions{} @@ -48,12 +48,12 @@ var _ = Describe("Global Felix config conversion methods", func() { Value: value1, Revision: "rv", } - res1 := &thirdparty.GlobalConfig{ + res1 := &custom.GlobalFelixConfig{ Metadata: metav1.ObjectMeta{ Name: name1, ResourceVersion: "rv", }, - Spec: thirdparty.GlobalConfigSpec{ + Spec: custom.GlobalFelixConfigSpec{ Name: key1.Name, Value: value1, }, @@ -83,8 +83,8 @@ var _ = Describe("Global Felix config conversion methods", func() { Expect(err).NotTo(HaveOccurred()) Expect(r.GetObjectMeta().GetName()).To(Equal(res1.Metadata.Name)) Expect(r.GetObjectMeta().GetResourceVersion()).To(Equal(res1.Metadata.ResourceVersion)) - Expect(r).To(BeAssignableToTypeOf(&thirdparty.GlobalConfig{})) - Expect(r.(*thirdparty.GlobalConfig).Spec).To(Equal(res1.Spec)) + Expect(r).To(BeAssignableToTypeOf(&custom.GlobalFelixConfig{})) + Expect(r.(*custom.GlobalFelixConfig).Spec).To(Equal(res1.Spec)) }) It("should convert between a Kuberenetes resource and the equivalent KVPair", func() { diff --git a/lib/backend/k8s/resources/systemnetworkpolicies.go b/lib/backend/k8s/resources/globalnetworkpolicies.go similarity index 52% rename from lib/backend/k8s/resources/systemnetworkpolicies.go rename to lib/backend/k8s/resources/globalnetworkpolicies.go index 78426b300..92b3b6ed8 100644 --- a/lib/backend/k8s/resources/systemnetworkpolicies.go +++ b/lib/backend/k8s/resources/globalnetworkpolicies.go @@ -15,12 +15,10 @@ package resources import ( - "fmt" "reflect" - "strings" "github.com/projectcalico/libcalico-go/lib/api" - "github.com/projectcalico/libcalico-go/lib/backend/k8s/thirdparty" + "github.com/projectcalico/libcalico-go/lib/backend/k8s/custom" "github.com/projectcalico/libcalico-go/lib/backend/model" "github.com/projectcalico/libcalico-go/lib/converter" @@ -30,32 +28,31 @@ import ( ) const ( - SystemNetworkPolicyResourceName = "systemnetworkpolicies" - SystemNetworkPolicyTPRName = "system-network-policy.alpha.projectcalico.org" - SystemNetworkPolicyNamePrefix = "snp.projectcalico.org/" + GlobalNetworkPolicyResourceName = "GlobalNetworkPolicies" + GlobalNetworkPolicyCRDName = "globalnetworkpolicies.crd.projectcalico.org" ) -func NewSystemNetworkPolicyClient(c *kubernetes.Clientset, r *rest.RESTClient) K8sResourceClient { +func NewGlobalNetworkPolicyClient(c *kubernetes.Clientset, r *rest.RESTClient) K8sResourceClient { return &customK8sResourceClient{ clientSet: c, restClient: r, - name: SystemNetworkPolicyTPRName, - resource: SystemNetworkPolicyResourceName, - description: "Calico System Network Policies", - k8sResourceType: reflect.TypeOf(thirdparty.SystemNetworkPolicy{}), - k8sListType: reflect.TypeOf(thirdparty.SystemNetworkPolicyList{}), - converter: SystemNetworkPolicyConverter{}, + name: GlobalNetworkPolicyCRDName, + resource: GlobalNetworkPolicyResourceName, + description: "Calico Global Network Policies", + k8sResourceType: reflect.TypeOf(custom.GlobalNetworkPolicy{}), + k8sListType: reflect.TypeOf(custom.GlobalNetworkPolicyList{}), + converter: GlobalNetworkPolicyConverter{}, } } -// SystemNetworkPolicyConverter implements the K8sResourceConverter interface. -type SystemNetworkPolicyConverter struct { +// GlobalNetworkPolicyConverter implements the K8sResourceConverter interface. +type GlobalNetworkPolicyConverter struct { // Since the Spec is identical to the Calico API Spec, we use the // API converter to convert to and from the model representation. converter.PolicyConverter } -func (_ SystemNetworkPolicyConverter) ListInterfaceToKey(l model.ListInterface) model.Key { +func (_ GlobalNetworkPolicyConverter) ListInterfaceToKey(l model.ListInterface) model.Key { pl := l.(model.PolicyListOptions) if pl.Name != "" { return model.PolicyKey{Name: pl.Name} @@ -63,31 +60,23 @@ func (_ SystemNetworkPolicyConverter) ListInterfaceToKey(l model.ListInterface) return nil } -func (_ SystemNetworkPolicyConverter) KeyToName(k model.Key) (string, error) { - // The name should be policed before we get here. - pk := k.(model.PolicyKey) - if !strings.HasPrefix(pk.Name, SystemNetworkPolicyNamePrefix) { - return "", fmt.Errorf("System Network Policy name %s is not correctly namespaced", pk.Name) - } - // Trim the namespace and ensure lowercase. - return strings.ToLower(strings.TrimPrefix(pk.Name, SystemNetworkPolicyNamePrefix)), nil +func (_ GlobalNetworkPolicyConverter) KeyToName(k model.Key) (string, error) { + return k.(model.PolicyKey).Name, nil } -func (_ SystemNetworkPolicyConverter) NameToKey(name string) (model.Key, error) { - policyName := fmt.Sprintf("%s%s", SystemNetworkPolicyNamePrefix, name) +func (_ GlobalNetworkPolicyConverter) NameToKey(name string) (model.Key, error) { return model.PolicyKey{ - Name: policyName, + Name: name, }, nil } -func (c SystemNetworkPolicyConverter) ToKVPair(r CustomK8sResource) (*model.KVPair, error) { +func (c GlobalNetworkPolicyConverter) ToKVPair(r CustomK8sResource) (*model.KVPair, error) { // Since we are using the Calico API Spec definition to store the Calico // Policy, use the client conversion helper to convert between KV and API. - t := r.(*thirdparty.SystemNetworkPolicy) - policyName := fmt.Sprintf("%s%s", SystemNetworkPolicyNamePrefix, t.Metadata.Name) + t := r.(*custom.GlobalNetworkPolicy) policy := api.Policy{ Metadata: api.PolicyMetadata{ - Name: policyName, + Name: t.Metadata.Name, }, Spec: t.Spec, } @@ -97,25 +86,25 @@ func (c SystemNetworkPolicyConverter) ToKVPair(r CustomK8sResource) (*model.KVPa return kvp, err } -func (c SystemNetworkPolicyConverter) FromKVPair(kvp *model.KVPair) (CustomK8sResource, error) { +func (c GlobalNetworkPolicyConverter) FromKVPair(kvp *model.KVPair) (CustomK8sResource, error) { r, err := c.ConvertKVPairToAPI(kvp) if err != nil { return nil, err } - tprName, err := c.KeyToName(kvp.Key) + crdName, err := c.KeyToName(kvp.Key) if err != nil { return nil, err } - tpr := thirdparty.SystemNetworkPolicy{ + crd := custom.GlobalNetworkPolicy{ Metadata: metav1.ObjectMeta{ - Name: tprName, + Name: crdName, }, Spec: r.(*api.Policy).Spec, } if kvp.Revision != nil { - tpr.Metadata.ResourceVersion = kvp.Revision.(string) + crd.Metadata.ResourceVersion = kvp.Revision.(string) } - return &tpr, nil + return &crd, nil } diff --git a/lib/backend/k8s/resources/systemnetworkpolicies_test.go b/lib/backend/k8s/resources/globalnetworkpolicies_test.go similarity index 85% rename from lib/backend/k8s/resources/systemnetworkpolicies_test.go rename to lib/backend/k8s/resources/globalnetworkpolicies_test.go index 9a6be78e3..2cdd9f9c3 100644 --- a/lib/backend/k8s/resources/systemnetworkpolicies_test.go +++ b/lib/backend/k8s/resources/globalnetworkpolicies_test.go @@ -16,8 +16,8 @@ package resources_test import ( "github.com/projectcalico/libcalico-go/lib/api" + "github.com/projectcalico/libcalico-go/lib/backend/k8s/custom" "github.com/projectcalico/libcalico-go/lib/backend/k8s/resources" - "github.com/projectcalico/libcalico-go/lib/backend/k8s/thirdparty" "github.com/projectcalico/libcalico-go/lib/backend/model" "github.com/projectcalico/libcalico-go/lib/numorstring" @@ -27,28 +27,25 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("System Network Policies conversion methods", func() { +var _ = Describe("Global Network Policies conversion methods", func() { - converter := resources.SystemNetworkPolicyConverter{} + converter := resources.GlobalNetworkPolicyConverter{} // Define some useful test data. listIncomplete := model.PolicyListOptions{} - keyInvalid := model.PolicyKey{ - Name: "foo.bar", - } // Compatible set of list, key and name list1 := model.PolicyListOptions{ - Name: "snp.projectcalico.org/abcd", + Name: "abcd", } key1 := model.PolicyKey{ - Name: "snp.projectcalico.org/abcd", + Name: "abcd", } name1 := "abcd" // Compatible set of key and name key2 := model.PolicyKey{ - Name: "snp.projectcalico.org/foo.bar", + Name: "foo.bar", } name2 := "foo.bar" @@ -75,7 +72,7 @@ var _ = Describe("System Network Policies conversion methods", func() { Revision: "rv", } - res1 := &thirdparty.SystemNetworkPolicy{ + res1 := &custom.GlobalNetworkPolicy{ Metadata: metav1.ObjectMeta{ Name: name2, ResourceVersion: "rv", @@ -121,8 +118,8 @@ var _ = Describe("System Network Policies conversion methods", func() { Expect(err).NotTo(HaveOccurred()) Expect(r.GetObjectMeta().GetName()).To(Equal(res1.Metadata.Name)) Expect(r.GetObjectMeta().GetResourceVersion()).To(Equal(res1.Metadata.ResourceVersion)) - Expect(r).To(BeAssignableToTypeOf(&thirdparty.SystemNetworkPolicy{})) - Expect(r.(*thirdparty.SystemNetworkPolicy).Spec).To(Equal(res1.Spec)) + Expect(r).To(BeAssignableToTypeOf(&custom.GlobalNetworkPolicy{})) + Expect(r.(*custom.GlobalNetworkPolicy).Spec).To(Equal(res1.Spec)) }) It("should convert between a Kuberenetes resource and the equivalent KVPair", func() { @@ -155,18 +152,13 @@ var _ = Describe("System Network Policies conversion methods", func() { Expect(k).To(Equal(key2)) }) - It("should fail to convert an invalid Key to a resource name", func() { - _, err := converter.KeyToName(keyInvalid) - Expect(err).To(HaveOccurred()) - }) - It("should convert between a KVPair and the equivalent Kubernetes resource", func() { r, err := converter.FromKVPair(kvp1) Expect(err).NotTo(HaveOccurred()) Expect(r.GetObjectMeta().GetName()).To(Equal(res1.Metadata.Name)) Expect(r.GetObjectMeta().GetResourceVersion()).To(Equal(res1.Metadata.ResourceVersion)) - Expect(r).To(BeAssignableToTypeOf(&thirdparty.SystemNetworkPolicy{})) - Expect(r.(*thirdparty.SystemNetworkPolicy).Spec).To(Equal(res1.Spec)) + Expect(r).To(BeAssignableToTypeOf(&custom.GlobalNetworkPolicy{})) + Expect(r.(*custom.GlobalNetworkPolicy).Spec).To(Equal(res1.Spec)) }) It("should convert between a Kuberenetes resource and the equivalent KVPair", func() { diff --git a/lib/backend/k8s/resources/ippool.go b/lib/backend/k8s/resources/ippool.go index 0c4759dd3..41f585dc1 100644 --- a/lib/backend/k8s/resources/ippool.go +++ b/lib/backend/k8s/resources/ippool.go @@ -15,36 +15,39 @@ package resources import ( - "encoding/json" "reflect" - "github.com/projectcalico/libcalico-go/lib/backend/k8s/thirdparty" + "github.com/projectcalico/libcalico-go/lib/api" + "github.com/projectcalico/libcalico-go/lib/backend/k8s/custom" "github.com/projectcalico/libcalico-go/lib/backend/model" + "github.com/projectcalico/libcalico-go/lib/converter" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) const ( - IPPoolResourceName = "ippools" - IPPoolTPRName = "ip-pool.projectcalico.org" + IPPoolResourceName = "IPPools" + IPPoolCRDName = "ippools.crd.projectcalico.org" ) func NewIPPoolClient(c *kubernetes.Clientset, r *rest.RESTClient) K8sResourceClient { return &customK8sResourceClient{ clientSet: c, restClient: r, - name: IPPoolTPRName, + name: IPPoolCRDName, resource: IPPoolResourceName, description: "Calico IP Pools", - k8sResourceType: reflect.TypeOf(thirdparty.IpPool{}), - k8sListType: reflect.TypeOf(thirdparty.IpPoolList{}), + k8sResourceType: reflect.TypeOf(custom.IPPool{}), + k8sListType: reflect.TypeOf(custom.IPPoolList{}), converter: IPPoolConverter{}, } } // IPPoolConverter implements the K8sResourceConverter interface. -type IPPoolConverter struct{} +type IPPoolConverter struct { + converter.IPPoolConverter +} func (_ IPPoolConverter) ListInterfaceToKey(l model.ListInterface) model.Key { il := l.(model.IPPoolListOptions) @@ -68,42 +71,57 @@ func (_ IPPoolConverter) NameToKey(name string) (model.Key, error) { }, nil } -func (_ IPPoolConverter) ToKVPair(r CustomK8sResource) (*model.KVPair, error) { - t := r.(*thirdparty.IpPool) - v := model.IPPool{} +func (i IPPoolConverter) ToKVPair(r CustomK8sResource) (*model.KVPair, error) { + // Since we are using the Calico API Spec definition to store the Calico + // IPPool, use the client conversion helper to convert API to KVP. + t := r.(*custom.IPPool) _, err := ResourceNameToIPNet(t.Metadata.Name) if err != nil { return nil, err } - err = json.Unmarshal([]byte(t.Spec.Value), &v) + ippool := api.IPPool{ + Metadata: api.IPPoolMetadata{ + CIDR: t.Spec.CIDR, + }, + Spec: t.Spec.IPPoolSpec, + } + + kvp, err := i.IPPoolConverter.ConvertAPIToKVPair(ippool) if err != nil { return nil, err } - return &model.KVPair{ - Key: model.IPPoolKey{CIDR: v.CIDR}, - Value: &v, - Revision: t.Metadata.ResourceVersion, - }, nil + kvp.Revision = t.Metadata.ResourceVersion + + return kvp, nil } -func (_ IPPoolConverter) FromKVPair(kvp *model.KVPair) (CustomK8sResource, error) { - v, err := json.Marshal(kvp.Value.(*model.IPPool)) +func (i IPPoolConverter) FromKVPair(kvp *model.KVPair) (CustomK8sResource, error) { + // Since we are using the Calico API Spec definition to store the IPPool CRD Spec, + // we can use the client conversion helper to convert KVP to API. + r, err := i.IPPoolConverter.ConvertKVPairToAPI(kvp) if err != nil { return nil, err } - tpr := thirdparty.IpPool{ + crdName, err := i.KeyToName(kvp.Key) + if err != nil { + return nil, err + } + + crd := custom.IPPool{ Metadata: metav1.ObjectMeta{ - Name: IPNetToResourceName(kvp.Key.(model.IPPoolKey).CIDR), + Name: crdName, }, - Spec: thirdparty.IpPoolSpec{ - Value: string(v), + Spec: custom.IPPoolSpec{ + IPPoolSpec: r.(*api.IPPool).Spec, + CIDR: r.(*api.IPPool).Metadata.CIDR, }, } + if kvp.Revision != nil { - tpr.Metadata.ResourceVersion = kvp.Revision.(string) + crd.Metadata.ResourceVersion = kvp.Revision.(string) } - return &tpr, nil + return &crd, nil } diff --git a/lib/backend/k8s/resources/ippool_test.go b/lib/backend/k8s/resources/ippool_test.go index 57ef309c4..aa3060252 100644 --- a/lib/backend/k8s/resources/ippool_test.go +++ b/lib/backend/k8s/resources/ippool_test.go @@ -15,8 +15,9 @@ package resources_test import ( + "github.com/projectcalico/libcalico-go/lib/api" + "github.com/projectcalico/libcalico-go/lib/backend/k8s/custom" "github.com/projectcalico/libcalico-go/lib/backend/k8s/resources" - "github.com/projectcalico/libcalico-go/lib/backend/k8s/thirdparty" "github.com/projectcalico/libcalico-go/lib/backend/model" "github.com/projectcalico/libcalico-go/lib/net" @@ -32,7 +33,6 @@ var _ = Describe("IP Pool conversion methods", func() { // Define some useful test data. listIncomplete := model.IPPoolListOptions{} - nameInvalid := "11-22-fail--23" // Compatible set of list, key and name list1 := model.IPPoolListOptions{ @@ -57,39 +57,38 @@ var _ = Describe("IP Pool conversion methods", func() { Masquerade: true, IPIPMode: "cross-subnet", IPIPInterface: "tunl0", + Disabled: false, + IPAM: true, }, Revision: "rv", } - res1 := &thirdparty.IpPool{ + res1 := &custom.IPPool{ Metadata: metav1.ObjectMeta{ Name: name2, ResourceVersion: "rv", }, - Spec: thirdparty.IpPoolSpec{ - Value: "{\"cidr\":\"11:22::/120\",\"ipip\":\"tunl0\",\"ipip_mode\":\"cross-subnet\",\"masquerade\":true,\"ipam\":false,\"disabled\":false}", + Spec: custom.IPPoolSpec{ + IPPoolSpec: api.IPPoolSpec{ + IPIP: &api.IPIPConfiguration{ + Enabled: true, + Mode: "cross-subnet", + }, + NATOutgoing: true, + Disabled: false, + }, + CIDR: key2.CIDR, }, } // Invalid Kubernetes resource, invalid name - resInvalidName := &thirdparty.IpPool{ - Metadata: metav1.ObjectMeta{ - Name: nameInvalid, - ResourceVersion: "test", - }, - Spec: thirdparty.IpPoolSpec{ - Value: "{}", - }, - } + nameInvalid := "11-22-fail--23" - // Invalid Kubernetes resource, invalid value - resInvalidValue := &thirdparty.IpPool{ + resInvalidName := &custom.IPPool{ Metadata: metav1.ObjectMeta{ - Name: name1, + Name: nameInvalid, ResourceVersion: "test", }, - Spec: thirdparty.IpPoolSpec{ - Value: "{", - }, + Spec: custom.IPPoolSpec{}, } It("should convert an incomplete ListInterface to no Key", func() { @@ -123,7 +122,7 @@ var _ = Describe("IP Pool conversion methods", func() { Expect(err).NotTo(HaveOccurred()) Expect(r.GetObjectMeta().GetName()).To(Equal(res1.Metadata.Name)) Expect(r.GetObjectMeta().GetResourceVersion()).To(Equal(res1.Metadata.ResourceVersion)) - Expect(r).To(BeAssignableToTypeOf(&thirdparty.IpPool{})) + Expect(r).To(BeAssignableToTypeOf(&custom.IPPool{})) }) It("should convert between a Kuberenetes resource and the equivalent KVPair", func() { @@ -139,9 +138,4 @@ var _ = Describe("IP Pool conversion methods", func() { _, err := converter.ToKVPair(resInvalidName) Expect(err).To(HaveOccurred()) }) - - It("should fail to convert an invalid Kuberenetes resource (invalid value)", func() { - _, err := converter.ToKVPair(resInvalidValue) - Expect(err).To(HaveOccurred()) - }) }) diff --git a/lib/backend/k8s/resources/names.go b/lib/backend/k8s/resources/names.go index 2d843809c..c57b2b5b9 100644 --- a/lib/backend/k8s/resources/names.go +++ b/lib/backend/k8s/resources/names.go @@ -28,8 +28,25 @@ import ( // IPToResourceName converts an IP address to a name used for a k8s resource. func IPToResourceName(ip net.IP) string { - name := strings.Replace(ip.String(), ".", "-", 3) - name = strings.Replace(name, ":", "-", 7) + name := "" + + if ip.To4() != nil { + name = strings.Replace(ip.String(), ".", "-", 3) + } else { + // IPv6 address can end in a "::" which would be a string ending in "--", + // which is not allowed in k8s name field, so we expand the IPv6 address and then replace ":" with "-". + // fe08:123:445:: will look like fe08-0123-0445-0000-0000-0000-0000-0000 + ip6 := ip.To16() + bytes := []string{} + + // Go through pairs of bytes in the address and convert them to a hex string. + for i := 0; i < len(ip6); i += 2 { + bytes = append(bytes, fmt.Sprintf("%.2x%.2x", ip6[i], ip6[i+1])) + } + + // Combine them all into a name. + name = strings.Join(bytes, "-") + } log.WithFields(log.Fields{ "Name": name, @@ -84,7 +101,7 @@ func ResourceNameToIPNet(name string) (*net.IPNet, error) { // character conversion used to convert an IP address to a k8s compatible name. func resourceNameToIPString(name string) string { // The IP address is stored in the name with periods and colons replaced - // by dashes. To determine if this is IPv4 or IPv6 count the dashes. If + // by dashes. To determine if this is IPv4 or IPv6 count the dashes. If // either of the following are true, it's IPv6: // - There is a "--" // - The number of "-" is greater than 3. diff --git a/lib/backend/k8s/resources/names_test.go b/lib/backend/k8s/resources/names_test.go index d58535946..49315c9e7 100644 --- a/lib/backend/k8s/resources/names_test.go +++ b/lib/backend/k8s/resources/names_test.go @@ -26,8 +26,14 @@ var _ = Describe("Name conversion methods", func() { It("should convert an IPv4 address to a resource compatible name", func() { Expect(resources.IPToResourceName(net.MustParseIP("11.223.3.41"))).To(Equal("11-223-3-41")) }) - It("should convert an IPv6 address to a resource compatible name", func() { - Expect(resources.IPToResourceName(net.MustParseIP("AA:1234::BBee:CC"))).To(Equal("aa-1234--bbee-cc")) + It("should convert a compressed IPv6 address to a resource compatible name", func() { + Expect(resources.IPToResourceName(net.MustParseIP("AA:1234:BBee::"))).To(Equal("00aa-1234-bbee-0000-0000-0000-0000-0000")) + }) + It("should convert a compressed IPv6 address to a resource compatible name", func() { + Expect(resources.IPToResourceName(net.MustParseIP("::1234:BBee:CC"))).To(Equal("0000-0000-0000-0000-0000-1234-bbee-00cc")) + }) + It("should convert a compressed IPv6 address to a resource compatible name", func() { + Expect(resources.IPToResourceName(net.MustParseIP("AA:1234::BBee:CC"))).To(Equal("00aa-1234-0000-0000-0000-0000-bbee-00cc")) }) It("should convert an IPv4 Network to a resource compatible name", func() { Expect(resources.IPNetToResourceName(net.MustParseNetwork("11.223.3.0/24"))).To(Equal("11-223-3-0-24")) @@ -51,9 +57,13 @@ var _ = Describe("Name conversion methods", func() { Expect(*i).To(Equal(net.MustParseIP("11.223.3.41"))) }) It("should convert a resource name to the equivalent IPv6 address", func() { - i, err := resources.ResourceNameToIP("aa-1234--bbee-cc") + i, err := resources.ResourceNameToIP("aa-1234-0-0-0-0-bbee-cc") Expect(err).NotTo(HaveOccurred()) - Expect(*i).To(Equal(net.MustParseIP("AA:1234::BBee:CC"))) + Expect(*i).To(Equal(net.MustParseIP("AA:1234:0:0:0:0:BBee:CC"))) + }) + It("should not convert an invalid resource name to an IP address", func() { + _, err := resources.ResourceNameToIP("aa-1234-0-0-0-0-bbee-cc-0-0") + Expect(err).To(HaveOccurred()) }) It("should not convert an invalid resource name to an IP address", func() { _, err := resources.ResourceNameToIP("11-223-3-4a") diff --git a/lib/backend/k8s/resources/nodebgppeer_test.go b/lib/backend/k8s/resources/nodebgppeer_test.go index eedb7ef75..c2748dc85 100644 --- a/lib/backend/k8s/resources/nodebgppeer_test.go +++ b/lib/backend/k8s/resources/nodebgppeer_test.go @@ -79,7 +79,7 @@ var _ = Describe("Node BGP conversion methods", func() { ) Expect(err).To(BeNil()) Expect(node).To(Equal("nodeX")) - Expect(name).To(Equal("1--2-3-4")) + Expect(name).To(Equal("0001-0000-0000-0000-0000-0002-0003-0004")) }) It("should convert a Key with node and PeerIP (IPv4)", func() { @@ -103,7 +103,7 @@ var _ = Describe("Node BGP conversion methods", func() { ) Expect(err).To(BeNil()) Expect(node).To(Equal("nodeY")) - Expect(name).To(Equal("aa-ff--12")) + Expect(name).To(Equal("00aa-00ff-0000-0000-0000-0000-0000-0012")) }) It("should convert a valid node name and resource name to a Key (IPv4)", func() { diff --git a/lib/backend/k8s/syncer.go b/lib/backend/k8s/syncer.go index 4dfe31256..36a2e852c 100644 --- a/lib/backend/k8s/syncer.go +++ b/lib/backend/k8s/syncer.go @@ -38,14 +38,14 @@ type kubeAPI interface { PodList(string, metav1.ListOptions) (*k8sapi.PodList, error) NetworkPolicyWatch(metav1.ListOptions) (watch.Interface, error) NetworkPolicyList() (extensions.NetworkPolicyList, error) - GlobalConfigWatch(metav1.ListOptions) (watch.Interface, error) - GlobalConfigList(model.GlobalConfigListOptions) ([]*model.KVPair, string, error) + GlobalFelixConfigWatch(metav1.ListOptions) (watch.Interface, error) + GlobalFelixConfigList(model.GlobalConfigListOptions) ([]*model.KVPair, string, error) IPPoolWatch(metav1.ListOptions) (watch.Interface, error) IPPoolList(model.IPPoolListOptions) ([]*model.KVPair, string, error) NodeWatch(metav1.ListOptions) (watch.Interface, error) NodeList(metav1.ListOptions) (list *k8sapi.NodeList, err error) - SystemNetworkPolicyWatch(metav1.ListOptions) (watch.Interface, error) - SystemNetworkPolicyList() ([]*model.KVPair, string, error) + GlobalNetworkPolicyWatch(metav1.ListOptions) (watch.Interface, error) + GlobalNetworkPolicyList() ([]*model.KVPair, string, error) HostConfigList(model.HostConfigListOptions) ([]*model.KVPair, error) getReadyStatus(k model.ReadyFlagKey) (*model.KVPair, error) } @@ -74,21 +74,21 @@ func (k *realKubeAPI) NetworkPolicyWatch(opts metav1.ListOptions) (watch watch.I return } -func (k *realKubeAPI) GlobalConfigWatch(opts metav1.ListOptions) (watch watch.Interface, err error) { - globalConfigWatcher := cache.NewListWatchFromClient( - k.kc.tprClientV1, - resources.GlobalConfigResourceName, - "kube-system", +func (k *realKubeAPI) GlobalFelixConfigWatch(opts metav1.ListOptions) (watch watch.Interface, err error) { + globalFelixConfigWatcher := cache.NewListWatchFromClient( + k.kc.crdClientV1, + resources.GlobalFelixConfigResourceName, + "", fields.Everything()) - watch, err = globalConfigWatcher.WatchFunc(opts) + watch, err = globalFelixConfigWatcher.WatchFunc(opts) return } func (k *realKubeAPI) IPPoolWatch(opts metav1.ListOptions) (watch watch.Interface, err error) { ipPoolWatcher := cache.NewListWatchFromClient( - k.kc.tprClientV1, + k.kc.crdClientV1, resources.IPPoolResourceName, - "kube-system", + "", fields.Everything()) watch, err = ipPoolWatcher.WatchFunc(opts) return @@ -114,17 +114,17 @@ func (k *realKubeAPI) NetworkPolicyList() (list extensions.NetworkPolicyList, er return } -func (k *realKubeAPI) SystemNetworkPolicyWatch(opts metav1.ListOptions) (watch.Interface, error) { +func (k *realKubeAPI) GlobalNetworkPolicyWatch(opts metav1.ListOptions) (watch.Interface, error) { watcher := cache.NewListWatchFromClient( - k.kc.tprClientV1alpha, - resources.SystemNetworkPolicyResourceName, - "kube-system", + k.kc.crdClientV1, + resources.GlobalNetworkPolicyResourceName, + "", fields.Everything()) return watcher.WatchFunc(opts) } -func (k *realKubeAPI) SystemNetworkPolicyList() ([]*model.KVPair, string, error) { - return k.kc.snpClient.List(model.PolicyListOptions{}) +func (k *realKubeAPI) GlobalNetworkPolicyList() ([]*model.KVPair, string, error) { + return k.kc.gnpClient.List(model.PolicyListOptions{}) } func (k *realKubeAPI) PodList(namespace string, opts metav1.ListOptions) (list *k8sapi.PodList, err error) { @@ -132,8 +132,8 @@ func (k *realKubeAPI) PodList(namespace string, opts metav1.ListOptions) (list * return } -func (k *realKubeAPI) GlobalConfigList(l model.GlobalConfigListOptions) ([]*model.KVPair, string, error) { - return k.kc.globalConfigClient.List(l) +func (k *realKubeAPI) GlobalFelixConfigList(l model.GlobalConfigListOptions) ([]*model.KVPair, string, error) { + return k.kc.globalFelixConfigClient.List(l) } func (k *realKubeAPI) HostConfigList(l model.HostConfigListOptions) ([]*model.KVPair, error) { @@ -162,7 +162,7 @@ func newSyncer(kubeAPI kubeAPI, converter converter, callbacks api.SyncerCallbac KEY_NS: map[string]model.Key{}, KEY_PO: map[string]model.Key{}, KEY_NP: map[string]model.Key{}, - KEY_SNP: map[string]model.Key{}, + KEY_GNP: map[string]model.Key{}, KEY_GC: map[string]model.Key{}, KEY_HC: map[string]model.Key{}, KEY_IP: map[string]model.Key{}, @@ -173,7 +173,7 @@ func newSyncer(kubeAPI kubeAPI, converter converter, callbacks api.SyncerCallbac KEY_NS: true, KEY_PO: true, KEY_NP: true, - KEY_SNP: true, + KEY_GNP: true, KEY_GC: true, KEY_HC: true, KEY_IP: true, @@ -210,8 +210,8 @@ type resourceVersions struct { podVersion string namespaceVersion string networkPolicyVersion string - systemNetworkPolicyVersion string - globalConfigVersion string + globalNetworkPolicyVersion string + globalFelixConfigVersion string poolVersion string } @@ -287,8 +287,8 @@ const ( KEY_NS = "Namespace" KEY_PO = "Pod" KEY_NP = "NetworkPolicy" - KEY_SNP = "SystemNetworkPolicy" - KEY_GC = "GlobalConfig" + KEY_GNP = "GlobalNetworkPolicy" + KEY_GC = "GlobalFelixConfig" KEY_HC = "HostConfig" KEY_IP = "IPPool" KEY_NO = "Node" @@ -302,7 +302,7 @@ func (syn *kubeSyncer) readFromKubernetesAPI() { latestVersions := resourceVersions{} // Other watcher vars. - var nsChan, poChan, npChan, snpChan, gcChan, poolChan, noChan <-chan watch.Event + var nsChan, poChan, npChan, gnpChan, gcChan, poolChan, noChan <-chan watch.Event var event watch.Event var opts metav1.ListOptions @@ -382,32 +382,32 @@ func (syn *kubeSyncer) readFromKubernetesAPI() { npChan = npWatch.ResultChan() } - if _, exists := syn.openWatchers[KEY_SNP]; !exists { - // Create watcher for SystemNetworkPolicy objects. - opts = metav1.ListOptions{ResourceVersion: latestVersions.systemNetworkPolicyVersion} - log.WithField("opts", opts).Debug("(Re)start SystemNetworkPolicy watch") - snpWatch, err := syn.kubeAPI.SystemNetworkPolicyWatch(opts) + if _, exists := syn.openWatchers[KEY_GNP]; !exists { + // Create watcher for GlobalNetworkPolicy objects. + opts = metav1.ListOptions{ResourceVersion: latestVersions.globalNetworkPolicyVersion} + log.WithField("opts", opts).Debug("(Re)start GlobalNetworkPolicy watch") + gnpWatch, err := syn.kubeAPI.GlobalNetworkPolicyWatch(opts) if err != nil { - log.Warnf("Failed to watch SystemNetworkPolicies, retrying: %s", err) + log.Warnf("Failed to watch GlobalNetworkPolicies, retrying: %s", err) time.Sleep(1 * time.Second) continue } - syn.openWatchers[KEY_SNP] = snpWatch - snpChan = snpWatch.ResultChan() + syn.openWatchers[KEY_GNP] = gnpWatch + gnpChan = gnpWatch.ResultChan() } if _, exists := syn.openWatchers[KEY_GC]; !exists { - // Create watcher for Calico global config resources. - opts = metav1.ListOptions{ResourceVersion: latestVersions.globalConfigVersion} - log.WithField("opts", opts).Info("(Re)start GlobalConfig watch") - globalConfigWatch, err := syn.kubeAPI.GlobalConfigWatch(opts) + // Create watcher for Calico global felix config resources. + opts = metav1.ListOptions{ResourceVersion: latestVersions.globalFelixConfigVersion} + log.WithField("opts", opts).Info("(Re)start GlobalFelixConfig watch") + globalFelixConfigWatch, err := syn.kubeAPI.GlobalFelixConfigWatch(opts) if err != nil { - log.Warnf("Failed to watch GlobalConfig, retrying: %s", err) + log.Warnf("Failed to watch GlobalFelixConfig, retrying: %s", err) time.Sleep(1 * time.Second) continue } - syn.openWatchers[KEY_GC] = globalConfigWatch - gcChan = globalConfigWatch.ResultChan() + syn.openWatchers[KEY_GC] = globalFelixConfigWatch + gcChan = globalFelixConfigWatch.ResultChan() } if _, exists := syn.openWatchers[KEY_IP]; !exists { @@ -491,24 +491,24 @@ func (syn *kubeSyncer) readFromKubernetesAPI() { kvp := syn.parseNetworkPolicyEvent(event) latestVersions.networkPolicyVersion = kvp.Revision.(string) syn.sendUpdates([]model.KVPair{*kvp}, KEY_NP) - case event = <-snpChan: - log.Debugf("Incoming SystemNetworkPolicy watch event. Type=%s", event.Type) + case event = <-gnpChan: + log.Debugf("Incoming GlobalNetworkPolicy watch event. Type=%s", event.Type) if syn.eventNeedsResync(event) { - syn.needsResync[KEY_SNP] = true + syn.needsResync[KEY_GNP] = true continue - } else if syn.eventRestartsWatch(event, KEY_SNP) { + } else if syn.eventRestartsWatch(event, KEY_GNP) { // Resources backed by TPRs need to be resynced on empty events. - syn.needsResync[KEY_SNP] = true - syn.closeWatcher(KEY_SNP) + syn.needsResync[KEY_GNP] = true + syn.closeWatcher(KEY_GNP) continue } // Event is OK - parse it and send it over the channel. - if kvp := syn.parseSystemNetworkPolicyEvent(event); kvp != nil { - latestVersions.systemNetworkPolicyVersion = kvp.Revision.(string) - syn.sendUpdates([]model.KVPair{*kvp}, KEY_SNP) + if kvp := syn.parseGlobalNetworkPolicyEvent(event); kvp != nil { + latestVersions.globalNetworkPolicyVersion = kvp.Revision.(string) + syn.sendUpdates([]model.KVPair{*kvp}, KEY_GNP) } case event = <-gcChan: - log.Debugf("Incoming GlobalConfig watch event. Type=%s", event.Type) + log.Debugf("Incoming GlobalFelixConfig watch event. Type=%s", event.Type) if syn.eventNeedsResync(event) { syn.needsResync[KEY_GC] = true continue @@ -519,8 +519,8 @@ func (syn *kubeSyncer) readFromKubernetesAPI() { continue } // Event is OK - parse it and send it over the channel. - kvp := syn.parseGlobalConfigEvent(event) - latestVersions.globalConfigVersion = kvp.Revision.(string) + kvp := syn.parseGlobalFelixConfigEvent(event) + latestVersions.globalFelixConfigVersion = kvp.Revision.(string) syn.sendUpdates([]model.KVPair{*kvp}, KEY_GC) case event = <-poolChan: log.Debugf("Incoming IPPool watch event. Type=%s", event.Type) @@ -656,25 +656,25 @@ func (syn *kubeSyncer) performSnapshot(versions *resourceVersions) (map[string][ } } - // Resync SystemNetworkPolicy only if needed. - if syn.needsResync[KEY_SNP] { - log.Info("Syncing SystemNetworkPolicy") - snpList, resourceVersion, err := syn.kubeAPI.SystemNetworkPolicyList() + // Resync GlobalNetworkPolicy only if needed. + if syn.needsResync[KEY_GNP] { + log.Info("Syncing GlobalNetworkPolicy") + gnpList, resourceVersion, err := syn.kubeAPI.GlobalNetworkPolicyList() if err != nil { - log.Warnf("Error querying SystemNetworkPolicies during snapshot, retrying: %s", err) + log.Warnf("Error querying GlobalNetworkPolicies during snapshot, retrying: %s", err) time.Sleep(1 * time.Second) continue } log.Debug("Received NetworkPolicy List() response") // Ensure maps are initialized. - snap[KEY_SNP] = []model.KVPair{} - keys[KEY_SNP] = map[string]bool{} + snap[KEY_GNP] = []model.KVPair{} + keys[KEY_GNP] = map[string]bool{} - versions.systemNetworkPolicyVersion = resourceVersion - for _, p := range snpList { - snap[KEY_SNP] = append(snap[KEY_IP], *p) - keys[KEY_SNP][p.Key.String()] = true + versions.globalNetworkPolicyVersion = resourceVersion + for _, p := range gnpList { + snap[KEY_GNP] = append(snap[KEY_IP], *p) + keys[KEY_GNP][p.Key.String()] = true } } @@ -712,22 +712,22 @@ func (syn *kubeSyncer) performSnapshot(versions *resourceVersions) (map[string][ } } - // Resync GlobalConfig only if needed. + // Resync GlobalFelixConfig only if needed. if syn.needsResync[KEY_GC] { - log.Info("Syncing GlobalConfig") - confList, resourceVersion, err := syn.kubeAPI.GlobalConfigList(model.GlobalConfigListOptions{}) + log.Info("Syncing GlobalFelixConfig") + confList, resourceVersion, err := syn.kubeAPI.GlobalFelixConfigList(model.GlobalConfigListOptions{}) if err != nil { - log.Warnf("Error querying GlobalConfig during snapshot, retrying: %s", err) + log.Warnf("Error querying GlobalFelixConfig during snapshot, retrying: %s", err) time.Sleep(1 * time.Second) continue } - log.Debug("Received GlobalConfig List() response") + log.Debug("Received GlobalFelixConfig List() response") // Ensure maps are initialized. snap[KEY_GC] = []model.KVPair{} keys[KEY_GC] = map[string]bool{} - versions.globalConfigVersion = resourceVersion + versions.globalFelixConfigVersion = resourceVersion for _, c := range confList { snap[KEY_GC] = append(snap[KEY_GC], *c) keys[KEY_GC][c.Key.String()] = true @@ -1003,12 +1003,12 @@ func (syn *kubeSyncer) parseNetworkPolicyEvent(e watch.Event) *model.KVPair { return kvp } -func (syn *kubeSyncer) parseGlobalConfigEvent(e watch.Event) *model.KVPair { - return syn.parseCustomK8sResourceEvent(e, resources.GlobalConfigConverter{}, "GlobalConfig") +func (syn *kubeSyncer) parseGlobalFelixConfigEvent(e watch.Event) *model.KVPair { + return syn.parseCustomK8sResourceEvent(e, resources.GlobalFelixConfigConverter{}, "GlobalFelixConfig") } -func (syn *kubeSyncer) parseSystemNetworkPolicyEvent(e watch.Event) *model.KVPair { - return syn.parseCustomK8sResourceEvent(e, resources.SystemNetworkPolicyConverter{}, "SystemNetworkPolicy") +func (syn *kubeSyncer) parseGlobalNetworkPolicyEvent(e watch.Event) *model.KVPair { + return syn.parseCustomK8sResourceEvent(e, resources.GlobalNetworkPolicyConverter{}, "GlobalNetworkPolicy") } func (syn *kubeSyncer) parseIPPoolEvent(e watch.Event) *model.KVPair { @@ -1025,16 +1025,16 @@ func (syn *kubeSyncer) parseCustomK8sResourceEvent( "ResourceType": resourceType, "EventType": e.Type, }) - tpr, ok := e.Object.(resources.CustomK8sResource) + crd, ok := e.Object.(resources.CustomK8sResource) if !ok { logContext.Panicf("Invalid custom resource event. Object: %+v", e.Object) } - logContext = logContext.WithField("Name", tpr.GetObjectMeta().GetName()) + logContext = logContext.WithField("Name", crd.GetObjectMeta().GetName()) logContext.Debug("Parsing watch event") // Convert the received resource into a KVPair. - kvp, err := converter.ToKVPair(tpr) + kvp, err := converter.ToKVPair(crd) if err == nil { // For deletes, we need to nil out the Value part of the KVPair if e.Type == watch.Deleted { @@ -1046,7 +1046,7 @@ func (syn *kubeSyncer) parseCustomK8sResourceEvent( // Error converting resource. Attempt to determine the Key and treat as // a delete (Value will be nil). logContext.WithError(err).Info("Failed to parse resource - may treat as delete") - key, err := converter.NameToKey(tpr.GetObjectMeta().GetName()) + key, err := converter.NameToKey(crd.GetObjectMeta().GetName()) if err == nil { logContext.WithField("Key", key).WithError(err).Error("Failed to parse resource, treating as deleted") return &model.KVPair{ diff --git a/lib/backend/k8s/syncer_test.go b/lib/backend/k8s/syncer_test.go index 85d212f31..cb5037824 100644 --- a/lib/backend/k8s/syncer_test.go +++ b/lib/backend/k8s/syncer_test.go @@ -109,7 +109,7 @@ func (tc *testClient) NetworkPolicyWatch(opts metav1.ListOptions) (w watch.Inter return } -func (tc *testClient) GlobalConfigWatch(opts metav1.ListOptions) (w watch.Interface, err error) { +func (tc *testClient) GlobalFelixConfigWatch(opts metav1.ListOptions) (w watch.Interface, err error) { w = tc.newWatch("global conf", make(chan watch.Event)) err = nil return @@ -154,7 +154,7 @@ func (tc *testClient) PodList(namespace string, opts metav1.ListOptions) (list * return } -func (tc *testClient) GlobalConfigList(l model.GlobalConfigListOptions) ([]*model.KVPair, string, error) { +func (tc *testClient) GlobalFelixConfigList(l model.GlobalConfigListOptions) ([]*model.KVPair, string, error) { tc.countList() return []*model.KVPair{}, "", nil } @@ -175,11 +175,11 @@ func (tc *testClient) NodeList(opts metav1.ListOptions) (list *k8sapi.NodeList, err = nil return } -func (tc *testClient) SystemNetworkPolicyWatch(opts metav1.ListOptions) (watch.Interface, error) { - return tc.newWatch("system network policy", make(chan watch.Event)), nil +func (tc *testClient) GlobalNetworkPolicyWatch(opts metav1.ListOptions) (watch.Interface, error) { + return tc.newWatch("global network policy", make(chan watch.Event)), nil } -func (tc *testClient) SystemNetworkPolicyList() ([]*model.KVPair, string, error) { +func (tc *testClient) GlobalNetworkPolicyList() ([]*model.KVPair, string, error) { tc.countList() return []*model.KVPair{}, "", nil } diff --git a/lib/backend/model/bgpconfig.go b/lib/backend/model/bgpconfig.go index 3e99a6b09..d4feef6ed 100644 --- a/lib/backend/model/bgpconfig.go +++ b/lib/backend/model/bgpconfig.go @@ -74,7 +74,7 @@ func (options GlobalBGPConfigListOptions) defaultPathRoot() string { } func (options GlobalBGPConfigListOptions) KeyFromDefaultPath(path string) Key { - log.Debugf("Get GlobalConfig key from %s", path) + log.Debugf("Get GlobalFelixConfig key from %s", path) r := matchGlobalBGPConfig.FindAllStringSubmatch(path, -1) if len(r) != 1 { log.Debugf("Didn't match regex") diff --git a/lib/backend/model/felixconfig.go b/lib/backend/model/felixconfig.go index f72887a91..8fa2f0136 100644 --- a/lib/backend/model/felixconfig.go +++ b/lib/backend/model/felixconfig.go @@ -81,7 +81,7 @@ func (key GlobalConfigKey) valueType() reflect.Type { } func (key GlobalConfigKey) String() string { - return fmt.Sprintf("GlobalConfig(name=%s)", key.Name) + return fmt.Sprintf("GlobalFelixConfig(name=%s)", key.Name) } type GlobalConfigListOptions struct { diff --git a/lib/backend/model/keys.go b/lib/backend/model/keys.go index 769138d25..6700f8a3a 100644 --- a/lib/backend/model/keys.go +++ b/lib/backend/model/keys.go @@ -210,7 +210,7 @@ func KeyFromDefaultPath(path string) Key { } return IPPoolKey{CIDR: *c} } else if m := matchGlobalConfig.FindStringSubmatch(path); m != nil { - log.Debugf("Path is a global config: %v", path) + log.Debugf("Path is a global felix config: %v", path) return GlobalConfigKey{Name: m[1]} } else if m := matchHostConfig.FindStringSubmatch(path); m != nil { log.Debugf("Path is a host config: %v", path) diff --git a/lib/backend/model/keys_test.go b/lib/backend/model/keys_test.go index 2c5d7f161..95d92d29a 100644 --- a/lib/backend/model/keys_test.go +++ b/lib/backend/model/keys_test.go @@ -80,7 +80,7 @@ var _ = DescribeTable( IPPoolKey{CIDR: mustParseCIDR("10.0.0.0/8")}, ), Entry( - "global config", + "global felix config", "/calico/v1/config/foo", GlobalConfigKey{Name: "foo"}, ), diff --git a/lib/client/config.go b/lib/client/config.go index 760a8e76b..d819ba47a 100644 --- a/lib/client/config.go +++ b/lib/client/config.go @@ -288,7 +288,7 @@ func (c *config) SetFelixConfig(name, node string, value string) error { // UnsetFelixConfig provides a mechanism for unsetting arbitrary Felix // configuration in the datastore. A blank value for the node will unset the -// global configuration. +// global felix configuration. // // Caution should be observed using this method as no validation is performed // and changing arbitrary configuration may have unexpected consequences. @@ -327,7 +327,7 @@ func (c *config) SetBGPConfig(name, node string, value string) error { // UnsetBGPConfig provides a mechanism for unsetting arbitrary BGP // configuration in the datastore. A blank value for the node will unset the -// global configuration. +// global felix configuration. // // Caution should be observed using this method as no validation is performed // and changing arbitrary configuration may have unexpected consequences. diff --git a/lib/client/ippool.go b/lib/client/ippool.go index 318c8a114..8f6de9751 100644 --- a/lib/client/ippool.go +++ b/lib/client/ippool.go @@ -18,7 +18,7 @@ import ( "github.com/projectcalico/libcalico-go/lib/api" "github.com/projectcalico/libcalico-go/lib/api/unversioned" "github.com/projectcalico/libcalico-go/lib/backend/model" - "github.com/projectcalico/libcalico-go/lib/ipip" + "github.com/projectcalico/libcalico-go/lib/converter" log "github.com/sirupsen/logrus" ) @@ -34,12 +34,13 @@ type IPPoolInterface interface { // ipPools implements IPPoolInterface type ipPools struct { + converter.IPPoolConverter c *Client } // newIPPools returns a new IPPoolInterface bound to the supplied client. func newIPPools(c *Client) IPPoolInterface { - return &ipPools{c} + return &ipPools{c: c} } // Create creates a new IP pool. @@ -132,70 +133,21 @@ func (h *ipPools) convertMetadataToListInterface(m unversioned.ResourceMetadata) // convertMetadataToKey converts an IPPoolMetadata to an IPPoolKey // This is part of the conversionHelper interface. func (h *ipPools) convertMetadataToKey(m unversioned.ResourceMetadata) (model.Key, error) { - pm := m.(api.IPPoolMetadata) - k := model.IPPoolKey{ - CIDR: pm.CIDR, - } - return k, nil + return h.IPPoolConverter.ConvertMetadataToKey(m) } // convertAPIToKVPair converts an API IPPool structure to a KVPair containing a // backend IPPool and IPPoolKey. // This is part of the conversionHelper interface. func (h *ipPools) convertAPIToKVPair(a unversioned.Resource) (*model.KVPair, error) { - ap := a.(api.IPPool) - k, err := h.convertMetadataToKey(ap.Metadata) - if err != nil { - return nil, err - } - - // Only valid interface for now is tunl0. - var ipipInterface string - var ipipMode ipip.Mode - if ap.Spec.IPIP != nil { - if ap.Spec.IPIP.Enabled { - ipipInterface = "tunl0" - } else { - ipipInterface = "" - } - ipipMode = ap.Spec.IPIP.Mode - } - - d := model.KVPair{ - Key: k, - Value: &model.IPPool{ - CIDR: ap.Metadata.CIDR, - IPIPInterface: ipipInterface, - IPIPMode: ipipMode, - Masquerade: ap.Spec.NATOutgoing, - IPAM: !ap.Spec.Disabled, - Disabled: ap.Spec.Disabled, - }, - } - - return &d, nil + return h.IPPoolConverter.ConvertAPIToKVPair(a) } // convertKVPairToAPI converts a KVPair containing a backend IPPool and IPPoolKey // to an API IPPool structure. // This is part of the conversionHelper interface. func (h *ipPools) convertKVPairToAPI(d *model.KVPair) (unversioned.Resource, error) { - backendPool := d.Value.(*model.IPPool) - - apiPool := api.NewIPPool() - apiPool.Metadata.CIDR = backendPool.CIDR - apiPool.Spec.NATOutgoing = backendPool.Masquerade - apiPool.Spec.Disabled = backendPool.Disabled - - // If any IPIP configuration is present then include the IPIP spec.. - if backendPool.IPIPInterface != "" || backendPool.IPIPMode != ipip.Undefined { - apiPool.Spec.IPIP = &api.IPIPConfiguration{ - Enabled: backendPool.IPIPInterface != "", - Mode: backendPool.IPIPMode, - } - } - - return apiPool, nil + return h.IPPoolConverter.ConvertKVPairToAPI(d) } // Apply updates an IP pool if it exists, or creates a new pool if it does not exist. diff --git a/lib/converter/ippool.go b/lib/converter/ippool.go new file mode 100644 index 000000000..63bf18c53 --- /dev/null +++ b/lib/converter/ippool.go @@ -0,0 +1,92 @@ +// Copyright (c) 2016-2017 Tigera, Inc. All rights reserved. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package converter + +import ( + "github.com/projectcalico/libcalico-go/lib/api" + "github.com/projectcalico/libcalico-go/lib/api/unversioned" + "github.com/projectcalico/libcalico-go/lib/backend/model" + "github.com/projectcalico/libcalico-go/lib/ipip" +) + +// IPPoolConverter implements a set of functions used for converting between +// API and backend representations of the IPPool resource. +type IPPoolConverter struct{} + +// ConvertMetadataToKey converts a IPPoolMetadata to a IPPoolKey. +func (p IPPoolConverter) ConvertMetadataToKey(m unversioned.ResourceMetadata) (model.Key, error) { + pm := m.(api.IPPoolMetadata) + k := model.IPPoolKey{ + CIDR: pm.CIDR, + } + return k, nil +} + +// ConvertAPIToKVPair converts an API Policy structure to a KVPair containing a +// backend IPPool and IPPoolKey. +func (p IPPoolConverter) ConvertAPIToKVPair(a unversioned.Resource) (*model.KVPair, error) { + ap := a.(api.IPPool) + k, err := p.ConvertMetadataToKey(ap.Metadata) + if err != nil { + return nil, err + } + + // Only valid interface for now is tunl0. + var ipipInterface string + var ipipMode ipip.Mode + if ap.Spec.IPIP != nil { + if ap.Spec.IPIP.Enabled { + ipipInterface = "tunl0" + } else { + ipipInterface = "" + } + ipipMode = ap.Spec.IPIP.Mode + } + + d := model.KVPair{ + Key: k, + Value: &model.IPPool{ + CIDR: ap.Metadata.CIDR, + IPIPInterface: ipipInterface, + IPIPMode: ipipMode, + Masquerade: ap.Spec.NATOutgoing, + IPAM: !ap.Spec.Disabled, + Disabled: ap.Spec.Disabled, + }, + } + + return &d, nil +} + +// ConvertKVPairToAPI converts a KVPair containing a backend IPPool and IPPoolKey +// to an API IPPool structure. +func (_ IPPoolConverter) ConvertKVPairToAPI(d *model.KVPair) (unversioned.Resource, error) { + backendPool := d.Value.(*model.IPPool) + + apiPool := api.NewIPPool() + apiPool.Metadata.CIDR = backendPool.CIDR + apiPool.Spec.NATOutgoing = backendPool.Masquerade + apiPool.Spec.Disabled = backendPool.Disabled + + // If any IPIP configuration is present then include the IPIP spec.. + if backendPool.IPIPInterface != "" || backendPool.IPIPMode != ipip.Undefined { + apiPool.Spec.IPIP = &api.IPIPConfiguration{ + Enabled: backendPool.IPIPInterface != "", + Mode: backendPool.IPIPMode, + } + } + + return apiPool, nil +} diff --git a/test/README.md b/test/README.md new file mode 100644 index 000000000..13cc2bc23 --- /dev/null +++ b/test/README.md @@ -0,0 +1,20 @@ + +## Create Custom Resource Definitions + +crds.yaml is applied before running the tests to initialize CRDs (CustomResourceDefinitions) +for the Kubernetes backend. +This manifest is applied in the Makefile once kubernetes API server is running. +crds.yaml creates the following CRDs: + - GlobalFelixConfig + - GlobalBGPPeer + - GlobalBGPConfig + - IPPool + - GlobalNetworkPolicy + +These CRDs must be created in advance for any Calico deployment with Kubernetes backend, +typically as part of the same manifest used to setup Calico. + + +## Create Mock Nodes + +mock-node.yaml creates mock node object for the tests. \ No newline at end of file diff --git a/test/crds.yaml b/test/crds.yaml new file mode 100644 index 000000000..777109bcd --- /dev/null +++ b/test/crds.yaml @@ -0,0 +1,69 @@ +kind: List +metadata: +apiVersion: v1 +items: +- apiVersion: apiextensions.k8s.io/v1beta1 + description: Calico Felix Global Felix Configuration + kind: CustomResourceDefinition + metadata: + name: globalfelixconfigs.crd.projectcalico.org + spec: + scope: Cluster + group: crd.projectcalico.org + version: v1 + names: + kind: GlobalFelixConfig + plural: globalfelixconfigs + singular: globalfelixconfig +- apiVersion: apiextensions.k8s.io/v1beta1 + description: Calico BGP Peers + kind: CustomResourceDefinition + metadata: + name: bgppeers.crd.projectcalico.org + spec: + scope: Cluster + group: crd.projectcalico.org + version: v1 + names: + kind: BGPPeer + plural: bgppeers + singular: bgppeer +- apiVersion: apiextensions.k8s.io/v1beta1 + description: Calico Global BGP Configuration + kind: CustomResourceDefinition + metadata: + name: globalbgpconfigs.crd.projectcalico.org + spec: + scope: Cluster + group: crd.projectcalico.org + version: v1 + names: + kind: GlobalBGPConfig + plural: globalbgpconfigs + singular: globalbgpconfig +- apiVersion: apiextensions.k8s.io/v1beta1 + description: Calico IP Pools + kind: CustomResourceDefinition + metadata: + name: ippools.crd.projectcalico.org + spec: + scope: Cluster + group: crd.projectcalico.org + version: v1 + names: + kind: IPPool + plural: ippools + singular: ippool +- apiVersion: apiextensions.k8s.io/v1beta1 + description: Calico Global Network Policies + kind: CustomResourceDefinition + metadata: + name: globalnetworkpolicies.crd.projectcalico.org + spec: + scope: Cluster + group: crd.projectcalico.org + version: v1 + names: + kind: GlobalNetworkPolicy + plural: globalnetworkpolicies + singular: globalnetworkpolicy diff --git a/test/mock-node.yaml b/test/mock-node.yaml new file mode 100644 index 000000000..318936142 --- /dev/null +++ b/test/mock-node.yaml @@ -0,0 +1,14 @@ +# This is a Node used by the k8s FV tests. A number of tests +# rely on this Node exising in the Kubernetes API. +kind: Node +apiVersion: v1 +metadata: + name: "127.0.0.1" +spec: + podCIDR: "10.10.10.0/24" +status: + addresses: + - type: NodeInternalIP + address: "127.0.0.1/32" + - type: NodeExternalIP + address: "5.6.7.8/32"