├── .github └── workflows │ └── push-to-docker-hub.yml ├── Dockerfile ├── LICENSE ├── README.md ├── calico-networksets-controller.go ├── go.mod └── k8s-mainfest.yaml /.github/workflows/push-to-docker-hub.yml: -------------------------------------------------------------------------------- 1 | name: Publish Docker image 2 | on: 3 | release: 4 | types: [published] 5 | jobs: 6 | push_to_registry: 7 | name: Push Docker image to Docker Hub 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Check out the repo 11 | uses: actions/checkout@v2 12 | - name: Push to Docker Hub 13 | uses: docker/build-push-action@v1 14 | with: 15 | username: ${{ secrets.DOCKER_USERNAME }} 16 | password: ${{ secrets.DOCKER_PASSWORD }} 17 | repository: ktrufanov/k8s-calico-networksets-controller 18 | tag_with_ref: true 19 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.15 AS builder 2 | 3 | WORKDIR $GOPATH/src/github.com/ktrufanov/k8s-calico-networksets-controller 4 | COPY . . 5 | RUN CGO_ENABLED=0 GOOS=linux go build -o /calico-networksets-controller calico-networksets-controller.go 6 | FROM alpine:latest 7 | COPY --from=builder /calico-networksets-controller /calico-networksets-controller 8 | 9 | CMD ["./calico-networksets-controller"] 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # k8s-calico-networksets-controller 2 | 3 | With this controller, you can control access to/from custom resources by their names
4 | The addresses of these resources can be obtained from your own dedicated service
5 | For example, they can be salt hosts, net prefixes, dns names...
6 | Here is an example with dns names
7 | Service for dns resolving - [dns-resolver](https://github.com/ktrufanov/dns-resolver) 8 | 9 | ## Install 10 | quick start 11 | ``` 12 | kubectl create ns calico-networksets-controller 13 | kubectl apply -f https://raw.githubusercontent.com/ktrufanov/dns-resolver/0.0.1/k8s-mainfest.yaml 14 | kubectl apply -f https://raw.githubusercontent.com/ktrufanov/k8s-calico-networksets-controller/0.0.11-1/k8s-mainfest.yaml 15 | ``` 16 | 17 | ## Description 18 | Controller watches by create/update/delete [Calico NetworkPolicy](https://docs.projectcalico.org/reference/resources/networkpolicy).
19 | If source/destination selector of NetworkPolicy have the specific label then controller creates/updates [Calico NetworkSet](https://docs.projectcalico.org/reference/resources/networkset).
20 | IP networks/CIDRs for NetworkSet are requested from the http url. This url is customizable for specific label.
21 | Controller periodically updates the NetworkSet.
22 | Аlso works with GlobalNetworkPolicy/GlobalNetworkSet. 23 | 24 | ***/config-k8s/networksets-controller.toml:*** 25 | ``` 26 | [main] 27 | reload_time=60000 28 | debug=false 29 | service_port=8080 30 | 31 | [DNS_RESOLVER] 32 | url="http://dns-resolver.calico-networksets-controller:8080/dns" 33 | 34 | [OTHER_NET_SOURCE] 35 | url="http://other-net-source.calico-networksets-controller:8080/nets" 36 | ``` 37 | ***main*** section: 38 | - ***reload-time*** - time to reload networksets in ms 39 | - ***debug*** - debug log 40 | - ***service_port*** - service listen port 41 | 42 | The names of other sections are specific labels
43 | - ***DNS_RESOLVER***, ***OTHER_NET_SOURCE*** - specific labels 44 | - ***url*** - net source for specific label 45 | 46 | http request example: 47 | ``` 48 | curl -X POST --data "item=github.com" http://dns-resolver.calico-networksets-controller:8080/dns 49 | ``` 50 | response must be in format: 51 | ``` 52 | {"Nets":["140.82.121.4/32"]} 53 | ``` 54 | ## Example 55 | NetworkPolicy: 56 | ``` 57 | apiVersion: crd.projectcalico.org/v1 58 | kind: NetworkPolicy 59 | metadata: 60 | name: default.test-allow-github 61 | namespace: examples 62 | spec: 63 | selector: app == 'k8s-example' 64 | types: 65 | - Egress 66 | egress: 67 | - action: Allow 68 | protocol: TCP 69 | destination: 70 | selector: DNS_RESOLVER == 'github.com' 71 | ports: 72 | - 443 73 | ``` 74 | This NetworkPolicy allow tcp port 443 to github.com 75 | 76 | NetworkSet will be create for this NetworkPolicy : 77 | ``` 78 | apiVersion: crd.projectcalico.org/v1 79 | kind: NetworkSet 80 | metadata: 81 | annotations: 82 | automatization/networksets-controller: "true" 83 | labels: 84 | DNS_RESOLVER: github.com 85 | name: auto-ns-github-com 86 | namespace: examples 87 | spec: 88 | nets: 89 | - 140.82.121.3/32 90 | ``` 91 | This NetworkSet will be refreshed every ***reload_time*** times 92 | 93 | ## Metrics 94 | - ***networksets_controller_errors*** - "Count of errors" 95 | -------------------------------------------------------------------------------- /calico-networksets-controller.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "reflect" 6 | "time" 7 | "sort" 8 | "fmt" 9 | "context" 10 | "strings" 11 | "regexp" 12 | "os" 13 | "os/signal" 14 | "encoding/json" 15 | "k8s.io/client-go/kubernetes" 16 | "k8s.io/client-go/rest" 17 | "k8s.io/apimachinery/pkg/runtime/schema" 18 | "k8s.io/client-go/dynamic" 19 | "k8s.io/client-go/dynamic/dynamicinformer" 20 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 21 | "k8s.io/client-go/tools/cache" 22 | "github.com/prometheus/client_golang/prometheus" 23 | "github.com/prometheus/client_golang/prometheus/promauto" 24 | "github.com/prometheus/client_golang/prometheus/promhttp" 25 | "github.com/projectcalico/libcalico-go/lib/options" 26 | "k8s.io/client-go/tools/clientcmd" 27 | "github.com/projectcalico/libcalico-go/lib/selector/tokenizer" 28 | "github.com/spf13/viper" 29 | calicoapi "github.com/projectcalico/libcalico-go/lib/apis/v3" 30 | calicoclient "github.com/projectcalico/libcalico-go/lib/clientv3" 31 | neturl "net/url" 32 | log "github.com/sirupsen/logrus" 33 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 | 35 | ) 36 | 37 | 38 | type networksetsController struct { 39 | runtime_viper *viper.Viper 40 | clientset *kubernetes.Clientset 41 | calicoClient calicoclient.Interface 42 | ctx context.Context 43 | metric_counter map[string]prometheus.Counter 44 | } 45 | 46 | type nets_type struct { 47 | Nets []string `json:"nets,omitempty" validate:"omitempty,dive,cidr"` 48 | } 49 | 50 | func getOneHTTPparamWithEmpty(r *http.Request,param string) string{ 51 | if param, ok := r.Form[param]; ok { 52 | return param[0] 53 | } 54 | return "" 55 | } 56 | 57 | 58 | 59 | func in_array(a string, list []string) bool { 60 | for _, b := range list { 61 | if b == a { 62 | return true 63 | } 64 | } 65 | return false 66 | } 67 | 68 | func AppendIfMissingStr(slice []string, i string) []string { 69 | for _, ele := range slice { 70 | if ele == i { 71 | return slice 72 | } 73 | } 74 | return append(slice, i) 75 | } 76 | 77 | func AppendIfMissing(slice []string, i ...string) []string { 78 | for _, sli := range i { 79 | slice=AppendIfMissingStr(slice, sli) 80 | } 81 | return slice 82 | } 83 | 84 | 85 | 86 | 87 | // getClients builds and returns Kubernetes and Calico clients. 88 | func (nc *networksetsController) getClients(kubeconfig string) (*kubernetes.Clientset, calicoclient.Interface, error) { 89 | // Get Calico client 90 | calicoClient, err := calicoclient.NewFromEnv() 91 | if err != nil { 92 | return nil, nil, fmt.Errorf("failed to build Calico client: %s", err) 93 | } 94 | 95 | // Now build the Kubernetes client, we support in-cluster config and kubeconfig 96 | // as means of configuring the client. 97 | k8sconfig, err := clientcmd.BuildConfigFromFlags("", kubeconfig) 98 | if err != nil { 99 | return nil, nil, fmt.Errorf("failed to build kubernetes client config: %s", err) 100 | } 101 | 102 | // Get Kubernetes clientset 103 | k8sClientset, err := kubernetes.NewForConfig(k8sconfig) 104 | if err != nil { 105 | return nil, nil, fmt.Errorf("failed to build kubernetes client: %s", err) 106 | } 107 | 108 | return k8sClientset, calicoClient, nil 109 | } 110 | 111 | 112 | func (nc *networksetsController) run() { 113 | config, err := rest.InClusterConfig() 114 | if err != nil { 115 | log.Println(err.Error()) 116 | } 117 | // creates the dynami client 118 | dc, err := dynamic.NewForConfig(config) 119 | if err != nil { 120 | log.Println(err.Error()) 121 | } 122 | // Create a factory object that we can say "hey, I need to watch this resource" 123 | // and it will give us back an informer for it 124 | f := dynamicinformer.NewFilteredDynamicSharedInformerFactory(dc, 0, metav1.NamespaceAll, nil) 125 | 126 | //Look by NetworkPolicies 127 | npr, _ := schema.ParseResourceArg("networkpolicies.v1.crd.projectcalico.org") 128 | // Finally, create our informer for resource! 129 | i := f.ForResource(*npr) 130 | 131 | stopChNetworkpolicy := make(chan struct{}) 132 | go nc.startNetworkpolicyWatching(stopChNetworkpolicy, i.Informer()) 133 | 134 | 135 | //Look by GlobalNetworkPolicies 136 | gnpr, _ := schema.ParseResourceArg("globalnetworkpolicies.v1.crd.projectcalico.org") 137 | // Finally, create our informer for resource! 138 | j := f.ForResource(*gnpr) 139 | stopChGlobalNetworkpolicy := make(chan struct{}) 140 | go nc.startGlobalNetworkpolicyWatching(stopChGlobalNetworkpolicy, j.Informer()) 141 | 142 | sigCh := make(chan os.Signal, 0) 143 | signal.Notify(sigCh, os.Kill, os.Interrupt) 144 | 145 | <-sigCh 146 | close(stopChNetworkpolicy) 147 | close(stopChGlobalNetworkpolicy) 148 | } 149 | 150 | 151 | 152 | func (nc *networksetsController) GetLabelsBySelector(sel string) (map[string][]string,error) { 153 | 154 | //Get config sections 155 | var config_strings []string 156 | for label,_ := range nc.runtime_viper.AllSettings() { 157 | if (label != "main") { 158 | config_strings = append(config_strings, label) 159 | } 160 | } 161 | log.Debug("config strings: ",config_strings) 162 | 163 | //Get tokens from selector 164 | tokens,parse_err := tokenizer.Tokenize(sel) 165 | if parse_err!=nil { 166 | return map[string][]string{}, parse_err 167 | } 168 | log.Debug("tokens: ",tokens) 169 | 170 | //return labels if tokens equal config label 171 | out := make(map[string][]string) 172 | now_label:="" 173 | for _,token := range tokens { 174 | token_label:=strings.ToLower(fmt.Sprintf("%v",token.Value)) 175 | if ( token.Kind == tokenizer.TokLabel ) { 176 | if ( in_array(token_label, config_strings ) ) { 177 | now_label=fmt.Sprintf("%v",token.Value) 178 | } else { 179 | now_label = "" 180 | } 181 | } 182 | if ( now_label!="" && token.Kind == tokenizer.TokStringLiteral) { 183 | out[now_label] = AppendIfMissing( out[now_label], fmt.Sprintf("%v",token.Value) ) 184 | } 185 | } 186 | 187 | return out,nil 188 | } 189 | 190 | func MergeMaps(base map[string][]string, in map[string][]string ) (map[string][]string) { 191 | out:=base 192 | for k,v := range in { 193 | out[k] = AppendIfMissing(out[k],v...) 194 | } 195 | return out 196 | } 197 | 198 | 199 | /* 200 | NETWORK POLICIES 201 | */ 202 | func (nc *networksetsController) GetNetworkPolicyRuleLabels( namespace string, np_name string ) ( map[string][]string, error) { 203 | 204 | //Read Network Policy 205 | out:=make(map[string][]string) 206 | np, err := nc.calicoClient.NetworkPolicies().Get(nc.ctx, namespace, np_name, options.GetOptions{}) 207 | if err != nil { 208 | return out, err 209 | } 210 | for _,rule := range np.Spec.Ingress { 211 | log.Debug("Ingress selector - ", rule.Source.Selector) 212 | 213 | lbls,lblerr := nc.GetLabelsBySelector(rule.Source.Selector) 214 | if lblerr != nil { 215 | return out, lblerr 216 | } 217 | out=MergeMaps(out,lbls) 218 | 219 | lbls,lblerr = nc.GetLabelsBySelector(rule.Destination.Selector) 220 | if lblerr != nil { 221 | return out, lblerr 222 | } 223 | out=MergeMaps(out,lbls) 224 | 225 | } 226 | for _,rule := range np.Spec.Egress { 227 | log.Debug("Egress selector - ", rule.Source.Selector) 228 | 229 | lbls,lblerr := nc.GetLabelsBySelector(rule.Source.Selector) 230 | if lblerr != nil { 231 | return out, lblerr 232 | } 233 | out=MergeMaps(out,lbls) 234 | 235 | lbls,lblerr = nc.GetLabelsBySelector(rule.Destination.Selector) 236 | if lblerr != nil { 237 | return out, lblerr 238 | } 239 | out=MergeMaps(out,lbls) 240 | } 241 | log.Debug("labels=",out) 242 | return out, nil 243 | 244 | } 245 | 246 | func (nc *networksetsController) SetupNetworksets(namespace string, name string) (error) { 247 | 248 | //Collect all policy in current namespace 249 | namespace_rules := make(map[string][]string) 250 | calicoPolicies, err := nc.calicoClient.NetworkPolicies().List(nc.ctx, options.ListOptions{Namespace: namespace}) 251 | if err != nil { 252 | return err 253 | } 254 | for _, policy := range calicoPolicies.Items { 255 | 256 | 257 | log.Debug("Find policy: ",policy.Name) 258 | //Check rules for selector 259 | rl,err_rule:=nc.GetNetworkPolicyRuleLabels( namespace, policy.Name ) 260 | if err_rule != nil { 261 | log.Info(err_rule) 262 | nc.metric_counter["error_counter"].Inc() 263 | continue 264 | } 265 | //In namespace_rules collect all label vaules 266 | namespace_rules = MergeMaps(namespace_rules,rl) 267 | } 268 | log.Debug("rule labels = ",namespace_rules) 269 | 270 | //Get NetwrokSets in current namespace 271 | current_networksets := make(map[string]map[string]bool) 272 | calicoNetworksets, err := nc.calicoClient.NetworkSets().List(nc.ctx, options.ListOptions{Namespace: namespace}) 273 | if err != nil { 274 | return err 275 | } 276 | //Find networksets with label from configuration 277 | for _, networkset := range calicoNetworksets.Items { 278 | if (metav1.HasAnnotation(networkset.ObjectMeta,"automatization/networksets-controller")) { 279 | meta_labels := networkset.ObjectMeta.GetLabels() 280 | for lbl,lbl_slice := range namespace_rules { 281 | for _,val := range lbl_slice { 282 | if meta_labels[lbl] == val { 283 | log.Debug("Find networkset ",networkset.Name," with label ",lbl," = ",val) 284 | if _,ok := current_networksets[lbl]; !ok { 285 | current_networksets[lbl] = map[string]bool{val: true} 286 | } else { 287 | current_networksets[lbl][val] = true 288 | } 289 | } 290 | } 291 | } 292 | 293 | } 294 | } 295 | log.Debug("current networksets ",current_networksets) 296 | 297 | //Create networksets 298 | for lbl,lbl_slice := range namespace_rules { 299 | for _,val := range lbl_slice { 300 | if _,ok := current_networksets[lbl][val];!ok { 301 | log.Info("Create networkset ",lbl," = ",val) 302 | re := regexp.MustCompile(`[^a-z0-9\-]`) 303 | name_ns := re.ReplaceAllString(strings.ToLower("auto-ns-"+val),"-") 304 | ns, ns_err := nc.NetworksetDef(namespace, name_ns , lbl, val) 305 | if ns_err != nil { 306 | log.Info(ns_err) 307 | nc.metric_counter["error_counter"].Inc() 308 | continue 309 | } 310 | _, err := nc.calicoClient.NetworkSets().Create(nc.ctx, ns, options.SetOptions{}) 311 | if err != nil { 312 | log.Info(err) 313 | nc.metric_counter["error_counter"].Inc() 314 | continue 315 | } 316 | } 317 | } 318 | } 319 | 320 | //Delete networksets 321 | for _, networkset := range calicoNetworksets.Items { 322 | if (metav1.HasAnnotation(networkset.ObjectMeta,"automatization/networksets-controller")) { 323 | meta_labels := networkset.ObjectMeta.GetLabels() 324 | for_delete := true 325 | for del_lbl, val_lbl := range meta_labels { 326 | if v,ok := current_networksets[del_lbl][val_lbl]; ok && v { 327 | for_delete = false 328 | continue 329 | } 330 | } 331 | if for_delete { 332 | ns_name:=networkset.GetName() 333 | log.Info("Delete NetworkSet ", ns_name) 334 | _, err := nc.calicoClient.NetworkSets().Delete(nc.ctx, namespace, ns_name, options.DeleteOptions{}) 335 | if err != nil { 336 | return err 337 | } 338 | } 339 | } 340 | } 341 | 342 | return nil 343 | } 344 | 345 | 346 | func (nc *networksetsController) ReloadNetworksets() (error) { 347 | 348 | var config_strings []string 349 | for label,_ := range nc.runtime_viper.AllSettings() { 350 | if (label != "main") { 351 | config_strings = append(config_strings, label) 352 | } 353 | } 354 | log.Debug("config strings: ",config_strings) 355 | 356 | calicoNetworksets, err := nc.calicoClient.NetworkSets().List(nc.ctx, options.ListOptions{}) 357 | if err != nil { 358 | return err 359 | } 360 | normal:=make(map[string]map[string]*calicoapi.NetworkSet) 361 | for _, networkset := range calicoNetworksets.Items { 362 | if (metav1.HasAnnotation(networkset.ObjectMeta,"automatization/networksets-controller")) { 363 | meta_labels := networkset.ObjectMeta.GetLabels() 364 | for lbl, val := range meta_labels { 365 | if in_array(strings.ToLower(lbl),config_strings) { 366 | 367 | var ns *calicoapi.NetworkSet 368 | if _,ok := normal[lbl][val]; ok { 369 | ns = normal[lbl][val] 370 | } else { 371 | 372 | var ns_err error 373 | ns, ns_err = nc.NetworksetDef(networkset.ObjectMeta.GetNamespace(), networkset.ObjectMeta.GetName() , lbl, val) 374 | if ns_err != nil { 375 | log.Info(ns_err) 376 | nc.metric_counter["error_counter"].Inc() 377 | continue 378 | } 379 | normal[lbl]=map[string]*calicoapi.NetworkSet{val:ns} 380 | } 381 | sort.Strings(ns.Spec.Nets) 382 | sort.Strings(networkset.Spec.Nets) 383 | if !reflect.DeepEqual(ns.Spec.Nets,networkset.Spec.Nets) { 384 | log.Debug("Nets are not in normal state. Update networkset ",networkset.ObjectMeta.GetName()) 385 | 386 | //Update networkset 387 | ns.ObjectMeta.SetResourceVersion(networkset.ObjectMeta.GetResourceVersion()) 388 | ns.ObjectMeta.SetCreationTimestamp(networkset.ObjectMeta.GetCreationTimestamp()) 389 | ns.ObjectMeta.SetUID(networkset.ObjectMeta.GetUID()) 390 | ns.ObjectMeta.SetNamespace(networkset.ObjectMeta.GetNamespace()) 391 | _, err := nc.calicoClient.NetworkSets().Update(nc.ctx, ns, options.SetOptions{}) 392 | if err != nil { 393 | log.Info(err) 394 | nc.metric_counter["error_counter"].Inc() 395 | continue 396 | } 397 | 398 | } else { 399 | log.Debug("Networkset '",networkset.ObjectMeta.GetName(),"' in normal state") 400 | } 401 | 402 | 403 | 404 | } 405 | } 406 | } 407 | } 408 | return nil 409 | } 410 | 411 | 412 | func (nc *networksetsController) startNetworkpolicyWatching(stopCh <-chan struct{}, s cache.SharedIndexInformer) { 413 | 414 | 415 | handlers := cache.ResourceEventHandlerFuncs{ 416 | AddFunc: func(obj interface{}) { 417 | 418 | u := obj.(*unstructured.Unstructured) 419 | namespace := u.GetNamespace() 420 | //Hack for strange calico networkpolicy names 421 | np_name:=strings.Replace(u.GetName(), "default.","",1) 422 | 423 | log.Debug("create event (namespace:",namespace," np:",np_name,")") 424 | err := nc.SetupNetworksets( namespace, np_name ) 425 | if err!= nil { 426 | log.Info(err) 427 | nc.metric_counter["error_counter"].Inc() 428 | } 429 | 430 | }, 431 | UpdateFunc: func(oldObj, obj interface{}) { 432 | 433 | u := obj.(*unstructured.Unstructured) 434 | namespace := u.GetNamespace() 435 | //Hack for strange calico networkpolicy names 436 | np_name:=strings.Replace(u.GetName(), "default.","",1) 437 | 438 | log.Debug("update event (namespace:",namespace," np:",np_name,")") 439 | err := nc.SetupNetworksets( namespace, np_name ) 440 | if err!= nil { 441 | log.Info(err) 442 | nc.metric_counter["error_counter"].Inc() 443 | } 444 | 445 | 446 | }, 447 | DeleteFunc: func(obj interface{}) { 448 | 449 | u := obj.(*unstructured.Unstructured) 450 | namespace := u.GetNamespace() 451 | //Hack for strange calico networkpolicy names 452 | np_name:=strings.Replace(u.GetName(), "default.","",1) 453 | 454 | log.Debug("create event (namespace:",namespace," np:",np_name,")") 455 | err := nc.SetupNetworksets( namespace, np_name ) 456 | if err!= nil { 457 | log.Info(err) 458 | nc.metric_counter["error_counter"].Inc() 459 | } 460 | 461 | }, 462 | } 463 | 464 | s.AddEventHandler(handlers) 465 | s.Run(stopCh) 466 | } 467 | 468 | func (nc *networksetsController) NetworksetDef(namespace string, name string, label string, value string) (*calicoapi.NetworkSet,error) { 469 | 470 | url := nc.runtime_viper.GetString(strings.ToLower(label+".url")) 471 | if url=="" { 472 | return &calicoapi.NetworkSet{}, fmt.Errorf("Empty url for config [%s]", label) 473 | } 474 | 475 | //Get nets from source network services 476 | v := neturl.Values{} 477 | v.Add( "item", value ) 478 | resp, err := http.PostForm( url, v ) 479 | if err!=nil { 480 | return &calicoapi.NetworkSet{}, err 481 | } 482 | defer resp.Body.Close() 483 | decoder := json.NewDecoder(resp.Body) 484 | var nets nets_type 485 | err_get_nets := decoder.Decode(&nets) 486 | if err_get_nets != nil { 487 | return &calicoapi.NetworkSet{}, fmt.Errorf("Fail to parse response from url(%s) %s = %s - %v",url,label,value,err_get_nets) 488 | } 489 | log.Debug("Nets from api [",label,"]: ",nets) 490 | if len(nets.Nets)<1 { 491 | return &calicoapi.NetworkSet{}, fmt.Errorf("Empty nets for %s = %s",label,value) 492 | } 493 | 494 | labels := map[string]string{ 495 | label: value, 496 | } 497 | return &calicoapi.NetworkSet{ 498 | ObjectMeta: metav1.ObjectMeta{ 499 | Name: name , 500 | Namespace: namespace, 501 | Labels: labels, 502 | Annotations: map[string]string{ 503 | "automatization/networksets-controller": "true", 504 | }, 505 | }, 506 | Spec: calicoapi.NetworkSetSpec{ 507 | Nets: nets.Nets, 508 | }, 509 | }, nil 510 | } 511 | 512 | 513 | /* 514 | GLOBAL NETWORK POLICIES 515 | */ 516 | 517 | func (nc *networksetsController) ReloadGlobalNetworksets() (error) { 518 | 519 | var config_strings []string 520 | for label,_ := range nc.runtime_viper.AllSettings() { 521 | if (label != "main") { 522 | config_strings = append(config_strings, label) 523 | } 524 | } 525 | log.Debug("config strings: ",config_strings) 526 | 527 | calicoGlobalNetworksets, err := nc.calicoClient.GlobalNetworkSets().List(nc.ctx, options.ListOptions{}) 528 | if err != nil { 529 | return err 530 | } 531 | normal:=make(map[string]map[string]*calicoapi.GlobalNetworkSet) 532 | for _, globalnetworkset := range calicoGlobalNetworksets.Items { 533 | if (metav1.HasAnnotation(globalnetworkset.ObjectMeta,"automatization/networksets-controller")) { 534 | meta_labels := globalnetworkset.ObjectMeta.GetLabels() 535 | for lbl, val := range meta_labels { 536 | if in_array(strings.ToLower(lbl),config_strings) { 537 | 538 | var gns *calicoapi.GlobalNetworkSet 539 | if _,ok := normal[lbl][val]; ok { 540 | gns = normal[lbl][val] 541 | } else { 542 | 543 | var gns_err error 544 | gns, gns_err = nc.GlobalNetworksetDef( globalnetworkset.ObjectMeta.GetName() , lbl, val) 545 | if gns_err != nil { 546 | log.Info(gns_err) 547 | nc.metric_counter["error_counter"].Inc() 548 | continue 549 | } 550 | normal[lbl]=map[string]*calicoapi.GlobalNetworkSet{val:gns} 551 | } 552 | 553 | sort.Strings(gns.Spec.Nets) 554 | sort.Strings(globalnetworkset.Spec.Nets) 555 | if !reflect.DeepEqual(gns.Spec.Nets,globalnetworkset.Spec.Nets) { 556 | log.Debug("Nets are not in normal state. Update global networkset ",globalnetworkset.ObjectMeta.GetName()) 557 | 558 | //Update networkset 559 | gns.ObjectMeta.SetResourceVersion(globalnetworkset.ObjectMeta.GetResourceVersion()) 560 | gns.ObjectMeta.SetCreationTimestamp(globalnetworkset.ObjectMeta.GetCreationTimestamp()) 561 | gns.ObjectMeta.SetUID(globalnetworkset.ObjectMeta.GetUID()) 562 | _, err := nc.calicoClient.GlobalNetworkSets().Update(nc.ctx, gns, options.SetOptions{}) 563 | if err != nil { 564 | log.Info(err) 565 | nc.metric_counter["error_counter"].Inc() 566 | continue 567 | } 568 | 569 | } else { 570 | log.Debug("GlobalNetworkset '",globalnetworkset.ObjectMeta.GetName(),"' in normal state") 571 | } 572 | 573 | 574 | 575 | } 576 | } 577 | } 578 | } 579 | return nil 580 | } 581 | 582 | 583 | func (nc *networksetsController) GlobalNetworksetDef( name string, label string, value string) (*calicoapi.GlobalNetworkSet,error) { 584 | 585 | url := nc.runtime_viper.GetString(strings.ToLower(label+".url")) 586 | if url=="" { 587 | return &calicoapi.GlobalNetworkSet{}, fmt.Errorf("Empty url for config [%s]", label) 588 | } 589 | 590 | //Get nets from source network services 591 | v := neturl.Values{} 592 | v.Add( "item", value ) 593 | resp, err := http.PostForm( url, v ) 594 | if err!=nil { 595 | return &calicoapi.GlobalNetworkSet{}, err 596 | } 597 | defer resp.Body.Close() 598 | decoder := json.NewDecoder(resp.Body) 599 | var nets nets_type 600 | err_get_nets := decoder.Decode(&nets) 601 | if err_get_nets != nil { 602 | return &calicoapi.GlobalNetworkSet{}, fmt.Errorf("Fail to parse response from url(%s) %s = %s - %v",url,label,value,err_get_nets) 603 | } 604 | log.Debug("Nets from api [",label,"]: ",nets) 605 | if len(nets.Nets)<1 { 606 | return &calicoapi.GlobalNetworkSet{}, fmt.Errorf("Empty nets for %s = %s",label,value) 607 | } 608 | 609 | labels := map[string]string{ 610 | label: value, 611 | } 612 | return &calicoapi.GlobalNetworkSet{ 613 | ObjectMeta: metav1.ObjectMeta{ 614 | Name: name , 615 | Labels: labels, 616 | Annotations: map[string]string{ 617 | "automatization/networksets-controller": "true", 618 | }, 619 | }, 620 | Spec: calicoapi.GlobalNetworkSetSpec{ 621 | Nets: nets.Nets, 622 | }, 623 | }, nil 624 | } 625 | 626 | 627 | func (nc *networksetsController) GetGlobalNetworkPolicyRuleLabels( np_name string ) ( map[string][]string, error) { 628 | 629 | //Read Network Policy 630 | out:=make(map[string][]string) 631 | np, err := nc.calicoClient.GlobalNetworkPolicies().Get(nc.ctx, np_name, options.GetOptions{}) 632 | if err != nil { 633 | return out, err 634 | } 635 | for _,rule := range np.Spec.Ingress { 636 | log.Debug("Ingress selector - ", rule.Source.Selector) 637 | 638 | lbls,lblerr := nc.GetLabelsBySelector(rule.Source.Selector) 639 | if lblerr != nil { 640 | return out, lblerr 641 | } 642 | out=MergeMaps(out,lbls) 643 | 644 | lbls,lblerr = nc.GetLabelsBySelector(rule.Destination.Selector) 645 | if lblerr != nil { 646 | return out, lblerr 647 | } 648 | out=MergeMaps(out,lbls) 649 | 650 | } 651 | for _,rule := range np.Spec.Egress { 652 | log.Debug("Egress selector - ", rule.Source.Selector) 653 | 654 | lbls,lblerr := nc.GetLabelsBySelector(rule.Source.Selector) 655 | if lblerr != nil { 656 | return out, lblerr 657 | } 658 | out=MergeMaps(out,lbls) 659 | 660 | lbls,lblerr = nc.GetLabelsBySelector(rule.Destination.Selector) 661 | if lblerr != nil { 662 | return out, lblerr 663 | } 664 | out=MergeMaps(out,lbls) 665 | } 666 | log.Debug("global labels=",out) 667 | return out, nil 668 | 669 | } 670 | 671 | 672 | 673 | func (nc *networksetsController) SetupGlobalNetworksets(name string) (error) { 674 | 675 | //Collect all policy in current namespace 676 | global_rules := make(map[string][]string) 677 | calicoPolicies, err := nc.calicoClient.GlobalNetworkPolicies().List(nc.ctx, options.ListOptions{}) 678 | if err != nil { 679 | return err 680 | } 681 | for _, policy := range calicoPolicies.Items { 682 | 683 | log.Debug("Find global policy: ",policy.Name) 684 | //Check rules for selector 685 | rl,err_rule:=nc.GetGlobalNetworkPolicyRuleLabels( policy.Name ) 686 | if err_rule != nil { 687 | log.Info(err_rule) 688 | nc.metric_counter["error_counter"].Inc() 689 | continue 690 | } 691 | //In namespace_rules collect all label vaules 692 | global_rules = MergeMaps(global_rules,rl) 693 | } 694 | log.Debug("global rule labels = ",global_rules) 695 | 696 | //Get GlobalNetwrokSets 697 | current_globalnetworksets := make(map[string]map[string]bool) 698 | calicoGlobalNetworksets, err := nc.calicoClient.GlobalNetworkSets().List(nc.ctx, options.ListOptions{}) 699 | if err != nil { 700 | return err 701 | } 702 | //Find networksets with label from configuration 703 | for _, globalnetworkset := range calicoGlobalNetworksets.Items { 704 | if (metav1.HasAnnotation(globalnetworkset.ObjectMeta,"automatization/networksets-controller")) { 705 | meta_labels := globalnetworkset.ObjectMeta.GetLabels() 706 | for lbl,lbl_slice := range global_rules { 707 | for _,val := range lbl_slice { 708 | if meta_labels[lbl] == val { 709 | log.Debug("Find global networkset ",globalnetworkset.Name," with label ",lbl," = ",val) 710 | if _,ok := current_globalnetworksets[lbl]; !ok { 711 | current_globalnetworksets[lbl] = map[string]bool{val: true} 712 | } else { 713 | current_globalnetworksets[lbl][val] = true 714 | } 715 | } 716 | } 717 | } 718 | 719 | } 720 | } 721 | log.Debug("current global networksets ",current_globalnetworksets) 722 | 723 | //Create networksets 724 | for lbl,lbl_slice := range global_rules { 725 | for _,val := range lbl_slice { 726 | if _,ok := current_globalnetworksets[lbl][val];!ok { 727 | log.Info("Create global networkset ",lbl," = ",val) 728 | re := regexp.MustCompile(`[^a-z0-9\-]`) 729 | name_ns := re.ReplaceAllString(strings.ToLower("auto-ns-"+val),"-") 730 | ns, ns_err := nc.GlobalNetworksetDef( name_ns , lbl, val) 731 | if ns_err != nil { 732 | log.Info(ns_err) 733 | nc.metric_counter["error_counter"].Inc() 734 | continue 735 | } 736 | _, err := nc.calicoClient.GlobalNetworkSets().Create(nc.ctx, ns, options.SetOptions{}) 737 | if err != nil { 738 | log.Info(err) 739 | nc.metric_counter["error_counter"].Inc() 740 | continue 741 | } 742 | } 743 | } 744 | } 745 | 746 | //Delete networksets 747 | for _, globalnetworkset := range calicoGlobalNetworksets.Items { 748 | if (metav1.HasAnnotation(globalnetworkset.ObjectMeta,"automatization/networksets-controller")) { 749 | meta_labels := globalnetworkset.ObjectMeta.GetLabels() 750 | for_delete := true 751 | for del_lbl, val_lbl := range meta_labels { 752 | if v,ok := current_globalnetworksets[del_lbl][val_lbl]; ok && v { 753 | for_delete = false 754 | continue 755 | } 756 | } 757 | if for_delete { 758 | ns_name:=globalnetworkset.GetName() 759 | log.Info("Delete GlobalNetworkSet ", ns_name) 760 | _, err := nc.calicoClient.GlobalNetworkSets().Delete(nc.ctx, ns_name, options.DeleteOptions{}) 761 | if err != nil { 762 | return err 763 | } 764 | } 765 | } 766 | } 767 | 768 | return nil 769 | } 770 | 771 | 772 | func (nc *networksetsController) startGlobalNetworkpolicyWatching(stopCh <-chan struct{}, s cache.SharedIndexInformer) { 773 | 774 | 775 | handlers := cache.ResourceEventHandlerFuncs{ 776 | AddFunc: func(obj interface{}) { 777 | 778 | u := obj.(*unstructured.Unstructured) 779 | //Hack for strange calico networkpolicy names 780 | gnp_name:=strings.Replace(u.GetName(), "default.","",1) 781 | 782 | log.Debug("create event global network policy "," gnp:",gnp_name,")") 783 | err := nc.SetupGlobalNetworksets( gnp_name ) 784 | if err!= nil { 785 | log.Info(err) 786 | nc.metric_counter["error_counter"].Inc() 787 | } 788 | 789 | }, 790 | UpdateFunc: func(oldObj, obj interface{}) { 791 | 792 | u := obj.(*unstructured.Unstructured) 793 | //Hack for strange calico networkpolicy names 794 | gnp_name:=strings.Replace(u.GetName(), "default.","",1) 795 | 796 | log.Debug("create event global network policy "," gnp:",gnp_name,")") 797 | err := nc.SetupGlobalNetworksets( gnp_name ) 798 | if err!= nil { 799 | log.Info(err) 800 | nc.metric_counter["error_counter"].Inc() 801 | } 802 | 803 | 804 | }, 805 | DeleteFunc: func(obj interface{}) { 806 | 807 | u := obj.(*unstructured.Unstructured) 808 | //Hack for strange calico networkpolicy names 809 | gnp_name:=strings.Replace(u.GetName(), "default.","",1) 810 | 811 | log.Debug("create event global network policy "," gnp:",gnp_name,")") 812 | err := nc.SetupGlobalNetworksets( gnp_name ) 813 | if err!= nil { 814 | log.Info(err) 815 | nc.metric_counter["error_counter"].Inc() 816 | } 817 | 818 | }, 819 | } 820 | 821 | s.AddEventHandler(handlers) 822 | s.Run(stopCh) 823 | } 824 | 825 | 826 | 827 | 828 | func (nc *networksetsController) Init() (error){ 829 | 830 | nc.runtime_viper = viper.New() 831 | nc.runtime_viper.SetConfigName("networksets-controller") 832 | nc.runtime_viper.AddConfigPath("/config-k8s") 833 | nc.runtime_viper.WatchConfig() 834 | err := nc.runtime_viper.ReadInConfig() 835 | if err != nil { 836 | fmt.Printf("Fatal error config file: %s \n", err) 837 | return err 838 | } 839 | 840 | //Get clients for calico and k8s 841 | var err_calico error 842 | nc.clientset, nc.calicoClient, err_calico = nc.getClients("") 843 | if err_calico != nil { 844 | return err_calico 845 | } 846 | 847 | nc.ctx = context.Background() 848 | 849 | is_debug := nc.runtime_viper.GetBool("main.debug") 850 | if is_debug { 851 | log.SetLevel(log.DebugLevel) 852 | } 853 | 854 | //Prometheus metrics 855 | nc.metric_counter = make(map[string]prometheus.Counter) 856 | 857 | nc.metric_counter["error_counter"] = promauto.NewCounter(prometheus.CounterOpts{ 858 | Name: "networksets_controller_errors", 859 | Help: "Count of errors", 860 | }) 861 | 862 | 863 | go nc.run() 864 | 865 | reload_time := nc.runtime_viper.GetDuration("main.reload_time") 866 | ticker := time.NewTicker(time.Millisecond * reload_time) 867 | go func() { 868 | for _ = range ticker.C { 869 | err_reload := nc.ReloadNetworksets() 870 | if err_reload != nil { 871 | log.Info(err_reload) 872 | nc.metric_counter["error_counter"].Inc() 873 | } 874 | } 875 | }() 876 | 877 | globalticker := time.NewTicker(time.Millisecond * reload_time) 878 | go func() { 879 | for _ = range globalticker.C { 880 | err_reload := nc.ReloadGlobalNetworksets() 881 | if err_reload != nil { 882 | log.Info(err_reload) 883 | nc.metric_counter["error_counter"].Inc() 884 | } 885 | } 886 | }() 887 | 888 | return nil 889 | } 890 | 891 | func main() { 892 | 893 | 894 | var nc networksetsController 895 | err:=nc.Init() 896 | if err!=nil { 897 | log.Info("Fail to start networksets controller", err) 898 | return 899 | } 900 | 901 | http.Handle("/metrics", promhttp.Handler()) 902 | 903 | service_port := nc.runtime_viper.GetString("main.service_port") 904 | 905 | log.Fatal(http.ListenAndServe(":"+service_port, nil)) 906 | } 907 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/projectcalico/libcalico-go/v3 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/banzaicloud/k8s-objectmatcher v1.5.0 // indirect 7 | github.com/ghodss/yaml v1.0.0 // indirect 8 | github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 9 | github.com/projectcalico/kube-controllers v3.8.9+incompatible 10 | github.com/projectcalico/libcalico-go v1.7.2-0.20201211095255-5d3be5f39c7c 11 | github.com/prometheus/client_golang v1.9.0 12 | github.com/sirupsen/logrus v1.6.0 13 | github.com/spf13/viper v1.7.1 // indirect 14 | k8s.io/api v0.20.1 15 | k8s.io/apimachinery v0.20.1 16 | k8s.io/apiserver v0.20.1 17 | k8s.io/client-go v0.20.1 18 | ) 19 | -------------------------------------------------------------------------------- /k8s-mainfest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: networksets-controller 5 | namespace: calico-networksets-controller 6 | data: 7 | networksets-controller.toml: | 8 | [main] 9 | reload_time=60000 10 | debug=false 11 | service_port=8080 12 | [DNS_RESOLVER] 13 | url="http://dns-resolver.calico-networksets-controller:8080/dns" 14 | --- 15 | apiVersion: v1 16 | kind: Secret 17 | metadata: 18 | annotations: 19 | kubernetes.io/service-account.name: calico-networksets-controller 20 | name: calico-networksets-controller-token 21 | namespace: calico-networksets-controller 22 | type: kubernetes.io/service-account-token 23 | --- 24 | apiVersion: v1 25 | kind: ServiceAccount 26 | metadata: 27 | name: calico-networksets-controller 28 | namespace: calico-networksets-controller 29 | labels: 30 | vault: secret 31 | --- 32 | apiVersion: rbac.authorization.k8s.io/v1 33 | kind: ClusterRole 34 | metadata: 35 | labels: 36 | app: calico-networksets-controller 37 | name: calico-networksets-controller 38 | rules: 39 | - apiGroups: ["crd.projectcalico.org"] 40 | resources: 41 | - networksets 42 | - globalnetworksets 43 | verbs: 44 | - get 45 | - list 46 | - watch 47 | - create 48 | - update 49 | - patch 50 | - delete 51 | - apiGroups: ["networking.k8s.io","crd.projectcalico.org"] 52 | resources: 53 | - networkpolicies 54 | - globalnetworkpolicies 55 | verbs: 56 | - get 57 | - list 58 | - watch 59 | --- 60 | apiVersion: rbac.authorization.k8s.io/v1 61 | kind: ClusterRoleBinding 62 | metadata: 63 | name: calico-networksets-controller 64 | roleRef: 65 | apiGroup: rbac.authorization.k8s.io 66 | kind: ClusterRole 67 | name: calico-networksets-controller 68 | subjects: 69 | - kind: ServiceAccount 70 | name: calico-networksets-controller 71 | namespace: calico-networksets-controller 72 | --- 73 | apiVersion: apps/v1 74 | kind: Deployment 75 | metadata: 76 | name: calico-networksets-controller 77 | namespace: calico-networksets-controller 78 | spec: 79 | selector: 80 | matchLabels: 81 | app: calico-networksets-controller 82 | replicas: 1 83 | template: 84 | metadata: 85 | labels: 86 | app: calico-networksets-controller 87 | spec: 88 | volumes: 89 | - name: networksets-controller 90 | configMap: 91 | name: networksets-controller 92 | items: 93 | - key: networksets-controller.toml 94 | path: networksets-controller.toml 95 | containers: 96 | - name: calico-networksets-controller 97 | image: ktrufanov/k8s-calico-networksets-controller:0.0.11 98 | env: 99 | - name: SERVICE_PORT 100 | value: "8080" 101 | imagePullPolicy: Always 102 | volumeMounts: 103 | - name: networksets-controller 104 | mountPath: /config-k8s 105 | ports: 106 | - name: http 107 | containerPort: 8080 108 | --- 109 | apiVersion: v1 110 | kind: Service 111 | metadata: 112 | name: calico-networksets-controller 113 | namespace: calico-networksets-controller 114 | labels: 115 | app: calico-networksets-controller 116 | spec: 117 | ports: 118 | - port: 8080 119 | targetPort: http 120 | protocol: TCP 121 | name: http 122 | selector: 123 | app: calico-networksets-controller 124 | --------------------------------------------------------------------------------