mirror of https://github.com/k3s-io/k3s
Merge pull request #1672 from galal-hussein/helm_update
helm-controller and kine updatepull/1677/head
commit
b2cab73d27
|
@ -29,7 +29,7 @@ ENV SELINUX $SELINUX
|
||||||
|
|
||||||
ARG DQLITE=true
|
ARG DQLITE=true
|
||||||
ENV DQLITE $DQLITE
|
ENV DQLITE $DQLITE
|
||||||
COPY --from=rancher/dqlite-build:v1.3.1-r1 /dist/artifacts /usr/src/
|
COPY --from=rancher/dqlite-build:v1.4.1-r1 /dist/artifacts /usr/src/
|
||||||
RUN if [ "$DQLITE" = true ]; then \
|
RUN if [ "$DQLITE" = true ]; then \
|
||||||
tar xzf /usr/src/dqlite.tgz -C / && \
|
tar xzf /usr/src/dqlite.tgz -C / && \
|
||||||
apk add --allow-untrusted /usr/local/packages/*.apk \
|
apk add --allow-untrusted /usr/local/packages/*.apk \
|
||||||
|
|
11
go.mod
11
go.mod
|
@ -61,12 +61,13 @@ replace (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Azure/go-autorest v14.0.1+incompatible // indirect
|
||||||
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
||||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||||
github.com/bhendo/go-powershell v0.0.0-20190719160123-219e7fb4e41e // indirect
|
github.com/bhendo/go-powershell v0.0.0-20190719160123-219e7fb4e41e // indirect
|
||||||
github.com/bronze1man/goStrongswanVici v0.0.0-20190828090544-27d02f80ba40 // indirect
|
github.com/bronze1man/goStrongswanVici v0.0.0-20190828090544-27d02f80ba40 // indirect
|
||||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 // indirect
|
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 // indirect
|
||||||
github.com/canonical/go-dqlite v1.3.0
|
github.com/canonical/go-dqlite v1.5.1
|
||||||
github.com/containerd/cgroups v0.0.0-00010101000000-000000000000 // indirect
|
github.com/containerd/cgroups v0.0.0-00010101000000-000000000000 // indirect
|
||||||
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69
|
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69
|
||||||
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 // indirect
|
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 // indirect
|
||||||
|
@ -100,11 +101,11 @@ require (
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/rakelkar/gonetsh v0.0.0-20190719023240-501daadcadf8 // indirect
|
github.com/rakelkar/gonetsh v0.0.0-20190719023240-501daadcadf8 // indirect
|
||||||
github.com/rancher/dynamiclistener v0.2.0
|
github.com/rancher/dynamiclistener v0.2.0
|
||||||
github.com/rancher/helm-controller v0.4.2-0.20200326195131-eb51d4fa9d8d
|
github.com/rancher/helm-controller v0.5.0
|
||||||
github.com/rancher/kine v0.3.5
|
github.com/rancher/kine v0.4.0
|
||||||
github.com/rancher/remotedialer v0.2.0
|
github.com/rancher/remotedialer v0.2.0
|
||||||
github.com/rancher/wrangler v0.5.4-0.20200326191509-4054411d9736
|
github.com/rancher/wrangler v0.6.1
|
||||||
github.com/rancher/wrangler-api v0.5.1-0.20200326194427-c13310506d04
|
github.com/rancher/wrangler-api v0.6.0
|
||||||
github.com/rootless-containers/rootlesskit v0.7.2
|
github.com/rootless-containers/rootlesskit v0.7.2
|
||||||
github.com/sirupsen/logrus v1.4.2
|
github.com/sirupsen/logrus v1.4.2
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
|
|
17
go.sum
17
go.sum
|
@ -7,6 +7,10 @@ github.com/Azure/azure-sdk-for-go v35.0.0+incompatible h1:PkmdmQUmeSdQQ5258f4SyC
|
||||||
github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||||
|
github.com/Azure/go-autorest v11.1.2+incompatible h1:viZ3tV5l4gE2Sw0xrasFHytCGtzYCrT+um/rrSQ1BfA=
|
||||||
|
github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||||
|
github.com/Azure/go-autorest v14.0.1+incompatible h1:YhojO9jolWIvvTW7ORhz2ZSNF6Q1TbLqUunKd3jrtyw=
|
||||||
|
github.com/Azure/go-autorest v14.0.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||||
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=
|
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=
|
||||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||||
github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU=
|
github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU=
|
||||||
|
@ -95,6 +99,8 @@ github.com/caddyserver/caddy v1.0.3/go.mod h1:G+ouvOY32gENkJC+jhgl62TyhvqEsFaDiZ
|
||||||
github.com/canonical/go-dqlite v1.2.0/go.mod h1:wp00vfMvPYgNCyxcPdHB5XExmDoCGoPUGymloAQT17Y=
|
github.com/canonical/go-dqlite v1.2.0/go.mod h1:wp00vfMvPYgNCyxcPdHB5XExmDoCGoPUGymloAQT17Y=
|
||||||
github.com/canonical/go-dqlite v1.3.0 h1:c+7eGZfh0K7yCmGrBkNRGZdY8R8+2jSSkz6Zr3YCjJE=
|
github.com/canonical/go-dqlite v1.3.0 h1:c+7eGZfh0K7yCmGrBkNRGZdY8R8+2jSSkz6Zr3YCjJE=
|
||||||
github.com/canonical/go-dqlite v1.3.0/go.mod h1:wp00vfMvPYgNCyxcPdHB5XExmDoCGoPUGymloAQT17Y=
|
github.com/canonical/go-dqlite v1.3.0/go.mod h1:wp00vfMvPYgNCyxcPdHB5XExmDoCGoPUGymloAQT17Y=
|
||||||
|
github.com/canonical/go-dqlite v1.5.1 h1:1YjtIrFsC1A3XlgsX38ARAiKhvkZS63PqsEd8z3T4yU=
|
||||||
|
github.com/canonical/go-dqlite v1.5.1/go.mod h1:wp00vfMvPYgNCyxcPdHB5XExmDoCGoPUGymloAQT17Y=
|
||||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/prettybench v0.0.0-20150116022406-03b8cfe5406c/go.mod h1:Xe6ZsFhtM8HrDku0pxJ3/Lr51rwykrzgFwpmTzleatY=
|
github.com/cespare/prettybench v0.0.0-20150116022406-03b8cfe5406c/go.mod h1:Xe6ZsFhtM8HrDku0pxJ3/Lr51rwykrzgFwpmTzleatY=
|
||||||
|
@ -640,8 +646,14 @@ github.com/rancher/flannel v0.11.0-k3s.2 h1:0GVr5ORAIvcri1LYTE8eMQ+NrRbuPeIniPaW
|
||||||
github.com/rancher/flannel v0.11.0-k3s.2/go.mod h1:Hn4ZV+eq0LhLZP63xZnxdGwXEoRSxs5sxELxu27M3UA=
|
github.com/rancher/flannel v0.11.0-k3s.2/go.mod h1:Hn4ZV+eq0LhLZP63xZnxdGwXEoRSxs5sxELxu27M3UA=
|
||||||
github.com/rancher/helm-controller v0.4.2-0.20200326195131-eb51d4fa9d8d h1:6w5gCRgJzWEGdGi/0Xv4XXuGZY8wgWduRA9A+4c1N8I=
|
github.com/rancher/helm-controller v0.4.2-0.20200326195131-eb51d4fa9d8d h1:6w5gCRgJzWEGdGi/0Xv4XXuGZY8wgWduRA9A+4c1N8I=
|
||||||
github.com/rancher/helm-controller v0.4.2-0.20200326195131-eb51d4fa9d8d/go.mod h1:3jCGmvjp3bFnbeuHL4HiODje9ZYJ/ujUBNtXHFXrwlM=
|
github.com/rancher/helm-controller v0.4.2-0.20200326195131-eb51d4fa9d8d/go.mod h1:3jCGmvjp3bFnbeuHL4HiODje9ZYJ/ujUBNtXHFXrwlM=
|
||||||
|
github.com/rancher/helm-controller v0.5.0 h1:BY5PG3dz6GWct2O9r8mFv73tZ7E5U9uI89QwMBXV83E=
|
||||||
|
github.com/rancher/helm-controller v0.5.0/go.mod h1:kEtAI/0AylXIplxWkIRR2xl3nhd4jZ6Wke1nvE/sKUs=
|
||||||
github.com/rancher/kine v0.3.5 h1:Tm4eOtejpnzs1WFBrXj76lCLvX9czLlTkgqUk9luCQk=
|
github.com/rancher/kine v0.3.5 h1:Tm4eOtejpnzs1WFBrXj76lCLvX9czLlTkgqUk9luCQk=
|
||||||
github.com/rancher/kine v0.3.5/go.mod h1:xEMl0tLCva9/9me7mXJ3m9Vo6yqHgC4OU3NiK4CPrGQ=
|
github.com/rancher/kine v0.3.5/go.mod h1:xEMl0tLCva9/9me7mXJ3m9Vo6yqHgC4OU3NiK4CPrGQ=
|
||||||
|
github.com/rancher/kine v0.3.6-0.20200422224205-0a0f5b924129 h1:4HYvCG8+pfkBlpYLv/5ZOdxagg3jTszMOqMjamMQ0hA=
|
||||||
|
github.com/rancher/kine v0.3.6-0.20200422224205-0a0f5b924129/go.mod h1:xEMl0tLCva9/9me7mXJ3m9Vo6yqHgC4OU3NiK4CPrGQ=
|
||||||
|
github.com/rancher/kine v0.4.0 h1:1IhWy3TzjExG8xnj46eyUEWdzqNAD1WrgL4eEBKm6Uc=
|
||||||
|
github.com/rancher/kine v0.4.0/go.mod h1:IImtCJ68AIkE+VY/kUI0NkyJL5q5WzO8QvMsSXqbrpA=
|
||||||
github.com/rancher/kubernetes v1.18.2-k3s.1 h1:LhWNObWF7dL/+T57LkYpuRKtsCBpt0P5G6dRVFG+Ncs=
|
github.com/rancher/kubernetes v1.18.2-k3s.1 h1:LhWNObWF7dL/+T57LkYpuRKtsCBpt0P5G6dRVFG+Ncs=
|
||||||
github.com/rancher/kubernetes v1.18.2-k3s.1/go.mod h1:z8xjOOO1Ljz+TaHpOxVGC7cxtF32TesIamoQ+BZrVS0=
|
github.com/rancher/kubernetes v1.18.2-k3s.1/go.mod h1:z8xjOOO1Ljz+TaHpOxVGC7cxtF32TesIamoQ+BZrVS0=
|
||||||
github.com/rancher/kubernetes/staging/src/k8s.io/api v1.18.2-k3s.1 h1:tYDY9g8+xLwUcsG9T6Xg7cBkO/vgU6yv7cQKqUN6NDE=
|
github.com/rancher/kubernetes/staging/src/k8s.io/api v1.18.2-k3s.1 h1:tYDY9g8+xLwUcsG9T6Xg7cBkO/vgU6yv7cQKqUN6NDE=
|
||||||
|
@ -693,9 +705,14 @@ github.com/rancher/wrangler v0.4.0 h1:iLvuJcZkd38E3RGG74dFMMNEju0PeTzfT1PQiv5okV
|
||||||
github.com/rancher/wrangler v0.4.0/go.mod h1:1cR91WLhZgkZ+U4fV9nVuXqKurWbgXcIReU4wnQvTN8=
|
github.com/rancher/wrangler v0.4.0/go.mod h1:1cR91WLhZgkZ+U4fV9nVuXqKurWbgXcIReU4wnQvTN8=
|
||||||
github.com/rancher/wrangler v0.5.4-0.20200326191509-4054411d9736 h1:hqpVLgNUxU5sQUV6SzJPMY8Fy7T9Qht2QkA2Q7O/SH0=
|
github.com/rancher/wrangler v0.5.4-0.20200326191509-4054411d9736 h1:hqpVLgNUxU5sQUV6SzJPMY8Fy7T9Qht2QkA2Q7O/SH0=
|
||||||
github.com/rancher/wrangler v0.5.4-0.20200326191509-4054411d9736/go.mod h1:L4HtjPeX8iqLgsxfJgz+JjKMcX2q3qbRXSeTlC/CSd4=
|
github.com/rancher/wrangler v0.5.4-0.20200326191509-4054411d9736/go.mod h1:L4HtjPeX8iqLgsxfJgz+JjKMcX2q3qbRXSeTlC/CSd4=
|
||||||
|
github.com/rancher/wrangler v0.6.0/go.mod h1:L4HtjPeX8iqLgsxfJgz+JjKMcX2q3qbRXSeTlC/CSd4=
|
||||||
|
github.com/rancher/wrangler v0.6.1 h1:7tyLk/FV2zCQkYg5SEtT4lSlsHNwa5yMOa797/VJhiQ=
|
||||||
|
github.com/rancher/wrangler v0.6.1/go.mod h1:L4HtjPeX8iqLgsxfJgz+JjKMcX2q3qbRXSeTlC/CSd4=
|
||||||
github.com/rancher/wrangler-api v0.2.0/go.mod h1:zTPdNLZO07KvRaVOx6XQbKBSV55Fnn4s7nqmrMPJqd8=
|
github.com/rancher/wrangler-api v0.2.0/go.mod h1:zTPdNLZO07KvRaVOx6XQbKBSV55Fnn4s7nqmrMPJqd8=
|
||||||
github.com/rancher/wrangler-api v0.5.1-0.20200326194427-c13310506d04 h1:y55e+kUaz/UswjN/oJdqHWMuoCG1FxwZJkxJEUONZZE=
|
github.com/rancher/wrangler-api v0.5.1-0.20200326194427-c13310506d04 h1:y55e+kUaz/UswjN/oJdqHWMuoCG1FxwZJkxJEUONZZE=
|
||||||
github.com/rancher/wrangler-api v0.5.1-0.20200326194427-c13310506d04/go.mod h1:R3nemXoECcrDqXDSHdY7yJay4j42TeEkU79Hep0rdJ8=
|
github.com/rancher/wrangler-api v0.5.1-0.20200326194427-c13310506d04/go.mod h1:R3nemXoECcrDqXDSHdY7yJay4j42TeEkU79Hep0rdJ8=
|
||||||
|
github.com/rancher/wrangler-api v0.6.0 h1:d/b0AkgZ+x41EYLIcUJiogtU3Y11Mqss2zr9VEKycRk=
|
||||||
|
github.com/rancher/wrangler-api v0.6.0/go.mod h1:RbuDkPNHhxcXuwAbLVvEAhH+UPAh+MIkpEd2fcGc0MM=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||||
github.com/robfig/cron v1.1.0 h1:jk4/Hud3TTdcrJgUOBgsqrZBarcxl6ADIjSC2iniwLY=
|
github.com/robfig/cron v1.1.0 h1:jk4/Hud3TTdcrJgUOBgsqrZBarcxl6ADIjSC2iniwLY=
|
||||||
github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
||||||
|
|
|
@ -52,18 +52,23 @@ func NewFactoryFromConfigOrDie(config *rest.Config) *Factory {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactoryFromConfig(config *rest.Config) (*Factory, error) {
|
func NewFactoryFromConfig(config *rest.Config) (*Factory, error) {
|
||||||
cs, err := clientset.NewForConfig(config)
|
return NewFactoryFromConfigWithOptions(config, nil)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
informerFactory := informers.NewSharedInformerFactory(cs, 2*time.Hour)
|
|
||||||
return NewFactory(cs, informerFactory), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {
|
func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {
|
||||||
if namespace == "" {
|
return NewFactoryFromConfigWithOptions(config, &FactoryOptions{
|
||||||
return NewFactoryFromConfig(config)
|
Namespace: namespace,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type FactoryOptions struct {
|
||||||
|
Namespace string
|
||||||
|
Resync time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &FactoryOptions{}
|
||||||
}
|
}
|
||||||
|
|
||||||
cs, err := clientset.NewForConfig(config)
|
cs, err := clientset.NewForConfig(config)
|
||||||
|
@ -71,7 +76,17 @@ func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
informerFactory := informers.NewSharedInformerFactoryWithOptions(cs, 2*time.Hour, informers.WithNamespace(namespace))
|
resync := opts.Resync
|
||||||
|
if resync == 0 {
|
||||||
|
resync = 2 * time.Hour
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Namespace == "" {
|
||||||
|
informerFactory := informers.NewSharedInformerFactory(cs, resync)
|
||||||
|
return NewFactory(cs, informerFactory), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
informerFactory := informers.NewSharedInformerFactoryWithOptions(cs, resync, informers.WithNamespace(opts.Namespace))
|
||||||
return NewFactory(cs, informerFactory), nil
|
return NewFactory(cs, informerFactory), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"github.com/rancher/wrangler/pkg/apply"
|
"github.com/rancher/wrangler/pkg/apply"
|
||||||
"github.com/rancher/wrangler/pkg/condition"
|
"github.com/rancher/wrangler/pkg/condition"
|
||||||
"github.com/rancher/wrangler/pkg/generic"
|
"github.com/rancher/wrangler/pkg/generic"
|
||||||
|
"github.com/rancher/wrangler/pkg/kv"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
@ -267,6 +268,7 @@ func RegisterAddonGeneratingHandler(ctx context.Context, controller AddonControl
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
statusHandler.opts = *opts
|
statusHandler.opts = *opts
|
||||||
}
|
}
|
||||||
|
controller.OnChange(ctx, name, statusHandler.Remove)
|
||||||
RegisterAddonStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
RegisterAddonStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +283,7 @@ func (a *addonStatusHandler) sync(key string, obj *v1.Addon) (*v1.Addon, error)
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
origStatus := obj.Status
|
origStatus := obj.Status.DeepCopy()
|
||||||
obj = obj.DeepCopy()
|
obj = obj.DeepCopy()
|
||||||
newStatus, err := a.handler(obj, obj.Status)
|
newStatus, err := a.handler(obj, obj.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -289,16 +291,16 @@ func (a *addonStatusHandler) sync(key string, obj *v1.Addon) (*v1.Addon, error)
|
||||||
newStatus = *origStatus.DeepCopy()
|
newStatus = *origStatus.DeepCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.Status = newStatus
|
|
||||||
if a.condition != "" {
|
if a.condition != "" {
|
||||||
if errors.IsConflict(err) {
|
if errors.IsConflict(err) {
|
||||||
a.condition.SetError(obj, "", nil)
|
a.condition.SetError(&newStatus, "", nil)
|
||||||
} else {
|
} else {
|
||||||
a.condition.SetError(obj, "", err)
|
a.condition.SetError(&newStatus, "", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !equality.Semantic.DeepEqual(origStatus, obj.Status) {
|
if !equality.Semantic.DeepEqual(origStatus, &newStatus) {
|
||||||
var newErr error
|
var newErr error
|
||||||
|
obj.Status = newStatus
|
||||||
obj, newErr = a.client.UpdateStatus(obj)
|
obj, newErr = a.client.UpdateStatus(obj)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = newErr
|
err = newErr
|
||||||
|
@ -315,29 +317,28 @@ type addonGeneratingHandler struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *addonGeneratingHandler) Remove(key string, obj *v1.Addon) (*v1.Addon, error) {
|
||||||
|
if obj != nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = &v1.Addon{}
|
||||||
|
obj.Namespace, obj.Name = kv.RSplit(key, "/")
|
||||||
|
obj.SetGroupVersionKind(a.gvk)
|
||||||
|
|
||||||
|
return nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
WithOwner(obj).
|
||||||
|
WithSetID(a.name).
|
||||||
|
ApplyObjects()
|
||||||
|
}
|
||||||
|
|
||||||
func (a *addonGeneratingHandler) Handle(obj *v1.Addon, status v1.AddonStatus) (v1.AddonStatus, error) {
|
func (a *addonGeneratingHandler) Handle(obj *v1.Addon, status v1.AddonStatus) (v1.AddonStatus, error) {
|
||||||
objs, newStatus, err := a.AddonGeneratingHandler(obj, status)
|
objs, newStatus, err := a.AddonGeneratingHandler(obj, status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newStatus, err
|
return newStatus, err
|
||||||
}
|
}
|
||||||
|
|
||||||
apply := a.apply
|
return newStatus, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
|
||||||
if !a.opts.DynamicLookup {
|
|
||||||
apply = apply.WithStrictCaching()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowCrossNamespace && !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithSetOwnerReference(true, false).
|
|
||||||
WithDefaultNamespace(obj.GetNamespace()).
|
|
||||||
WithListerNamespace(obj.GetNamespace())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithRestrictClusterScoped()
|
|
||||||
}
|
|
||||||
|
|
||||||
return newStatus, apply.
|
|
||||||
WithOwner(obj).
|
WithOwner(obj).
|
||||||
WithSetID(a.name).
|
WithSetID(a.name).
|
||||||
ApplyObjects(objs...)
|
ApplyObjects(objs...)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
docker.io/rancher/coredns-coredns:1.6.3
|
docker.io/rancher/coredns-coredns:1.6.3
|
||||||
docker.io/rancher/klipper-helm:v0.2.3
|
docker.io/rancher/klipper-helm:v0.2.5
|
||||||
docker.io/rancher/klipper-lb:v0.1.2
|
docker.io/rancher/klipper-lb:v0.1.2
|
||||||
docker.io/rancher/library-traefik:1.7.19
|
docker.io/rancher/library-traefik:1.7.19
|
||||||
docker.io/rancher/local-path-provisioner:v0.0.11
|
docker.io/rancher/local-path-provisioner:v0.0.11
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
.sqlite
|
.sqlite
|
||||||
|
cmd/dqlite/dqlite
|
||||||
|
cmd/dqlite-demo/dqlite-demo
|
||||||
demo
|
demo
|
||||||
profile.coverprofile
|
profile.coverprofile
|
||||||
overalls.coverprofile
|
overalls.coverprofile
|
||||||
|
|
|
@ -10,9 +10,32 @@ Usage
|
||||||
|
|
||||||
The best way to understand how to use the ```go-dqlite``` package is probably by
|
The best way to understand how to use the ```go-dqlite``` package is probably by
|
||||||
looking at the source code of the [demo
|
looking at the source code of the [demo
|
||||||
program](https://github.com/canonical/go-dqlite/tree/master/cmd/dqlite-demo) and
|
program](https://github.com/canonical/go-dqlite/tree/master/cmd/dqlite-demo/main.go) and
|
||||||
use it as example.
|
use it as example.
|
||||||
|
|
||||||
|
In general your application will use code such as:
|
||||||
|
|
||||||
|
|
||||||
|
```go
|
||||||
|
dir := "/path/to/data/directory"
|
||||||
|
address := "1.2.3.4:666" // Unique node address
|
||||||
|
cluster := []string{...} // Optional list of existing nodes, when starting a new node
|
||||||
|
app, err := app.New(dir, app.WithAddress(address), app.WithCluster(cluster))
|
||||||
|
if err != nil {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := app.Open(context.Background(), "my-database")
|
||||||
|
if err != nil {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// db is a *sql.DB object
|
||||||
|
if _, err := db.Exec("CREATE TABLE my_table (n INT)"); err != nil
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Build
|
Build
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -50,42 +73,61 @@ go install -tags libsqlite3 ./cmd/dqlite-demo
|
||||||
|
|
||||||
from the top-level directory of this repository.
|
from the top-level directory of this repository.
|
||||||
|
|
||||||
Once the ```dqlite-demo``` binary is installed, start three nodes of the demo
|
This builds a demo dqlite application, which exposes a simple key/value store
|
||||||
application, respectively with IDs ```1```, ```2,``` and ```3```:
|
over an HTTP API.
|
||||||
|
|
||||||
|
Once the `dqlite-demo` binary is installed (normally under `~/go/bin`),
|
||||||
|
start three nodes of the demo application:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dqlite-demo start 1 &
|
dqlite-demo --api 127.0.0.1:8001 --db 127.0.0.1:9001 &
|
||||||
dqlite-demo start 2 &
|
dqlite-demo --api 127.0.0.1:8002 --db 127.0.0.1:9002 --join 127.0.0.1:9001 &
|
||||||
dqlite-demo start 3 &
|
dqlite-demo --api 127.0.0.1:8003 --db 127.0.0.1:9003 --join 127.0.0.1:9001 &
|
||||||
```
|
```
|
||||||
|
|
||||||
The node with ID ```1``` automatically becomes the leader of a single node
|
The `--api` flag tells the demo program where to expose its HTTP API.
|
||||||
cluster, while the nodes with IDs ```2``` and ```3``` are waiting to be notified
|
|
||||||
what cluster they belong to. Let's make nodes ```2``` and ```3``` join the
|
The `--db` flag tells the demo program to use the given address for internal
|
||||||
cluster:
|
database replication.
|
||||||
|
|
||||||
|
The `--join` flag is optional and should be used only for additional nodes after
|
||||||
|
the first one. It informs them about the existing cluster, so they can
|
||||||
|
automatically join it.
|
||||||
|
|
||||||
|
Now we can start using the cluster. Let's insert a key pair:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dqlite-demo add 2
|
curl -X PUT -d my-key http://127.0.0.1:8001/my-value
|
||||||
dqlite-demo add 3
|
|
||||||
```
|
|
||||||
|
|
||||||
Now we can start using the cluster. The demo application is just a simple
|
|
||||||
key/value store that stores data in a SQLite table. Let's insert a key pair:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
dqlite-demo update my-key my-value
|
|
||||||
```
|
```
|
||||||
|
|
||||||
and then retrive it from the database:
|
and then retrive it from the database:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dqlite-demo query my-key
|
curl http://127.0.0.1:8001/my-value
|
||||||
```
|
```
|
||||||
|
|
||||||
Currently node ```1``` is the leader. If we stop it and then try to query the
|
Currently the first node is the leader. If we stop it and then try to query the
|
||||||
key again we'll notice that the ```query``` command hangs for a bit waiting for
|
key again curl will fail, but we can simply change the endpoint to another node
|
||||||
the failover to occur and for another node to step up as leader:
|
and things will work since an automatic failover has taken place:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kill -TERM %1; curl http://127.0.0.1:8002/my-value
|
||||||
|
```
|
||||||
|
|
||||||
|
Shell
|
||||||
|
------
|
||||||
|
|
||||||
|
A basic SQLite-like dqlite shell can be built with:
|
||||||
|
|
||||||
```
|
```
|
||||||
kill -TERM %1; sleep 0.1; dqlite-demo query my-key; dqlite-demo cluster
|
go install -tags libsqlite3 ./cmd/dqlite
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can test it with the `dqlite-demo` with:
|
||||||
|
|
||||||
|
```
|
||||||
|
dqlite -s 127.0.0.1:9001
|
||||||
|
```
|
||||||
|
|
||||||
|
It supports normal SQL queries plus the special `.cluster` and `.leader`
|
||||||
|
commands to inspect the cluster members and the current leader.
|
||||||
|
|
|
@ -2,10 +2,6 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/canonical/go-dqlite/internal/protocol"
|
"github.com/canonical/go-dqlite/internal/protocol"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -57,22 +53,13 @@ func New(ctx context.Context, address string, options ...Option) (*Client, error
|
||||||
return nil, errors.Wrap(err, "failed to establish network connection")
|
return nil, errors.Wrap(err, "failed to establish network connection")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Latest protocol version.
|
protocol, err := protocol.Handshake(ctx, conn, protocol.VersionOne)
|
||||||
proto := make([]byte, 8)
|
|
||||||
binary.LittleEndian.PutUint64(proto, protocol.VersionOne)
|
|
||||||
|
|
||||||
// Perform the protocol handshake.
|
|
||||||
n, err := conn.Write(proto)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return nil, errors.Wrap(err, "failed to send handshake")
|
return nil, err
|
||||||
}
|
|
||||||
if n != 8 {
|
|
||||||
conn.Close()
|
|
||||||
return nil, errors.Wrap(io.ErrShortWrite, "failed to send handshake")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client := &Client{protocol: protocol.NewProtocol(protocol.VersionOne, conn)}
|
client := &Client{protocol: protocol}
|
||||||
|
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
@ -274,10 +261,3 @@ func defaultOptions() *options {
|
||||||
LogFunc: DefaultLogFunc,
|
LogFunc: DefaultLogFunc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultDialFunc(ctx context.Context, address string) (net.Conn, error) {
|
|
||||||
if strings.HasPrefix(address, "@") {
|
|
||||||
return protocol.UnixDial(ctx, address)
|
|
||||||
}
|
|
||||||
return protocol.TCPDial(ctx, address)
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/canonical/go-dqlite/internal/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultDialFunc is the default dial function, which can handle plain TCP and
|
||||||
|
// Unix socket endpoints. You can customize it with WithDialFunc()
|
||||||
|
func DefaultDialFunc(ctx context.Context, address string) (net.Conn, error) {
|
||||||
|
if strings.HasPrefix(address, "@") {
|
||||||
|
return protocol.UnixDial(ctx, address)
|
||||||
|
}
|
||||||
|
return protocol.TCPDial(ctx, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialFuncWithTLS returns a dial function that uses TLS encryption.
|
||||||
|
//
|
||||||
|
// The given dial function will be used to establish the network connection,
|
||||||
|
// and the given TLS config will be used for encryption.
|
||||||
|
func DialFuncWithTLS(dial DialFunc, config *tls.Config) DialFunc {
|
||||||
|
return func(ctx context.Context, addr string) (net.Conn, error) {
|
||||||
|
conn, err := dial(ctx, addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tls.Client(conn, config), nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,10 +2,7 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Rican7/retry/backoff"
|
|
||||||
"github.com/Rican7/retry/strategy"
|
|
||||||
"github.com/canonical/go-dqlite/internal/protocol"
|
"github.com/canonical/go-dqlite/internal/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,10 +15,7 @@ func FindLeader(ctx context.Context, store NodeStore, options ...Option) (*Clien
|
||||||
}
|
}
|
||||||
|
|
||||||
config := protocol.Config{
|
config := protocol.Config{
|
||||||
Dial: o.DialFunc,
|
Dial: o.DialFunc,
|
||||||
AttemptTimeout: time.Second,
|
|
||||||
RetryStrategies: []strategy.Strategy{
|
|
||||||
strategy.Backoff(backoff.BinaryExponential(time.Millisecond))},
|
|
||||||
}
|
}
|
||||||
connector := protocol.NewConnector(0, store, config, o.LogFunc)
|
connector := protocol.NewConnector(0, store, config, o.LogFunc)
|
||||||
protocol, err := connector.Connect(ctx)
|
protocol, err := connector.Connect(ctx)
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/canonical/go-dqlite/internal/logging"
|
"github.com/canonical/go-dqlite/internal/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,11 +18,5 @@ const (
|
||||||
LogError = logging.Error
|
LogError = logging.Error
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// DefaultLogFunc doesn't emit any message.
|
||||||
logger = log.New(os.Stdout, "", log.LstdFlags|log.Lmicroseconds)
|
func DefaultLogFunc(l LogLevel, format string, a ...interface{}) {}
|
||||||
)
|
|
||||||
|
|
||||||
// DefaultLogFunc emits messages using the stdlib's logger.
|
|
||||||
func DefaultLogFunc(l LogLevel, format string, a ...interface{}) {
|
|
||||||
logger.Output(2, fmt.Sprintf("[%s]: %s", l.String(), fmt.Sprintf(format, a...)))
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,7 +4,11 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ghodss/yaml"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/canonical/go-dqlite/internal/protocol"
|
"github.com/canonical/go-dqlite/internal/protocol"
|
||||||
|
@ -163,3 +167,65 @@ func (d *DatabaseNodeStore) Set(ctx context.Context, servers []NodeInfo) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Persists a list addresses of dqlite nodes in a YAML file.
|
||||||
|
type YamlNodeStore struct {
|
||||||
|
path string
|
||||||
|
servers []NodeInfo
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewYamlNodeStore creates a new YamlNodeStore backed by the given YAML file.
|
||||||
|
func NewYamlNodeStore(path string) (*YamlNodeStore, error) {
|
||||||
|
servers := []NodeInfo{}
|
||||||
|
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := yaml.Unmarshal(data, &servers); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
store := &YamlNodeStore{
|
||||||
|
path: path,
|
||||||
|
servers: servers,
|
||||||
|
}
|
||||||
|
|
||||||
|
return store, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current servers.
|
||||||
|
func (s *YamlNodeStore) Get(ctx context.Context) ([]NodeInfo, error) {
|
||||||
|
s.mu.RLock()
|
||||||
|
defer s.mu.RUnlock()
|
||||||
|
|
||||||
|
return s.servers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the servers addresses.
|
||||||
|
func (s *YamlNodeStore) Set(ctx context.Context, servers []NodeInfo) error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
data, err := yaml.Marshal(servers)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(s.path, data, 0600); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.servers = servers
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/canonical/go-dqlite/internal/bindings"
|
"github.com/canonical/go-dqlite/internal/bindings"
|
||||||
|
"github.com/canonical/go-dqlite/internal/protocol"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ import (
|
||||||
// from setting Single-thread mode at all.
|
// from setting Single-thread mode at all.
|
||||||
func ConfigMultiThread() error {
|
func ConfigMultiThread() error {
|
||||||
if err := bindings.ConfigMultiThread(); err != nil {
|
if err := bindings.ConfigMultiThread(); err != nil {
|
||||||
if err, ok := err.(bindings.Error); ok && err.Code == 21 /* SQLITE_MISUSE */ {
|
if err, ok := err.(protocol.Error); ok && err.Code == 21 /* SQLITE_MISUSE */ {
|
||||||
return fmt.Errorf("SQLite is already initialized")
|
return fmt.Errorf("SQLite is already initialized")
|
||||||
}
|
}
|
||||||
return errors.Wrap(err, "unknown error")
|
return errors.Wrap(err, "unknown error")
|
||||||
|
|
|
@ -24,12 +24,9 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Rican7/retry/backoff"
|
|
||||||
"github.com/Rican7/retry/strategy"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/canonical/go-dqlite/client"
|
"github.com/canonical/go-dqlite/client"
|
||||||
"github.com/canonical/go-dqlite/internal/bindings"
|
|
||||||
"github.com/canonical/go-dqlite/internal/protocol"
|
"github.com/canonical/go-dqlite/internal/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,7 +41,7 @@ type Driver struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error is returned in case of database errors.
|
// Error is returned in case of database errors.
|
||||||
type Error = bindings.Error
|
type Error = protocol.Error
|
||||||
|
|
||||||
// Error codes. Values here mostly overlap with native SQLite codes.
|
// Error codes. Values here mostly overlap with native SQLite codes.
|
||||||
const (
|
const (
|
||||||
|
@ -87,6 +84,9 @@ func WithDialFunc(dial DialFunc) Option {
|
||||||
// WithConnectionTimeout sets the connection timeout.
|
// WithConnectionTimeout sets the connection timeout.
|
||||||
//
|
//
|
||||||
// If not used, the default is 5 seconds.
|
// If not used, the default is 5 seconds.
|
||||||
|
//
|
||||||
|
// DEPRECATED: Connection cancellation is supported via the driver.Connector
|
||||||
|
// interface, which is used internally by the stdlib sql package.
|
||||||
func WithConnectionTimeout(timeout time.Duration) Option {
|
func WithConnectionTimeout(timeout time.Duration) Option {
|
||||||
return func(options *options) {
|
return func(options *options) {
|
||||||
options.ConnectionTimeout = timeout
|
options.ConnectionTimeout = timeout
|
||||||
|
@ -96,7 +96,7 @@ func WithConnectionTimeout(timeout time.Duration) Option {
|
||||||
// WithConnectionBackoffFactor sets the exponential backoff factor for retrying
|
// WithConnectionBackoffFactor sets the exponential backoff factor for retrying
|
||||||
// failed connection attempts.
|
// failed connection attempts.
|
||||||
//
|
//
|
||||||
// If not used, the default is 50 milliseconds.
|
// If not used, the default is 100 milliseconds.
|
||||||
func WithConnectionBackoffFactor(factor time.Duration) Option {
|
func WithConnectionBackoffFactor(factor time.Duration) Option {
|
||||||
return func(options *options) {
|
return func(options *options) {
|
||||||
options.ConnectionBackoffFactor = factor
|
options.ConnectionBackoffFactor = factor
|
||||||
|
@ -113,7 +113,28 @@ func WithConnectionBackoffCap(cap time.Duration) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithAttemptTimeout sets the timeout for each individual connection attempt .
|
||||||
|
//
|
||||||
|
// If not used, the default is 60 seconds.
|
||||||
|
func WithAttemptTimeout(timeout time.Duration) Option {
|
||||||
|
return func(options *options) {
|
||||||
|
options.AttemptTimeout = timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithRetryLimit sets the maximum number of connection retries.
|
||||||
|
//
|
||||||
|
// If not used, the default is 0 (unlimited retries)
|
||||||
|
func WithRetryLimit(limit uint) Option {
|
||||||
|
return func(options *options) {
|
||||||
|
options.RetryLimit = limit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithContext sets a global cancellation context.
|
// WithContext sets a global cancellation context.
|
||||||
|
//
|
||||||
|
// DEPRECATED: This API is no a no-op. Users should explicitly pass a context
|
||||||
|
// if they wish to cancel their requests.
|
||||||
func WithContext(context context.Context) Option {
|
func WithContext(context context.Context) Option {
|
||||||
return func(options *options) {
|
return func(options *options) {
|
||||||
options.Context = context
|
options.Context = context
|
||||||
|
@ -123,7 +144,8 @@ func WithContext(context context.Context) Option {
|
||||||
// WithContextTimeout sets the default client context timeout when no context
|
// WithContextTimeout sets the default client context timeout when no context
|
||||||
// deadline is provided.
|
// deadline is provided.
|
||||||
//
|
//
|
||||||
// If not used, the default is 5 seconds.
|
// DEPRECATED: This API is no a no-op. Users should explicitly pass a context
|
||||||
|
// if they wish to cancel their requests.
|
||||||
func WithContextTimeout(timeout time.Duration) Option {
|
func WithContextTimeout(timeout time.Duration) Option {
|
||||||
return func(options *options) {
|
return func(options *options) {
|
||||||
options.ContextTimeout = timeout
|
options.ContextTimeout = timeout
|
||||||
|
@ -148,13 +170,10 @@ func New(store client.NodeStore, options ...Option) (*Driver, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
driver.clientConfig.Dial = o.Dial
|
driver.clientConfig.Dial = o.Dial
|
||||||
driver.clientConfig.AttemptTimeout = 5 * time.Second
|
driver.clientConfig.AttemptTimeout = o.AttemptTimeout
|
||||||
driver.clientConfig.RetryStrategies = []strategy.Strategy{
|
driver.clientConfig.BackoffFactor = o.ConnectionBackoffFactor
|
||||||
driverConnectionRetryStrategy(
|
driver.clientConfig.BackoffCap = o.ConnectionBackoffCap
|
||||||
o.ConnectionBackoffFactor,
|
driver.clientConfig.RetryLimit = o.RetryLimit
|
||||||
o.ConnectionBackoffCap,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
return driver, nil
|
return driver, nil
|
||||||
}
|
}
|
||||||
|
@ -163,63 +182,48 @@ func New(store client.NodeStore, options ...Option) (*Driver, error) {
|
||||||
type options struct {
|
type options struct {
|
||||||
Log client.LogFunc
|
Log client.LogFunc
|
||||||
Dial protocol.DialFunc
|
Dial protocol.DialFunc
|
||||||
|
AttemptTimeout time.Duration
|
||||||
ConnectionTimeout time.Duration
|
ConnectionTimeout time.Duration
|
||||||
ContextTimeout time.Duration
|
ContextTimeout time.Duration
|
||||||
ConnectionBackoffFactor time.Duration
|
ConnectionBackoffFactor time.Duration
|
||||||
ConnectionBackoffCap time.Duration
|
ConnectionBackoffCap time.Duration
|
||||||
|
RetryLimit uint
|
||||||
Context context.Context
|
Context context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a options object with sane defaults.
|
// Create a options object with sane defaults.
|
||||||
func defaultOptions() *options {
|
func defaultOptions() *options {
|
||||||
return &options{
|
return &options{
|
||||||
Log: client.DefaultLogFunc,
|
Log: client.DefaultLogFunc,
|
||||||
Dial: client.DefaultDialFunc,
|
Dial: client.DefaultDialFunc,
|
||||||
ConnectionTimeout: 15 * time.Second,
|
|
||||||
ContextTimeout: 2 * time.Second,
|
|
||||||
ConnectionBackoffFactor: 50 * time.Millisecond,
|
|
||||||
ConnectionBackoffCap: time.Second,
|
|
||||||
Context: context.Background(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a retry strategy with jittered exponential backoff, capped at the
|
// A Connector represents a driver in a fixed configuration and can create any
|
||||||
// given amount of time.
|
// number of equivalent Conns for use by multiple goroutines.
|
||||||
func driverConnectionRetryStrategy(factor, cap time.Duration) strategy.Strategy {
|
type Connector struct {
|
||||||
backoff := backoff.BinaryExponential(factor)
|
uri string
|
||||||
|
driver *Driver
|
||||||
return func(attempt uint) bool {
|
|
||||||
if attempt > 0 {
|
|
||||||
duration := backoff(attempt)
|
|
||||||
if duration > cap {
|
|
||||||
duration = cap
|
|
||||||
}
|
|
||||||
time.Sleep(duration)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open establishes a new connection to a SQLite database on the dqlite server.
|
// Connect returns a connection to the database.
|
||||||
//
|
func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) {
|
||||||
// The given name must be a pure file name without any directory segment,
|
if c.driver.context != nil {
|
||||||
// dqlite will connect to a database with that name in its data directory.
|
ctx = c.driver.context
|
||||||
//
|
}
|
||||||
// Query parameters are always valid except for "mode=memory".
|
|
||||||
//
|
if c.driver.connectionTimeout != 0 {
|
||||||
// If this node is not the leader, or the leader is unknown an ErrNotLeader
|
var cancel func()
|
||||||
// error is returned.
|
ctx, cancel = context.WithTimeout(ctx, c.driver.connectionTimeout)
|
||||||
func (d *Driver) Open(uri string) (driver.Conn, error) {
|
defer cancel()
|
||||||
ctx, cancel := context.WithTimeout(d.context, d.connectionTimeout)
|
}
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// TODO: generate a client ID.
|
// TODO: generate a client ID.
|
||||||
connector := protocol.NewConnector(0, d.store, d.clientConfig, d.log)
|
connector := protocol.NewConnector(0, c.driver.store, c.driver.clientConfig, c.driver.log)
|
||||||
|
|
||||||
conn := &Conn{
|
conn := &Conn{
|
||||||
log: d.log,
|
log: c.driver.log,
|
||||||
contextTimeout: d.contextTimeout,
|
contextTimeout: c.driver.contextTimeout,
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
@ -227,15 +231,11 @@ func (d *Driver) Open(uri string) (driver.Conn, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to create dqlite connection")
|
return nil, errors.Wrap(err, "failed to create dqlite connection")
|
||||||
}
|
}
|
||||||
conn.protocol.SetContextTimeout(d.contextTimeout)
|
|
||||||
|
|
||||||
conn.request.Init(4096)
|
conn.request.Init(4096)
|
||||||
conn.response.Init(4096)
|
conn.response.Init(4096)
|
||||||
|
|
||||||
defer conn.request.Reset()
|
protocol.EncodeOpen(&conn.request, c.uri, 0, "volatile")
|
||||||
defer conn.response.Reset()
|
|
||||||
|
|
||||||
protocol.EncodeOpen(&conn.request, uri, 0, "volatile")
|
|
||||||
|
|
||||||
if err := conn.protocol.Call(ctx, &conn.request, &conn.response); err != nil {
|
if err := conn.protocol.Call(ctx, &conn.request, &conn.response); err != nil {
|
||||||
conn.protocol.Close()
|
conn.protocol.Close()
|
||||||
|
@ -251,11 +251,45 @@ func (d *Driver) Open(uri string) (driver.Conn, error) {
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Driver returns the underlying Driver of the Connector,
|
||||||
|
func (c *Connector) Driver() driver.Driver {
|
||||||
|
return c.driver
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenConnector must parse the name in the same format that Driver.Open
|
||||||
|
// parses the name parameter.
|
||||||
|
func (d *Driver) OpenConnector(name string) (driver.Connector, error) {
|
||||||
|
connector := &Connector{
|
||||||
|
uri: name,
|
||||||
|
driver: d,
|
||||||
|
}
|
||||||
|
return connector, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open establishes a new connection to a SQLite database on the dqlite server.
|
||||||
|
//
|
||||||
|
// The given name must be a pure file name without any directory segment,
|
||||||
|
// dqlite will connect to a database with that name in its data directory.
|
||||||
|
//
|
||||||
|
// Query parameters are always valid except for "mode=memory".
|
||||||
|
//
|
||||||
|
// If this node is not the leader, or the leader is unknown an ErrNotLeader
|
||||||
|
// error is returned.
|
||||||
|
func (d *Driver) Open(uri string) (driver.Conn, error) {
|
||||||
|
connector, err := d.OpenConnector(uri)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return connector.Connect(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
// SetContextTimeout sets the default client timeout when no context deadline
|
// SetContextTimeout sets the default client timeout when no context deadline
|
||||||
// is provided.
|
// is provided.
|
||||||
func (d *Driver) SetContextTimeout(timeout time.Duration) {
|
//
|
||||||
d.contextTimeout = timeout
|
// DEPRECATED: This API is no a no-op. Users should explicitly pass a context
|
||||||
}
|
// if they wish to cancel their requests.
|
||||||
|
func (d *Driver) SetContextTimeout(timeout time.Duration) {}
|
||||||
|
|
||||||
// ErrNoAvailableLeader is returned as root cause of Open() if there's no
|
// ErrNoAvailableLeader is returned as root cause of Open() if there's no
|
||||||
// leader available in the cluster.
|
// leader available in the cluster.
|
||||||
|
@ -275,9 +309,6 @@ type Conn struct {
|
||||||
// context is for the preparation of the statement, it must not store the
|
// context is for the preparation of the statement, it must not store the
|
||||||
// context within the statement itself.
|
// context within the statement itself.
|
||||||
func (c *Conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
|
func (c *Conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
|
||||||
defer c.request.Reset()
|
|
||||||
defer c.response.Reset()
|
|
||||||
|
|
||||||
stmt := &Stmt{
|
stmt := &Stmt{
|
||||||
protocol: c.protocol,
|
protocol: c.protocol,
|
||||||
request: &c.request,
|
request: &c.request,
|
||||||
|
@ -306,9 +337,6 @@ func (c *Conn) Prepare(query string) (driver.Stmt, error) {
|
||||||
|
|
||||||
// ExecContext is an optional interface that may be implemented by a Conn.
|
// ExecContext is an optional interface that may be implemented by a Conn.
|
||||||
func (c *Conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
|
func (c *Conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
|
||||||
defer c.request.Reset()
|
|
||||||
defer c.response.Reset()
|
|
||||||
|
|
||||||
protocol.EncodeExecSQL(&c.request, uint64(c.id), query, args)
|
protocol.EncodeExecSQL(&c.request, uint64(c.id), query, args)
|
||||||
|
|
||||||
if err := c.protocol.Call(ctx, &c.request, &c.response); err != nil {
|
if err := c.protocol.Call(ctx, &c.request, &c.response); err != nil {
|
||||||
|
@ -330,18 +358,14 @@ func (c *Conn) Query(query string, args []driver.Value) (driver.Rows, error) {
|
||||||
|
|
||||||
// QueryContext is an optional interface that may be implemented by a Conn.
|
// QueryContext is an optional interface that may be implemented by a Conn.
|
||||||
func (c *Conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
|
func (c *Conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
|
||||||
defer c.request.Reset()
|
|
||||||
|
|
||||||
protocol.EncodeQuerySQL(&c.request, uint64(c.id), query, args)
|
protocol.EncodeQuerySQL(&c.request, uint64(c.id), query, args)
|
||||||
|
|
||||||
if err := c.protocol.Call(ctx, &c.request, &c.response); err != nil {
|
if err := c.protocol.Call(ctx, &c.request, &c.response); err != nil {
|
||||||
c.response.Reset()
|
|
||||||
return nil, driverError(err)
|
return nil, driverError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err := protocol.DecodeRows(&c.response)
|
rows, err := protocol.DecodeRows(&c.response)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.response.Reset()
|
|
||||||
return nil, driverError(err)
|
return nil, driverError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,7 +415,15 @@ func (c *Conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, e
|
||||||
//
|
//
|
||||||
// Deprecated: Drivers should implement ConnBeginTx instead (or additionally).
|
// Deprecated: Drivers should implement ConnBeginTx instead (or additionally).
|
||||||
func (c *Conn) Begin() (driver.Tx, error) {
|
func (c *Conn) Begin() (driver.Tx, error) {
|
||||||
return c.BeginTx(context.Background(), driver.TxOptions{})
|
ctx := context.Background()
|
||||||
|
|
||||||
|
if c.contextTimeout > 0 {
|
||||||
|
var cancel func()
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), c.contextTimeout)
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.BeginTx(ctx, driver.TxOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tx is a transaction.
|
// Tx is a transaction.
|
||||||
|
@ -401,8 +433,7 @@ type Tx struct {
|
||||||
|
|
||||||
// Commit the transaction.
|
// Commit the transaction.
|
||||||
func (tx *Tx) Commit() error {
|
func (tx *Tx) Commit() error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), tx.conn.contextTimeout)
|
ctx := context.Background()
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if _, err := tx.conn.ExecContext(ctx, "COMMIT", nil); err != nil {
|
if _, err := tx.conn.ExecContext(ctx, "COMMIT", nil); err != nil {
|
||||||
return driverError(err)
|
return driverError(err)
|
||||||
|
@ -413,8 +444,7 @@ func (tx *Tx) Commit() error {
|
||||||
|
|
||||||
// Rollback the transaction.
|
// Rollback the transaction.
|
||||||
func (tx *Tx) Rollback() error {
|
func (tx *Tx) Rollback() error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), tx.conn.contextTimeout)
|
ctx := context.Background()
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if _, err := tx.conn.ExecContext(ctx, "ROLLBACK", nil); err != nil {
|
if _, err := tx.conn.ExecContext(ctx, "ROLLBACK", nil); err != nil {
|
||||||
return driverError(err)
|
return driverError(err)
|
||||||
|
@ -436,9 +466,6 @@ type Stmt struct {
|
||||||
|
|
||||||
// Close closes the statement.
|
// Close closes the statement.
|
||||||
func (s *Stmt) Close() error {
|
func (s *Stmt) Close() error {
|
||||||
defer s.request.Reset()
|
|
||||||
defer s.response.Reset()
|
|
||||||
|
|
||||||
protocol.EncodeFinalize(s.request, s.db, s.id)
|
protocol.EncodeFinalize(s.request, s.db, s.id)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
@ -464,9 +491,6 @@ func (s *Stmt) NumInput() int {
|
||||||
//
|
//
|
||||||
// ExecContext must honor the context timeout and return when it is canceled.
|
// ExecContext must honor the context timeout and return when it is canceled.
|
||||||
func (s *Stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
|
func (s *Stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
|
||||||
defer s.request.Reset()
|
|
||||||
defer s.response.Reset()
|
|
||||||
|
|
||||||
protocol.EncodeExec(s.request, s.db, s.id, args)
|
protocol.EncodeExec(s.request, s.db, s.id, args)
|
||||||
|
|
||||||
if err := s.protocol.Call(ctx, s.request, s.response); err != nil {
|
if err := s.protocol.Call(ctx, s.request, s.response); err != nil {
|
||||||
|
@ -491,22 +515,14 @@ func (s *Stmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||||
//
|
//
|
||||||
// QueryContext must honor the context timeout and return when it is canceled.
|
// QueryContext must honor the context timeout and return when it is canceled.
|
||||||
func (s *Stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
|
func (s *Stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
|
||||||
defer s.request.Reset()
|
|
||||||
|
|
||||||
// FIXME: this shouldn't be needed but we have hit a few panics
|
|
||||||
// probably due to the response object not being fully reset.
|
|
||||||
s.response.Reset()
|
|
||||||
|
|
||||||
protocol.EncodeQuery(s.request, s.db, s.id, args)
|
protocol.EncodeQuery(s.request, s.db, s.id, args)
|
||||||
|
|
||||||
if err := s.protocol.Call(ctx, s.request, s.response); err != nil {
|
if err := s.protocol.Call(ctx, s.request, s.response); err != nil {
|
||||||
s.response.Reset()
|
|
||||||
return nil, driverError(err)
|
return nil, driverError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err := protocol.DecodeRows(s.response)
|
rows, err := protocol.DecodeRows(s.response)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.response.Reset()
|
|
||||||
return nil, driverError(err)
|
return nil, driverError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
package bindings
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <sqlite3.h>
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
// Error holds information about a SQLite error.
|
|
||||||
type Error struct {
|
|
||||||
Code int
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e Error) Error() string {
|
|
||||||
if e.Message != "" {
|
|
||||||
return e.Message
|
|
||||||
}
|
|
||||||
return C.GoString(C.sqlite3_errstr(C.int(e.Code)))
|
|
||||||
}
|
|
|
@ -90,14 +90,14 @@ func init() {
|
||||||
|
|
||||||
func ConfigSingleThread() error {
|
func ConfigSingleThread() error {
|
||||||
if rc := C.sqlite3ConfigSingleThread(); rc != 0 {
|
if rc := C.sqlite3ConfigSingleThread(); rc != 0 {
|
||||||
return Error{Code: int(rc)}
|
return protocol.Error{Message: C.GoString(C.sqlite3_errstr(rc)), Code: int(rc)}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConfigMultiThread() error {
|
func ConfigMultiThread() error {
|
||||||
if rc := C.sqlite3ConfigMultiThread(); rc != 0 {
|
if rc := C.sqlite3ConfigMultiThread(); rc != 0 {
|
||||||
return Error{Code: int(rc)}
|
return protocol.Error{Message: C.GoString(C.sqlite3_errstr(rc)), Code: int(rc)}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -197,6 +197,14 @@ func (s *Node) Recover(cluster []protocol.NodeInfo) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateID generates a unique ID for a server.
|
||||||
|
func GenerateID(address string) uint64 {
|
||||||
|
caddress := C.CString(address)
|
||||||
|
defer C.free(unsafe.Pointer(caddress))
|
||||||
|
id := C.dqlite_generate_node_id(caddress)
|
||||||
|
return uint64(id)
|
||||||
|
}
|
||||||
|
|
||||||
// Extract the underlying socket from a connection.
|
// Extract the underlying socket from a connection.
|
||||||
func connToSocket(conn net.Conn) (C.int, error) {
|
func connToSocket(conn net.Conn) (C.int, error) {
|
||||||
file, err := conn.(fileConn).File()
|
file, err := conn.(fileConn).File()
|
||||||
|
|
|
@ -2,13 +2,14 @@ package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Rican7/retry/strategy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config holds various configuration parameters for a dqlite client.
|
// Config holds various configuration parameters for a dqlite client.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Dial DialFunc // Network dialer.
|
Dial DialFunc // Network dialer.
|
||||||
AttemptTimeout time.Duration // Timeout for each individual Dial attempt.
|
DialTimeout time.Duration // Timeout for establishing a network connection .
|
||||||
RetryStrategies []strategy.Strategy // Strategies used for retrying to connect to a leader.
|
AttemptTimeout time.Duration // Timeout for each individual attempt to probe a server's leadership.
|
||||||
|
BackoffFactor time.Duration // Exponential backoff factor for retries.
|
||||||
|
BackoffCap time.Duration // Maximum connection retry backoff value,
|
||||||
|
RetryLimit uint // Maximum number of retries, or 0 for unlimited.
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/Rican7/retry"
|
"github.com/Rican7/retry"
|
||||||
|
"github.com/Rican7/retry/backoff"
|
||||||
|
"github.com/Rican7/retry/strategy"
|
||||||
"github.com/canonical/go-dqlite/internal/logging"
|
"github.com/canonical/go-dqlite/internal/logging"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
@ -27,6 +30,22 @@ type Connector struct {
|
||||||
// NewConnector returns a new connector that can be used by a dqlite driver to
|
// NewConnector returns a new connector that can be used by a dqlite driver to
|
||||||
// create new clients connected to a leader dqlite server.
|
// create new clients connected to a leader dqlite server.
|
||||||
func NewConnector(id uint64, store NodeStore, config Config, log logging.Func) *Connector {
|
func NewConnector(id uint64, store NodeStore, config Config, log logging.Func) *Connector {
|
||||||
|
if config.DialTimeout == 0 {
|
||||||
|
config.DialTimeout = 10 * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.AttemptTimeout == 0 {
|
||||||
|
config.AttemptTimeout = 60 * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.BackoffFactor == 0 {
|
||||||
|
config.BackoffFactor = 100 * time.Millisecond
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.BackoffCap == 0 {
|
||||||
|
config.BackoffCap = time.Second
|
||||||
|
}
|
||||||
|
|
||||||
connector := &Connector{
|
connector := &Connector{
|
||||||
id: id,
|
id: id,
|
||||||
store: store,
|
store: store,
|
||||||
|
@ -43,6 +62,8 @@ func NewConnector(id uint64, store NodeStore, config Config, log logging.Func) *
|
||||||
func (c *Connector) Connect(ctx context.Context) (*Protocol, error) {
|
func (c *Connector) Connect(ctx context.Context) (*Protocol, error) {
|
||||||
var protocol *Protocol
|
var protocol *Protocol
|
||||||
|
|
||||||
|
strategies := makeRetryStrategies(c.config.BackoffFactor, c.config.BackoffCap, c.config.RetryLimit)
|
||||||
|
|
||||||
// The retry strategy should be configured to retry indefinitely, until
|
// The retry strategy should be configured to retry indefinitely, until
|
||||||
// the given context is done.
|
// the given context is done.
|
||||||
err := retry.Retry(func(attempt uint) error {
|
err := retry.Retry(func(attempt uint) error {
|
||||||
|
@ -61,17 +82,16 @@ func (c *Connector) Connect(ctx context.Context) (*Protocol, error) {
|
||||||
var err error
|
var err error
|
||||||
protocol, err = c.connectAttemptAll(ctx, log)
|
protocol, err = c.connectAttemptAll(ctx, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log(logging.Debug, "connection failed err=%v", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}, c.config.RetryStrategies...)
|
}, strategies...)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// The retry strategy should never give up until success or
|
// We exhausted the number of retries allowed by the configured
|
||||||
// context expiration.
|
// strategy.
|
||||||
panic("connect retry aborted unexpectedly")
|
return nil, ErrNoAvailableLeader
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
|
@ -92,7 +112,7 @@ func (c *Connector) connectAttemptAll(ctx context.Context, log logging.Func) (*P
|
||||||
// Make an attempt for each address until we find the leader.
|
// Make an attempt for each address until we find the leader.
|
||||||
for _, server := range servers {
|
for _, server := range servers {
|
||||||
log := func(l logging.Level, format string, a ...interface{}) {
|
log := func(l logging.Level, format string, a ...interface{}) {
|
||||||
format += fmt.Sprintf(" address=%s id=%d", server.Address, server.ID)
|
format += fmt.Sprintf(" address=%s", server.Address)
|
||||||
log(l, format, a...)
|
log(l, format, a...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,68 +127,71 @@ func (c *Connector) connectAttemptAll(ctx context.Context, log logging.Func) (*P
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This server is unavailable, try with the next target.
|
// This server is unavailable, try with the next target.
|
||||||
log(logging.Debug, "server connection failed err=%v", err)
|
log(logging.Warn, "server unavailable err=%v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if protocol != nil {
|
if protocol != nil {
|
||||||
// We found the leader
|
// We found the leader
|
||||||
log(logging.Info, "connected")
|
log(logging.Debug, "connected")
|
||||||
return protocol, nil
|
return protocol, nil
|
||||||
}
|
}
|
||||||
if leader == "" {
|
if leader == "" {
|
||||||
// This server does not know who the current leader is,
|
// This server does not know who the current leader is,
|
||||||
// try with the next target.
|
// try with the next target.
|
||||||
|
log(logging.Warn, "no known leader")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, it means this server reported that another
|
// If we get here, it means this server reported that another
|
||||||
// server is the leader, let's close the connection to this
|
// server is the leader, let's close the connection to this
|
||||||
// server and try with the suggested one.
|
// server and try with the suggested one.
|
||||||
//logger = logger.With(zap.String("leader", leader))
|
log(logging.Debug, "connect to reported leader %s", leader)
|
||||||
|
|
||||||
|
ctx, cancel = context.WithTimeout(ctx, c.config.AttemptTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
protocol, leader, err = c.connectAttemptOne(ctx, leader, version)
|
protocol, leader, err = c.connectAttemptOne(ctx, leader, version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// The leader reported by the previous server is
|
// The leader reported by the previous server is
|
||||||
// unavailable, try with the next target.
|
// unavailable, try with the next target.
|
||||||
//logger.Info("leader server connection failed", zap.String("err", err.Error()))
|
log(logging.Warn, "reported leader unavailable err=%v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if protocol == nil {
|
if protocol == nil {
|
||||||
// The leader reported by the target server does not consider itself
|
// The leader reported by the target server does not consider itself
|
||||||
// the leader, try with the next target.
|
// the leader, try with the next target.
|
||||||
//logger.Info("reported leader server is not the leader")
|
log(logging.Warn, "reported leader server is not the leader")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log(logging.Info, "connected")
|
log(logging.Debug, "connected")
|
||||||
return protocol, nil
|
return protocol, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, ErrNoAvailableLeader
|
return nil, ErrNoAvailableLeader
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect establishes a connection with a dqlite node.
|
// Perform the initial handshake using the given protocol version.
|
||||||
func Connect(ctx context.Context, dial DialFunc, address string, version uint64) (*Protocol, error) {
|
func Handshake(ctx context.Context, conn net.Conn, version uint64) (*Protocol, error) {
|
||||||
// Establish the connection.
|
|
||||||
conn, err := dial(ctx, address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to establish network connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Latest protocol version.
|
// Latest protocol version.
|
||||||
protocol := make([]byte, 8)
|
protocol := make([]byte, 8)
|
||||||
binary.LittleEndian.PutUint64(protocol, version)
|
binary.LittleEndian.PutUint64(protocol, version)
|
||||||
|
|
||||||
|
// Honor the ctx deadline, if present.
|
||||||
|
if deadline, ok := ctx.Deadline(); ok {
|
||||||
|
conn.SetDeadline(deadline)
|
||||||
|
defer conn.SetDeadline(time.Time{})
|
||||||
|
}
|
||||||
|
|
||||||
// Perform the protocol handshake.
|
// Perform the protocol handshake.
|
||||||
n, err := conn.Write(protocol)
|
n, err := conn.Write(protocol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.Close()
|
|
||||||
return nil, errors.Wrap(err, "failed to send handshake")
|
return nil, errors.Wrap(err, "failed to send handshake")
|
||||||
}
|
}
|
||||||
if n != 8 {
|
if n != 8 {
|
||||||
conn.Close()
|
|
||||||
return nil, errors.Wrap(io.ErrShortWrite, "failed to send handshake")
|
return nil, errors.Wrap(io.ErrShortWrite, "failed to send handshake")
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewProtocol(version, conn), nil
|
return newProtocol(version, conn), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to the given dqlite server and check if it's the leader.
|
// Connect to the given dqlite server and check if it's the leader.
|
||||||
|
@ -181,8 +204,18 @@ func Connect(ctx context.Context, dial DialFunc, address string, version uint64)
|
||||||
// - Target is the leader: -> server, "", nil
|
// - Target is the leader: -> server, "", nil
|
||||||
//
|
//
|
||||||
func (c *Connector) connectAttemptOne(ctx context.Context, address string, version uint64) (*Protocol, string, error) {
|
func (c *Connector) connectAttemptOne(ctx context.Context, address string, version uint64) (*Protocol, string, error) {
|
||||||
protocol, err := Connect(ctx, c.config.Dial, address, version)
|
dialCtx, cancel := context.WithTimeout(ctx, c.config.DialTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Establish the connection.
|
||||||
|
conn, err := c.config.Dial(dialCtx, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return nil, "", errors.Wrap(err, "failed to establish network connection")
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol, err := Handshake(ctx, conn, version)
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,8 +252,8 @@ func (c *Connector) connectAttemptOne(ctx context.Context, address string, versi
|
||||||
return nil, "", nil
|
return nil, "", nil
|
||||||
case address:
|
case address:
|
||||||
// This server is the leader, register ourselves and return.
|
// This server is the leader, register ourselves and return.
|
||||||
request.Reset()
|
request.reset()
|
||||||
response.Reset()
|
response.reset()
|
||||||
|
|
||||||
EncodeClient(&request, c.id)
|
EncodeClient(&request, c.id)
|
||||||
|
|
||||||
|
@ -247,4 +280,33 @@ func (c *Connector) connectAttemptOne(ctx context.Context, address string, versi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return a retry strategy with exponential backoff, capped at the given amount
|
||||||
|
// of time and possibly with a maximum number of retries.
|
||||||
|
func makeRetryStrategies(factor, cap time.Duration, limit uint) []strategy.Strategy {
|
||||||
|
backoff := backoff.BinaryExponential(factor)
|
||||||
|
|
||||||
|
strategies := []strategy.Strategy{}
|
||||||
|
|
||||||
|
if limit > 0 {
|
||||||
|
strategies = append(strategies, strategy.Limit(limit))
|
||||||
|
}
|
||||||
|
|
||||||
|
strategies = append(strategies,
|
||||||
|
func(attempt uint) bool {
|
||||||
|
if attempt > 0 {
|
||||||
|
duration := backoff(attempt)
|
||||||
|
// Duration might be negative in case of integer overflow.
|
||||||
|
if duration > cap || duration <= 0 {
|
||||||
|
duration = cap
|
||||||
|
}
|
||||||
|
time.Sleep(duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return strategies
|
||||||
|
}
|
||||||
|
|
||||||
var errBadProtocol = fmt.Errorf("bad protocol")
|
var errBadProtocol = fmt.Errorf("bad protocol")
|
||||||
|
|
|
@ -2,6 +2,7 @@ package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,3 +19,15 @@ func UnixDial(ctx context.Context, address string) (net.Conn, error) {
|
||||||
dialer := net.Dialer{}
|
dialer := net.Dialer{}
|
||||||
return dialer.DialContext(ctx, "unix", address)
|
return dialer.DialContext(ctx, "unix", address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TLSCipherSuites are the cipher suites by the go-dqlite TLS helpers.
|
||||||
|
var TLSCipherSuites = []uint16{
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
}
|
||||||
|
|
|
@ -27,3 +27,13 @@ func (e ErrRequest) Error() string {
|
||||||
// ErrRowsPart is returned when the first batch of a multi-response result
|
// ErrRowsPart is returned when the first batch of a multi-response result
|
||||||
// batch is done.
|
// batch is done.
|
||||||
var ErrRowsPart = fmt.Errorf("not all rows were returned in this response")
|
var ErrRowsPart = fmt.Errorf("not all rows were returned in this response")
|
||||||
|
|
||||||
|
// Error holds information about a SQLite error.
|
||||||
|
type Error struct {
|
||||||
|
Code int
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Error) Error() string {
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
|
|
@ -26,24 +26,23 @@ type Message struct {
|
||||||
flags uint8
|
flags uint8
|
||||||
extra uint16
|
extra uint16
|
||||||
header []byte // Statically allocated header buffer
|
header []byte // Statically allocated header buffer
|
||||||
body1 buffer // Statically allocated body data, using bytes
|
body buffer // Message body data.
|
||||||
body2 buffer // Dynamically allocated body data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the message using the given size of the statically
|
// Init initializes the message using the given initial size for the data
|
||||||
// allocated buffer (i.e. a buffer which is re-used across requests or
|
// buffer, which is re-used across requests or responses encoded or decoded
|
||||||
// responses encoded or decoded using this message object).
|
// using this message object.
|
||||||
func (m *Message) Init(staticSize int) {
|
func (m *Message) Init(initialBufferSize int) {
|
||||||
if (staticSize % messageWordSize) != 0 {
|
if (initialBufferSize % messageWordSize) != 0 {
|
||||||
panic("static size is not aligned to word boundary")
|
panic("initial buffer size is not aligned to word boundary")
|
||||||
}
|
}
|
||||||
m.header = make([]byte, messageHeaderSize)
|
m.header = make([]byte, messageHeaderSize)
|
||||||
m.body1.Bytes = make([]byte, staticSize)
|
m.body.Bytes = make([]byte, initialBufferSize)
|
||||||
m.Reset()
|
m.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the state of the message so it can be used to encode or decode again.
|
// Reset the state of the message so it can be used to encode or decode again.
|
||||||
func (m *Message) Reset() {
|
func (m *Message) reset() {
|
||||||
m.words = 0
|
m.words = 0
|
||||||
m.mtype = 0
|
m.mtype = 0
|
||||||
m.flags = 0
|
m.flags = 0
|
||||||
|
@ -51,9 +50,7 @@ func (m *Message) Reset() {
|
||||||
for i := 0; i < messageHeaderSize; i++ {
|
for i := 0; i < messageHeaderSize; i++ {
|
||||||
m.header[i] = 0
|
m.header[i] = 0
|
||||||
}
|
}
|
||||||
m.body1.Offset = 0
|
m.body.Offset = 0
|
||||||
m.body2.Bytes = nil
|
|
||||||
m.body2.Offset = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append a byte slice to the message.
|
// Append a byte slice to the message.
|
||||||
|
@ -232,11 +229,11 @@ func (m *Message) putNamedValues(values NamedValues) {
|
||||||
// Finalize the message by setting the message type and the number
|
// Finalize the message by setting the message type and the number
|
||||||
// of words in the body (calculated from the body size).
|
// of words in the body (calculated from the body size).
|
||||||
func (m *Message) putHeader(mtype uint8) {
|
func (m *Message) putHeader(mtype uint8) {
|
||||||
if m.body1.Offset <= 0 {
|
if m.body.Offset <= 0 {
|
||||||
panic("static offset is not positive")
|
panic("static offset is not positive")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m.body1.Offset % messageWordSize) != 0 {
|
if (m.body.Offset % messageWordSize) != 0 {
|
||||||
panic("static body is not aligned")
|
panic("static body is not aligned")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,22 +241,7 @@ func (m *Message) putHeader(mtype uint8) {
|
||||||
m.flags = 0
|
m.flags = 0
|
||||||
m.extra = 0
|
m.extra = 0
|
||||||
|
|
||||||
m.words = uint32(m.body1.Offset) / messageWordSize
|
m.words = uint32(m.body.Offset) / messageWordSize
|
||||||
|
|
||||||
if m.body2.Bytes == nil {
|
|
||||||
m.finalize()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.body2.Offset <= 0 {
|
|
||||||
panic("dynamic offset is not positive")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m.body2.Offset % messageWordSize) != 0 {
|
|
||||||
panic("dynamic body is not aligned")
|
|
||||||
}
|
|
||||||
|
|
||||||
m.words += uint32(m.body2.Offset) / messageWordSize
|
|
||||||
|
|
||||||
m.finalize()
|
m.finalize()
|
||||||
}
|
}
|
||||||
|
@ -276,27 +258,14 @@ func (m *Message) finalize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) bufferForPut(size int) *buffer {
|
func (m *Message) bufferForPut(size int) *buffer {
|
||||||
if m.body2.Bytes != nil {
|
for (m.body.Offset + size) > len(m.body.Bytes) {
|
||||||
if (m.body2.Offset + size) > len(m.body2.Bytes) {
|
// Grow message buffer.
|
||||||
// Grow body2.
|
bytes := make([]byte, len(m.body.Bytes)*2)
|
||||||
//
|
copy(bytes, m.body.Bytes)
|
||||||
// TODO: find a good grow strategy.
|
m.body.Bytes = bytes
|
||||||
bytes := make([]byte, m.body2.Offset+size)
|
|
||||||
copy(bytes, m.body2.Bytes)
|
|
||||||
m.body2.Bytes = bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
return &m.body2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m.body1.Offset + size) > len(m.body1.Bytes) {
|
return &m.body
|
||||||
m.body2.Bytes = make([]byte, size)
|
|
||||||
m.body2.Offset = 0
|
|
||||||
|
|
||||||
return &m.body2
|
|
||||||
}
|
|
||||||
|
|
||||||
return &m.body1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the message type and its flags.
|
// Return the message type and its flags.
|
||||||
|
@ -310,31 +279,6 @@ func (m *Message) getString() string {
|
||||||
|
|
||||||
index := bytes.IndexByte(b.Bytes[b.Offset:], 0)
|
index := bytes.IndexByte(b.Bytes[b.Offset:], 0)
|
||||||
if index == -1 {
|
if index == -1 {
|
||||||
// Check if the string overflows in the dynamic buffer.
|
|
||||||
if b == &m.body1 && m.body2.Bytes != nil {
|
|
||||||
// Assert that this is the first read of the dynamic buffer.
|
|
||||||
if m.body2.Offset != 0 {
|
|
||||||
panic("static buffer read after dynamic buffer one")
|
|
||||||
}
|
|
||||||
index = bytes.IndexByte(m.body2.Bytes[0:], 0)
|
|
||||||
if index != -1 {
|
|
||||||
// We found the trailing part of the string.
|
|
||||||
data := b.Bytes[b.Offset:]
|
|
||||||
data = append(data, m.body2.Bytes[0:index]...)
|
|
||||||
|
|
||||||
index++
|
|
||||||
|
|
||||||
if trailing := index % messageWordSize; trailing != 0 {
|
|
||||||
// Account for padding, moving index to the next word boundary.
|
|
||||||
index += messageWordSize - trailing
|
|
||||||
}
|
|
||||||
|
|
||||||
m.body1.Offset = len(m.body1.Bytes)
|
|
||||||
m.body2.Advance(index)
|
|
||||||
|
|
||||||
return string(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic("no string found")
|
panic("no string found")
|
||||||
}
|
}
|
||||||
s := string(b.Bytes[b.Offset : b.Offset+index])
|
s := string(b.Bytes[b.Offset : b.Offset+index])
|
||||||
|
@ -465,31 +409,23 @@ func (m *Message) getFiles() Files {
|
||||||
|
|
||||||
func (m *Message) hasBeenConsumed() bool {
|
func (m *Message) hasBeenConsumed() bool {
|
||||||
size := int(m.words * messageWordSize)
|
size := int(m.words * messageWordSize)
|
||||||
return (m.body1.Offset == size || m.body1.Offset == len(m.body1.Bytes)) &&
|
return m.body.Offset == size
|
||||||
m.body1.Offset+m.body2.Offset == size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) lastByte() byte {
|
func (m *Message) lastByte() byte {
|
||||||
size := int(m.words * messageWordSize)
|
size := int(m.words * messageWordSize)
|
||||||
if size > len(m.body1.Bytes) {
|
return m.body.Bytes[size-1]
|
||||||
size = size - m.body1.Offset
|
|
||||||
return m.body2.Bytes[size-1]
|
|
||||||
}
|
|
||||||
return m.body1.Bytes[size-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) bufferForGet() *buffer {
|
func (m *Message) bufferForGet() *buffer {
|
||||||
size := int(m.words * messageWordSize)
|
size := int(m.words * messageWordSize)
|
||||||
if m.body1.Offset == size || m.body1.Offset == len(m.body1.Bytes) {
|
// The static body has been exahusted, use the dynamic one.
|
||||||
// The static body has been exahusted, use the dynamic one.
|
if m.body.Offset == size {
|
||||||
if m.body1.Offset+m.body2.Offset == size {
|
err := fmt.Errorf("short message: type=%d words=%d off=%d", m.mtype, m.words, m.body.Offset)
|
||||||
err := fmt.Errorf("short message: type=%d words=%d off=%d", m.mtype, m.words, m.body1.Offset)
|
panic(err)
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return &m.body2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &m.body1
|
return &m.body
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result holds the result of a statement.
|
// Result holds the result of a statement.
|
||||||
|
@ -639,7 +575,7 @@ func (r *Rows) Close() error {
|
||||||
err = fmt.Errorf("unexpected end of message")
|
err = fmt.Errorf("unexpected end of message")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r.message.Reset()
|
r.message.reset()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -664,7 +600,7 @@ func (f *Files) Next() (string, []byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Files) Close() {
|
func (f *Files) Close() {
|
||||||
f.message.Reset()
|
f.message.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -13,31 +13,23 @@ import (
|
||||||
|
|
||||||
// Protocol sends and receive the dqlite message on the wire.
|
// Protocol sends and receive the dqlite message on the wire.
|
||||||
type Protocol struct {
|
type Protocol struct {
|
||||||
version uint64 // Protocol version
|
version uint64 // Protocol version
|
||||||
conn net.Conn // Underlying network connection.
|
conn net.Conn // Underlying network connection.
|
||||||
contextTimeout time.Duration // Default context timeout.
|
closeCh chan struct{} // Stops the heartbeat when the connection gets closed
|
||||||
closeCh chan struct{} // Stops the heartbeat when the connection gets closed
|
mu sync.Mutex // Serialize requests
|
||||||
mu sync.Mutex // Serialize requests
|
netErr error // A network error occurred
|
||||||
netErr error // A network error occurred
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProtocol(version uint64, conn net.Conn) *Protocol {
|
func newProtocol(version uint64, conn net.Conn) *Protocol {
|
||||||
protocol := &Protocol{
|
protocol := &Protocol{
|
||||||
version: version,
|
version: version,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
closeCh: make(chan struct{}),
|
closeCh: make(chan struct{}),
|
||||||
contextTimeout: 5 * time.Second,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return protocol
|
return protocol
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetContextTimeout sets the default context timeout when no deadline is
|
|
||||||
// provided.
|
|
||||||
func (p *Protocol) SetContextTimeout(timeout time.Duration) {
|
|
||||||
p.contextTimeout = timeout
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call invokes a dqlite RPC, sending a request message and receiving a
|
// Call invokes a dqlite RPC, sending a request message and receiving a
|
||||||
// response message.
|
// response message.
|
||||||
func (p *Protocol) Call(ctx context.Context, request, response *Message) (err error) {
|
func (p *Protocol) Call(ctx context.Context, request, response *Message) (err error) {
|
||||||
|
@ -50,21 +42,22 @@ func (p *Protocol) Call(ctx context.Context, request, response *Message) (err er
|
||||||
return p.netErr
|
return p.netErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Honor the ctx deadline, if present, or use a default.
|
var budget time.Duration
|
||||||
deadline, ok := ctx.Deadline()
|
|
||||||
if !ok {
|
// Honor the ctx deadline, if present.
|
||||||
deadline = time.Now().Add(p.contextTimeout)
|
if deadline, ok := ctx.Deadline(); ok {
|
||||||
|
p.conn.SetDeadline(deadline)
|
||||||
|
budget = time.Until(deadline)
|
||||||
|
defer p.conn.SetDeadline(time.Time{})
|
||||||
}
|
}
|
||||||
|
|
||||||
p.conn.SetDeadline(deadline)
|
|
||||||
|
|
||||||
if err = p.send(request); err != nil {
|
if err = p.send(request); err != nil {
|
||||||
err = errors.Wrap(err, "failed to send request")
|
err = errors.Wrapf(err, "send request (budget=%s)", budget)
|
||||||
goto err
|
goto err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = p.recv(response); err != nil {
|
if err = p.recv(response); err != nil {
|
||||||
err = errors.Wrap(err, "failed to receive response")
|
err = errors.Wrapf(err, "receive response (budget=%s)", budget)
|
||||||
goto err
|
goto err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,14 +84,11 @@ func (p *Protocol) Interrupt(ctx context.Context, request *Message, response *Me
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
// Honor the ctx deadline, if present, or use a default.
|
// Honor the ctx deadline, if present.
|
||||||
deadline, ok := ctx.Deadline()
|
if deadline, ok := ctx.Deadline(); ok {
|
||||||
if !ok {
|
p.conn.SetDeadline(deadline)
|
||||||
deadline = time.Now().Add(2 * time.Second)
|
defer p.conn.SetDeadline(time.Time{})
|
||||||
}
|
}
|
||||||
p.conn.SetDeadline(deadline)
|
|
||||||
|
|
||||||
defer request.Reset()
|
|
||||||
|
|
||||||
EncodeInterrupt(request, 0)
|
EncodeInterrupt(request, 0)
|
||||||
|
|
||||||
|
@ -108,12 +98,10 @@ func (p *Protocol) Interrupt(ctx context.Context, request *Message, response *Me
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if err := p.recv(response); err != nil {
|
if err := p.recv(response); err != nil {
|
||||||
response.Reset()
|
|
||||||
return errors.Wrap(err, "failed to receive response")
|
return errors.Wrap(err, "failed to receive response")
|
||||||
}
|
}
|
||||||
|
|
||||||
mtype, _ := response.getHeader()
|
mtype, _ := response.getHeader()
|
||||||
response.Reset()
|
|
||||||
|
|
||||||
if mtype == ResponseEmpty {
|
if mtype == ResponseEmpty {
|
||||||
break
|
break
|
||||||
|
@ -155,7 +143,7 @@ func (p *Protocol) sendHeader(req *Message) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Protocol) sendBody(req *Message) error {
|
func (p *Protocol) sendBody(req *Message) error {
|
||||||
buf := req.body1.Bytes[:req.body1.Offset]
|
buf := req.body.Bytes[:req.body.Offset]
|
||||||
n, err := p.conn.Write(buf)
|
n, err := p.conn.Write(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to send static body")
|
return errors.Wrap(err, "failed to send static body")
|
||||||
|
@ -165,24 +153,12 @@ func (p *Protocol) sendBody(req *Message) error {
|
||||||
return errors.Wrap(io.ErrShortWrite, "failed to write body")
|
return errors.Wrap(io.ErrShortWrite, "failed to write body")
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.body2.Bytes == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = req.body2.Bytes[:req.body2.Offset]
|
|
||||||
n, err = p.conn.Write(buf)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to send dynamic body")
|
|
||||||
}
|
|
||||||
|
|
||||||
if n != len(buf) {
|
|
||||||
return errors.Wrap(io.ErrShortWrite, "failed to write body")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Protocol) recv(res *Message) error {
|
func (p *Protocol) recv(res *Message) error {
|
||||||
|
res.reset()
|
||||||
|
|
||||||
if err := p.recvHeader(res); err != nil {
|
if err := p.recvHeader(res); err != nil {
|
||||||
return errors.Wrap(err, "failed to receive header")
|
return errors.Wrap(err, "failed to receive header")
|
||||||
}
|
}
|
||||||
|
@ -209,30 +185,19 @@ func (p *Protocol) recvHeader(res *Message) error {
|
||||||
|
|
||||||
func (p *Protocol) recvBody(res *Message) error {
|
func (p *Protocol) recvBody(res *Message) error {
|
||||||
n := int(res.words) * messageWordSize
|
n := int(res.words) * messageWordSize
|
||||||
n1 := n
|
|
||||||
n2 := 0
|
|
||||||
|
|
||||||
if n1 > len(res.body1.Bytes) {
|
for n > len(res.body.Bytes) {
|
||||||
// We need to allocate the dynamic buffer.
|
// Grow message buffer.
|
||||||
n1 = len(res.body1.Bytes)
|
bytes := make([]byte, len(res.body.Bytes)*2)
|
||||||
n2 = n - n1
|
res.body.Bytes = bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := res.body1.Bytes[:n1]
|
buf := res.body.Bytes[:n]
|
||||||
|
|
||||||
if err := p.recvPeek(buf); err != nil {
|
if err := p.recvPeek(buf); err != nil {
|
||||||
return errors.Wrap(err, "failed to read body")
|
return errors.Wrap(err, "failed to read body")
|
||||||
}
|
}
|
||||||
|
|
||||||
if n2 > 0 {
|
|
||||||
res.body2.Bytes = make([]byte, n2)
|
|
||||||
res.body2.Offset = 0
|
|
||||||
buf = res.body2.Bytes
|
|
||||||
if err := p.recvPeek(buf); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to read body")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ package protocol
|
||||||
|
|
||||||
// EncodeLeader encodes a Leader request.
|
// EncodeLeader encodes a Leader request.
|
||||||
func EncodeLeader(request *Message) {
|
func EncodeLeader(request *Message) {
|
||||||
|
request.reset()
|
||||||
request.putUint64(0)
|
request.putUint64(0)
|
||||||
|
|
||||||
request.putHeader(RequestLeader)
|
request.putHeader(RequestLeader)
|
||||||
|
@ -14,6 +15,7 @@ func EncodeLeader(request *Message) {
|
||||||
|
|
||||||
// EncodeClient encodes a Client request.
|
// EncodeClient encodes a Client request.
|
||||||
func EncodeClient(request *Message, id uint64) {
|
func EncodeClient(request *Message, id uint64) {
|
||||||
|
request.reset()
|
||||||
request.putUint64(id)
|
request.putUint64(id)
|
||||||
|
|
||||||
request.putHeader(RequestClient)
|
request.putHeader(RequestClient)
|
||||||
|
@ -21,6 +23,7 @@ func EncodeClient(request *Message, id uint64) {
|
||||||
|
|
||||||
// EncodeHeartbeat encodes a Heartbeat request.
|
// EncodeHeartbeat encodes a Heartbeat request.
|
||||||
func EncodeHeartbeat(request *Message, timestamp uint64) {
|
func EncodeHeartbeat(request *Message, timestamp uint64) {
|
||||||
|
request.reset()
|
||||||
request.putUint64(timestamp)
|
request.putUint64(timestamp)
|
||||||
|
|
||||||
request.putHeader(RequestHeartbeat)
|
request.putHeader(RequestHeartbeat)
|
||||||
|
@ -28,6 +31,7 @@ func EncodeHeartbeat(request *Message, timestamp uint64) {
|
||||||
|
|
||||||
// EncodeOpen encodes a Open request.
|
// EncodeOpen encodes a Open request.
|
||||||
func EncodeOpen(request *Message, name string, flags uint64, vfs string) {
|
func EncodeOpen(request *Message, name string, flags uint64, vfs string) {
|
||||||
|
request.reset()
|
||||||
request.putString(name)
|
request.putString(name)
|
||||||
request.putUint64(flags)
|
request.putUint64(flags)
|
||||||
request.putString(vfs)
|
request.putString(vfs)
|
||||||
|
@ -37,6 +41,7 @@ func EncodeOpen(request *Message, name string, flags uint64, vfs string) {
|
||||||
|
|
||||||
// EncodePrepare encodes a Prepare request.
|
// EncodePrepare encodes a Prepare request.
|
||||||
func EncodePrepare(request *Message, db uint64, sql string) {
|
func EncodePrepare(request *Message, db uint64, sql string) {
|
||||||
|
request.reset()
|
||||||
request.putUint64(db)
|
request.putUint64(db)
|
||||||
request.putString(sql)
|
request.putString(sql)
|
||||||
|
|
||||||
|
@ -45,6 +50,7 @@ func EncodePrepare(request *Message, db uint64, sql string) {
|
||||||
|
|
||||||
// EncodeExec encodes a Exec request.
|
// EncodeExec encodes a Exec request.
|
||||||
func EncodeExec(request *Message, db uint32, stmt uint32, values NamedValues) {
|
func EncodeExec(request *Message, db uint32, stmt uint32, values NamedValues) {
|
||||||
|
request.reset()
|
||||||
request.putUint32(db)
|
request.putUint32(db)
|
||||||
request.putUint32(stmt)
|
request.putUint32(stmt)
|
||||||
request.putNamedValues(values)
|
request.putNamedValues(values)
|
||||||
|
@ -54,6 +60,7 @@ func EncodeExec(request *Message, db uint32, stmt uint32, values NamedValues) {
|
||||||
|
|
||||||
// EncodeQuery encodes a Query request.
|
// EncodeQuery encodes a Query request.
|
||||||
func EncodeQuery(request *Message, db uint32, stmt uint32, values NamedValues) {
|
func EncodeQuery(request *Message, db uint32, stmt uint32, values NamedValues) {
|
||||||
|
request.reset()
|
||||||
request.putUint32(db)
|
request.putUint32(db)
|
||||||
request.putUint32(stmt)
|
request.putUint32(stmt)
|
||||||
request.putNamedValues(values)
|
request.putNamedValues(values)
|
||||||
|
@ -63,6 +70,7 @@ func EncodeQuery(request *Message, db uint32, stmt uint32, values NamedValues) {
|
||||||
|
|
||||||
// EncodeFinalize encodes a Finalize request.
|
// EncodeFinalize encodes a Finalize request.
|
||||||
func EncodeFinalize(request *Message, db uint32, stmt uint32) {
|
func EncodeFinalize(request *Message, db uint32, stmt uint32) {
|
||||||
|
request.reset()
|
||||||
request.putUint32(db)
|
request.putUint32(db)
|
||||||
request.putUint32(stmt)
|
request.putUint32(stmt)
|
||||||
|
|
||||||
|
@ -71,6 +79,7 @@ func EncodeFinalize(request *Message, db uint32, stmt uint32) {
|
||||||
|
|
||||||
// EncodeExecSQL encodes a ExecSQL request.
|
// EncodeExecSQL encodes a ExecSQL request.
|
||||||
func EncodeExecSQL(request *Message, db uint64, sql string, values NamedValues) {
|
func EncodeExecSQL(request *Message, db uint64, sql string, values NamedValues) {
|
||||||
|
request.reset()
|
||||||
request.putUint64(db)
|
request.putUint64(db)
|
||||||
request.putString(sql)
|
request.putString(sql)
|
||||||
request.putNamedValues(values)
|
request.putNamedValues(values)
|
||||||
|
@ -80,6 +89,7 @@ func EncodeExecSQL(request *Message, db uint64, sql string, values NamedValues)
|
||||||
|
|
||||||
// EncodeQuerySQL encodes a QuerySQL request.
|
// EncodeQuerySQL encodes a QuerySQL request.
|
||||||
func EncodeQuerySQL(request *Message, db uint64, sql string, values NamedValues) {
|
func EncodeQuerySQL(request *Message, db uint64, sql string, values NamedValues) {
|
||||||
|
request.reset()
|
||||||
request.putUint64(db)
|
request.putUint64(db)
|
||||||
request.putString(sql)
|
request.putString(sql)
|
||||||
request.putNamedValues(values)
|
request.putNamedValues(values)
|
||||||
|
@ -89,6 +99,7 @@ func EncodeQuerySQL(request *Message, db uint64, sql string, values NamedValues)
|
||||||
|
|
||||||
// EncodeInterrupt encodes a Interrupt request.
|
// EncodeInterrupt encodes a Interrupt request.
|
||||||
func EncodeInterrupt(request *Message, db uint64) {
|
func EncodeInterrupt(request *Message, db uint64) {
|
||||||
|
request.reset()
|
||||||
request.putUint64(db)
|
request.putUint64(db)
|
||||||
|
|
||||||
request.putHeader(RequestInterrupt)
|
request.putHeader(RequestInterrupt)
|
||||||
|
@ -96,6 +107,7 @@ func EncodeInterrupt(request *Message, db uint64) {
|
||||||
|
|
||||||
// EncodeAdd encodes a Add request.
|
// EncodeAdd encodes a Add request.
|
||||||
func EncodeAdd(request *Message, id uint64, address string) {
|
func EncodeAdd(request *Message, id uint64, address string) {
|
||||||
|
request.reset()
|
||||||
request.putUint64(id)
|
request.putUint64(id)
|
||||||
request.putString(address)
|
request.putString(address)
|
||||||
|
|
||||||
|
@ -104,6 +116,7 @@ func EncodeAdd(request *Message, id uint64, address string) {
|
||||||
|
|
||||||
// EncodeAssign encodes a Assign request.
|
// EncodeAssign encodes a Assign request.
|
||||||
func EncodeAssign(request *Message, id uint64, role uint64) {
|
func EncodeAssign(request *Message, id uint64, role uint64) {
|
||||||
|
request.reset()
|
||||||
request.putUint64(id)
|
request.putUint64(id)
|
||||||
request.putUint64(role)
|
request.putUint64(role)
|
||||||
|
|
||||||
|
@ -112,6 +125,7 @@ func EncodeAssign(request *Message, id uint64, role uint64) {
|
||||||
|
|
||||||
// EncodeRemove encodes a Remove request.
|
// EncodeRemove encodes a Remove request.
|
||||||
func EncodeRemove(request *Message, id uint64) {
|
func EncodeRemove(request *Message, id uint64) {
|
||||||
|
request.reset()
|
||||||
request.putUint64(id)
|
request.putUint64(id)
|
||||||
|
|
||||||
request.putHeader(RequestRemove)
|
request.putHeader(RequestRemove)
|
||||||
|
@ -119,6 +133,7 @@ func EncodeRemove(request *Message, id uint64) {
|
||||||
|
|
||||||
// EncodeDump encodes a Dump request.
|
// EncodeDump encodes a Dump request.
|
||||||
func EncodeDump(request *Message, name string) {
|
func EncodeDump(request *Message, name string) {
|
||||||
|
request.reset()
|
||||||
request.putString(name)
|
request.putString(name)
|
||||||
|
|
||||||
request.putHeader(RequestDump)
|
request.putHeader(RequestDump)
|
||||||
|
@ -126,6 +141,7 @@ func EncodeDump(request *Message, name string) {
|
||||||
|
|
||||||
// EncodeCluster encodes a Cluster request.
|
// EncodeCluster encodes a Cluster request.
|
||||||
func EncodeCluster(request *Message, format uint64) {
|
func EncodeCluster(request *Message, format uint64) {
|
||||||
|
request.reset()
|
||||||
request.putUint64(format)
|
request.putUint64(format)
|
||||||
|
|
||||||
request.putHeader(RequestCluster)
|
request.putHeader(RequestCluster)
|
||||||
|
@ -133,6 +149,7 @@ func EncodeCluster(request *Message, format uint64) {
|
||||||
|
|
||||||
// EncodeTransfer encodes a Transfer request.
|
// EncodeTransfer encodes a Transfer request.
|
||||||
func EncodeTransfer(request *Message, id uint64) {
|
func EncodeTransfer(request *Message, id uint64) {
|
||||||
|
request.reset()
|
||||||
request.putUint64(id)
|
request.putUint64(id)
|
||||||
|
|
||||||
request.putHeader(RequestTransfer)
|
request.putHeader(RequestTransfer)
|
||||||
|
|
|
@ -54,6 +54,7 @@ if [ "$entity" = "--request" ]; then
|
||||||
|
|
||||||
// Encode${cmd} encodes a $cmd request.
|
// Encode${cmd} encodes a $cmd request.
|
||||||
func Encode${cmd}(request *Message${args}) {
|
func Encode${cmd}(request *Message${args}) {
|
||||||
|
request.reset()
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
for i in "${@}"
|
for i in "${@}"
|
||||||
|
|
|
@ -118,6 +118,16 @@ func (s *Node) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BootstrapID is a magic ID that should be used for the fist node in a
|
||||||
|
// cluster. Alternatively ID 1 can be used as well.
|
||||||
|
const BootstrapID = 0x2dc171858c3155be
|
||||||
|
|
||||||
|
// GenerateID generates a unique ID for a new node, based on a hash of its
|
||||||
|
// address and the current time.
|
||||||
|
func GenerateID(address string) uint64 {
|
||||||
|
return bindings.GenerateID(address)
|
||||||
|
}
|
||||||
|
|
||||||
// Create a options object with sane defaults.
|
// Create a options object with sane defaults.
|
||||||
func defaultOptions() *options {
|
func defaultOptions() *options {
|
||||||
return &options{
|
return &options{
|
||||||
|
|
|
@ -39,9 +39,10 @@ type Controller struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
image = "rancher/klipper-helm:v0.2.3"
|
image = "rancher/klipper-helm:v0.2.5"
|
||||||
label = "helmcharts.helm.cattle.io/chart"
|
Label = "helmcharts.helm.cattle.io/chart"
|
||||||
name = "helm-controller"
|
CRDName = "helmcharts.helm.cattle.io"
|
||||||
|
Name = "helm-controller"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Register(ctx context.Context, apply apply.Apply,
|
func Register(ctx context.Context, apply apply.Apply,
|
||||||
|
@ -50,7 +51,7 @@ func Register(ctx context.Context, apply apply.Apply,
|
||||||
crbs rbaccontroller.ClusterRoleBindingController,
|
crbs rbaccontroller.ClusterRoleBindingController,
|
||||||
sas corecontroller.ServiceAccountController,
|
sas corecontroller.ServiceAccountController,
|
||||||
cm corecontroller.ConfigMapController) {
|
cm corecontroller.ConfigMapController) {
|
||||||
apply = apply.WithSetID(name).
|
apply = apply.WithSetID(Name).
|
||||||
WithCacheTypes(helms, jobs, crbs, sas, cm).
|
WithCacheTypes(helms, jobs, crbs, sas, cm).
|
||||||
WithStrictCaching().WithPatcher(batch.SchemeGroupVersion.WithKind("Job"), func(namespace, name string, pt types.PatchType, data []byte) (runtime.Object, error) {
|
WithStrictCaching().WithPatcher(batch.SchemeGroupVersion.WithKind("Job"), func(namespace, name string, pt types.PatchType, data []byte) (runtime.Object, error) {
|
||||||
err := jobs.Delete(namespace, name, &metav1.DeleteOptions{})
|
err := jobs.Delete(namespace, name, &metav1.DeleteOptions{})
|
||||||
|
@ -63,7 +64,7 @@ func Register(ctx context.Context, apply apply.Apply,
|
||||||
relatedresource.Watch(ctx, "helm-pod-watch",
|
relatedresource.Watch(ctx, "helm-pod-watch",
|
||||||
func(namespace, name string, obj runtime.Object) ([]relatedresource.Key, error) {
|
func(namespace, name string, obj runtime.Object) ([]relatedresource.Key, error) {
|
||||||
if job, ok := obj.(*batch.Job); ok {
|
if job, ok := obj.(*batch.Job); ok {
|
||||||
name := job.Labels[label]
|
name := job.Labels[Label]
|
||||||
if name != "" {
|
if name != "" {
|
||||||
return []relatedresource.Key{
|
return []relatedresource.Key{
|
||||||
{
|
{
|
||||||
|
@ -84,8 +85,8 @@ func Register(ctx context.Context, apply apply.Apply,
|
||||||
apply: apply,
|
apply: apply,
|
||||||
}
|
}
|
||||||
|
|
||||||
helms.OnChange(ctx, name, controller.OnHelmChanged)
|
helms.OnChange(ctx, Name, controller.OnHelmChanged)
|
||||||
helms.OnRemove(ctx, name, controller.OnHelmRemove)
|
helms.OnRemove(ctx, Name, controller.OnHelmRemove)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) OnHelmChanged(key string, chart *helmv1.HelmChart) (*helmv1.HelmChart, error) {
|
func (c *Controller) OnHelmChanged(key string, chart *helmv1.HelmChart) (*helmv1.HelmChart, error) {
|
||||||
|
@ -163,7 +164,7 @@ func job(chart *helmv1.HelmChart) (*batch.Job, *core.ConfigMap) {
|
||||||
Name: fmt.Sprintf("helm-%s-%s", action, chart.Name),
|
Name: fmt.Sprintf("helm-%s-%s", action, chart.Name),
|
||||||
Namespace: chart.Namespace,
|
Namespace: chart.Namespace,
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
label: chart.Name,
|
Label: chart.Name,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: batch.JobSpec{
|
Spec: batch.JobSpec{
|
||||||
|
@ -171,7 +172,7 @@ func job(chart *helmv1.HelmChart) (*batch.Job, *core.ConfigMap) {
|
||||||
Template: core.PodTemplateSpec{
|
Template: core.PodTemplateSpec{
|
||||||
ObjectMeta: meta.ObjectMeta{
|
ObjectMeta: meta.ObjectMeta{
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
label: chart.Name,
|
Label: chart.Name,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: core.PodSpec{
|
Spec: core.PodSpec{
|
||||||
|
|
|
@ -35,6 +35,7 @@ var (
|
||||||
);`,
|
);`,
|
||||||
}
|
}
|
||||||
nameIdx = "create index kine_name_index on kine (name)"
|
nameIdx = "create index kine_name_index on kine (name)"
|
||||||
|
nameIDIdx = "create index kine_name_id_index on kine (name,id)"
|
||||||
revisionIdx = "create unique index kine_name_prev_revision_uindex on kine (name, prev_revision)"
|
revisionIdx = "create unique index kine_name_prev_revision_uindex on kine (name, prev_revision)"
|
||||||
createDB = "create database if not exists "
|
createDB = "create database if not exists "
|
||||||
)
|
)
|
||||||
|
@ -87,6 +88,7 @@ func setup(db *sql.DB) error {
|
||||||
// check if duplicate indexes
|
// check if duplicate indexes
|
||||||
indexes := []string{
|
indexes := []string{
|
||||||
nameIdx,
|
nameIdx,
|
||||||
|
nameIDIdx,
|
||||||
revisionIdx}
|
revisionIdx}
|
||||||
|
|
||||||
for _, idx := range indexes {
|
for _, idx := range indexes {
|
||||||
|
|
|
@ -35,6 +35,7 @@ var (
|
||||||
old_value bytea
|
old_value bytea
|
||||||
);`,
|
);`,
|
||||||
`CREATE INDEX IF NOT EXISTS kine_name_index ON kine (name)`,
|
`CREATE INDEX IF NOT EXISTS kine_name_index ON kine (name)`,
|
||||||
|
`CREATE INDEX IF NOT EXISTS kine_name_id_index ON kine (name,id)`,
|
||||||
`CREATE UNIQUE INDEX IF NOT EXISTS kine_name_prev_revision_uindex ON kine (name, prev_revision)`,
|
`CREATE UNIQUE INDEX IF NOT EXISTS kine_name_prev_revision_uindex ON kine (name, prev_revision)`,
|
||||||
}
|
}
|
||||||
createDB = "create database "
|
createDB = "create database "
|
||||||
|
|
|
@ -295,6 +295,7 @@ func (l *LogStructured) ttlEvents(ctx context.Context) chan *server.Event {
|
||||||
|
|
||||||
func (l *LogStructured) ttl(ctx context.Context) {
|
func (l *LogStructured) ttl(ctx context.Context) {
|
||||||
// vary naive TTL support
|
// vary naive TTL support
|
||||||
|
mutex := &sync.Mutex{}
|
||||||
for event := range l.ttlEvents(ctx) {
|
for event := range l.ttlEvents(ctx) {
|
||||||
go func(event *server.Event) {
|
go func(event *server.Event) {
|
||||||
select {
|
select {
|
||||||
|
@ -302,7 +303,9 @@ func (l *LogStructured) ttl(ctx context.Context) {
|
||||||
return
|
return
|
||||||
case <-time.After(time.Duration(event.KV.Lease) * time.Second):
|
case <-time.After(time.Duration(event.KV.Lease) * time.Second):
|
||||||
}
|
}
|
||||||
|
mutex.Lock()
|
||||||
l.Delete(ctx, event.KV.Key, event.KV.ModRevision)
|
l.Delete(ctx, event.KV.Key, event.KV.ModRevision)
|
||||||
|
mutex.Unlock()
|
||||||
}(event)
|
}(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
35
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/apps/factory.go
generated
vendored
35
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/apps/factory.go
generated
vendored
|
@ -52,18 +52,23 @@ func NewFactoryFromConfigOrDie(config *rest.Config) *Factory {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactoryFromConfig(config *rest.Config) (*Factory, error) {
|
func NewFactoryFromConfig(config *rest.Config) (*Factory, error) {
|
||||||
cs, err := clientset.NewForConfig(config)
|
return NewFactoryFromConfigWithOptions(config, nil)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
informerFactory := informers.NewSharedInformerFactory(cs, 2*time.Hour)
|
|
||||||
return NewFactory(cs, informerFactory), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {
|
func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {
|
||||||
if namespace == "" {
|
return NewFactoryFromConfigWithOptions(config, &FactoryOptions{
|
||||||
return NewFactoryFromConfig(config)
|
Namespace: namespace,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type FactoryOptions struct {
|
||||||
|
Namespace string
|
||||||
|
Resync time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &FactoryOptions{}
|
||||||
}
|
}
|
||||||
|
|
||||||
cs, err := clientset.NewForConfig(config)
|
cs, err := clientset.NewForConfig(config)
|
||||||
|
@ -71,7 +76,17 @@ func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
informerFactory := informers.NewSharedInformerFactoryWithOptions(cs, 2*time.Hour, informers.WithNamespace(namespace))
|
resync := opts.Resync
|
||||||
|
if resync == 0 {
|
||||||
|
resync = 2 * time.Hour
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Namespace == "" {
|
||||||
|
informerFactory := informers.NewSharedInformerFactory(cs, resync)
|
||||||
|
return NewFactory(cs, informerFactory), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
informerFactory := informers.NewSharedInformerFactoryWithOptions(cs, resync, informers.WithNamespace(opts.Namespace))
|
||||||
return NewFactory(cs, informerFactory), nil
|
return NewFactory(cs, informerFactory), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/apps/v1/daemonset.go
generated
vendored
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/apps/v1/daemonset.go
generated
vendored
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/rancher/wrangler/pkg/apply"
|
"github.com/rancher/wrangler/pkg/apply"
|
||||||
"github.com/rancher/wrangler/pkg/condition"
|
"github.com/rancher/wrangler/pkg/condition"
|
||||||
"github.com/rancher/wrangler/pkg/generic"
|
"github.com/rancher/wrangler/pkg/generic"
|
||||||
|
"github.com/rancher/wrangler/pkg/kv"
|
||||||
v1 "k8s.io/api/apps/v1"
|
v1 "k8s.io/api/apps/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
@ -267,6 +268,7 @@ func RegisterDaemonSetGeneratingHandler(ctx context.Context, controller DaemonSe
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
statusHandler.opts = *opts
|
statusHandler.opts = *opts
|
||||||
}
|
}
|
||||||
|
controller.OnChange(ctx, name, statusHandler.Remove)
|
||||||
RegisterDaemonSetStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
RegisterDaemonSetStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +283,7 @@ func (a *daemonSetStatusHandler) sync(key string, obj *v1.DaemonSet) (*v1.Daemon
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
origStatus := obj.Status
|
origStatus := obj.Status.DeepCopy()
|
||||||
obj = obj.DeepCopy()
|
obj = obj.DeepCopy()
|
||||||
newStatus, err := a.handler(obj, obj.Status)
|
newStatus, err := a.handler(obj, obj.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -289,16 +291,16 @@ func (a *daemonSetStatusHandler) sync(key string, obj *v1.DaemonSet) (*v1.Daemon
|
||||||
newStatus = *origStatus.DeepCopy()
|
newStatus = *origStatus.DeepCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.Status = newStatus
|
|
||||||
if a.condition != "" {
|
if a.condition != "" {
|
||||||
if errors.IsConflict(err) {
|
if errors.IsConflict(err) {
|
||||||
a.condition.SetError(obj, "", nil)
|
a.condition.SetError(&newStatus, "", nil)
|
||||||
} else {
|
} else {
|
||||||
a.condition.SetError(obj, "", err)
|
a.condition.SetError(&newStatus, "", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !equality.Semantic.DeepEqual(origStatus, obj.Status) {
|
if !equality.Semantic.DeepEqual(origStatus, &newStatus) {
|
||||||
var newErr error
|
var newErr error
|
||||||
|
obj.Status = newStatus
|
||||||
obj, newErr = a.client.UpdateStatus(obj)
|
obj, newErr = a.client.UpdateStatus(obj)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = newErr
|
err = newErr
|
||||||
|
@ -315,29 +317,28 @@ type daemonSetGeneratingHandler struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *daemonSetGeneratingHandler) Remove(key string, obj *v1.DaemonSet) (*v1.DaemonSet, error) {
|
||||||
|
if obj != nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = &v1.DaemonSet{}
|
||||||
|
obj.Namespace, obj.Name = kv.RSplit(key, "/")
|
||||||
|
obj.SetGroupVersionKind(a.gvk)
|
||||||
|
|
||||||
|
return nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
WithOwner(obj).
|
||||||
|
WithSetID(a.name).
|
||||||
|
ApplyObjects()
|
||||||
|
}
|
||||||
|
|
||||||
func (a *daemonSetGeneratingHandler) Handle(obj *v1.DaemonSet, status v1.DaemonSetStatus) (v1.DaemonSetStatus, error) {
|
func (a *daemonSetGeneratingHandler) Handle(obj *v1.DaemonSet, status v1.DaemonSetStatus) (v1.DaemonSetStatus, error) {
|
||||||
objs, newStatus, err := a.DaemonSetGeneratingHandler(obj, status)
|
objs, newStatus, err := a.DaemonSetGeneratingHandler(obj, status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newStatus, err
|
return newStatus, err
|
||||||
}
|
}
|
||||||
|
|
||||||
apply := a.apply
|
return newStatus, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
|
||||||
if !a.opts.DynamicLookup {
|
|
||||||
apply = apply.WithStrictCaching()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowCrossNamespace && !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithSetOwnerReference(true, false).
|
|
||||||
WithDefaultNamespace(obj.GetNamespace()).
|
|
||||||
WithListerNamespace(obj.GetNamespace())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithRestrictClusterScoped()
|
|
||||||
}
|
|
||||||
|
|
||||||
return newStatus, apply.
|
|
||||||
WithOwner(obj).
|
WithOwner(obj).
|
||||||
WithSetID(a.name).
|
WithSetID(a.name).
|
||||||
ApplyObjects(objs...)
|
ApplyObjects(objs...)
|
||||||
|
|
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/apps/v1/deployment.go
generated
vendored
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/apps/v1/deployment.go
generated
vendored
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/rancher/wrangler/pkg/apply"
|
"github.com/rancher/wrangler/pkg/apply"
|
||||||
"github.com/rancher/wrangler/pkg/condition"
|
"github.com/rancher/wrangler/pkg/condition"
|
||||||
"github.com/rancher/wrangler/pkg/generic"
|
"github.com/rancher/wrangler/pkg/generic"
|
||||||
|
"github.com/rancher/wrangler/pkg/kv"
|
||||||
v1 "k8s.io/api/apps/v1"
|
v1 "k8s.io/api/apps/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
@ -267,6 +268,7 @@ func RegisterDeploymentGeneratingHandler(ctx context.Context, controller Deploym
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
statusHandler.opts = *opts
|
statusHandler.opts = *opts
|
||||||
}
|
}
|
||||||
|
controller.OnChange(ctx, name, statusHandler.Remove)
|
||||||
RegisterDeploymentStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
RegisterDeploymentStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +283,7 @@ func (a *deploymentStatusHandler) sync(key string, obj *v1.Deployment) (*v1.Depl
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
origStatus := obj.Status
|
origStatus := obj.Status.DeepCopy()
|
||||||
obj = obj.DeepCopy()
|
obj = obj.DeepCopy()
|
||||||
newStatus, err := a.handler(obj, obj.Status)
|
newStatus, err := a.handler(obj, obj.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -289,16 +291,16 @@ func (a *deploymentStatusHandler) sync(key string, obj *v1.Deployment) (*v1.Depl
|
||||||
newStatus = *origStatus.DeepCopy()
|
newStatus = *origStatus.DeepCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.Status = newStatus
|
|
||||||
if a.condition != "" {
|
if a.condition != "" {
|
||||||
if errors.IsConflict(err) {
|
if errors.IsConflict(err) {
|
||||||
a.condition.SetError(obj, "", nil)
|
a.condition.SetError(&newStatus, "", nil)
|
||||||
} else {
|
} else {
|
||||||
a.condition.SetError(obj, "", err)
|
a.condition.SetError(&newStatus, "", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !equality.Semantic.DeepEqual(origStatus, obj.Status) {
|
if !equality.Semantic.DeepEqual(origStatus, &newStatus) {
|
||||||
var newErr error
|
var newErr error
|
||||||
|
obj.Status = newStatus
|
||||||
obj, newErr = a.client.UpdateStatus(obj)
|
obj, newErr = a.client.UpdateStatus(obj)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = newErr
|
err = newErr
|
||||||
|
@ -315,29 +317,28 @@ type deploymentGeneratingHandler struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *deploymentGeneratingHandler) Remove(key string, obj *v1.Deployment) (*v1.Deployment, error) {
|
||||||
|
if obj != nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = &v1.Deployment{}
|
||||||
|
obj.Namespace, obj.Name = kv.RSplit(key, "/")
|
||||||
|
obj.SetGroupVersionKind(a.gvk)
|
||||||
|
|
||||||
|
return nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
WithOwner(obj).
|
||||||
|
WithSetID(a.name).
|
||||||
|
ApplyObjects()
|
||||||
|
}
|
||||||
|
|
||||||
func (a *deploymentGeneratingHandler) Handle(obj *v1.Deployment, status v1.DeploymentStatus) (v1.DeploymentStatus, error) {
|
func (a *deploymentGeneratingHandler) Handle(obj *v1.Deployment, status v1.DeploymentStatus) (v1.DeploymentStatus, error) {
|
||||||
objs, newStatus, err := a.DeploymentGeneratingHandler(obj, status)
|
objs, newStatus, err := a.DeploymentGeneratingHandler(obj, status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newStatus, err
|
return newStatus, err
|
||||||
}
|
}
|
||||||
|
|
||||||
apply := a.apply
|
return newStatus, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
|
||||||
if !a.opts.DynamicLookup {
|
|
||||||
apply = apply.WithStrictCaching()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowCrossNamespace && !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithSetOwnerReference(true, false).
|
|
||||||
WithDefaultNamespace(obj.GetNamespace()).
|
|
||||||
WithListerNamespace(obj.GetNamespace())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithRestrictClusterScoped()
|
|
||||||
}
|
|
||||||
|
|
||||||
return newStatus, apply.
|
|
||||||
WithOwner(obj).
|
WithOwner(obj).
|
||||||
WithSetID(a.name).
|
WithSetID(a.name).
|
||||||
ApplyObjects(objs...)
|
ApplyObjects(objs...)
|
||||||
|
|
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/apps/v1/statefulset.go
generated
vendored
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/apps/v1/statefulset.go
generated
vendored
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/rancher/wrangler/pkg/apply"
|
"github.com/rancher/wrangler/pkg/apply"
|
||||||
"github.com/rancher/wrangler/pkg/condition"
|
"github.com/rancher/wrangler/pkg/condition"
|
||||||
"github.com/rancher/wrangler/pkg/generic"
|
"github.com/rancher/wrangler/pkg/generic"
|
||||||
|
"github.com/rancher/wrangler/pkg/kv"
|
||||||
v1 "k8s.io/api/apps/v1"
|
v1 "k8s.io/api/apps/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
@ -267,6 +268,7 @@ func RegisterStatefulSetGeneratingHandler(ctx context.Context, controller Statef
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
statusHandler.opts = *opts
|
statusHandler.opts = *opts
|
||||||
}
|
}
|
||||||
|
controller.OnChange(ctx, name, statusHandler.Remove)
|
||||||
RegisterStatefulSetStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
RegisterStatefulSetStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +283,7 @@ func (a *statefulSetStatusHandler) sync(key string, obj *v1.StatefulSet) (*v1.St
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
origStatus := obj.Status
|
origStatus := obj.Status.DeepCopy()
|
||||||
obj = obj.DeepCopy()
|
obj = obj.DeepCopy()
|
||||||
newStatus, err := a.handler(obj, obj.Status)
|
newStatus, err := a.handler(obj, obj.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -289,16 +291,16 @@ func (a *statefulSetStatusHandler) sync(key string, obj *v1.StatefulSet) (*v1.St
|
||||||
newStatus = *origStatus.DeepCopy()
|
newStatus = *origStatus.DeepCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.Status = newStatus
|
|
||||||
if a.condition != "" {
|
if a.condition != "" {
|
||||||
if errors.IsConflict(err) {
|
if errors.IsConflict(err) {
|
||||||
a.condition.SetError(obj, "", nil)
|
a.condition.SetError(&newStatus, "", nil)
|
||||||
} else {
|
} else {
|
||||||
a.condition.SetError(obj, "", err)
|
a.condition.SetError(&newStatus, "", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !equality.Semantic.DeepEqual(origStatus, obj.Status) {
|
if !equality.Semantic.DeepEqual(origStatus, &newStatus) {
|
||||||
var newErr error
|
var newErr error
|
||||||
|
obj.Status = newStatus
|
||||||
obj, newErr = a.client.UpdateStatus(obj)
|
obj, newErr = a.client.UpdateStatus(obj)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = newErr
|
err = newErr
|
||||||
|
@ -315,29 +317,28 @@ type statefulSetGeneratingHandler struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *statefulSetGeneratingHandler) Remove(key string, obj *v1.StatefulSet) (*v1.StatefulSet, error) {
|
||||||
|
if obj != nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = &v1.StatefulSet{}
|
||||||
|
obj.Namespace, obj.Name = kv.RSplit(key, "/")
|
||||||
|
obj.SetGroupVersionKind(a.gvk)
|
||||||
|
|
||||||
|
return nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
WithOwner(obj).
|
||||||
|
WithSetID(a.name).
|
||||||
|
ApplyObjects()
|
||||||
|
}
|
||||||
|
|
||||||
func (a *statefulSetGeneratingHandler) Handle(obj *v1.StatefulSet, status v1.StatefulSetStatus) (v1.StatefulSetStatus, error) {
|
func (a *statefulSetGeneratingHandler) Handle(obj *v1.StatefulSet, status v1.StatefulSetStatus) (v1.StatefulSetStatus, error) {
|
||||||
objs, newStatus, err := a.StatefulSetGeneratingHandler(obj, status)
|
objs, newStatus, err := a.StatefulSetGeneratingHandler(obj, status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newStatus, err
|
return newStatus, err
|
||||||
}
|
}
|
||||||
|
|
||||||
apply := a.apply
|
return newStatus, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
|
||||||
if !a.opts.DynamicLookup {
|
|
||||||
apply = apply.WithStrictCaching()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowCrossNamespace && !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithSetOwnerReference(true, false).
|
|
||||||
WithDefaultNamespace(obj.GetNamespace()).
|
|
||||||
WithListerNamespace(obj.GetNamespace())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithRestrictClusterScoped()
|
|
||||||
}
|
|
||||||
|
|
||||||
return newStatus, apply.
|
|
||||||
WithOwner(obj).
|
WithOwner(obj).
|
||||||
WithSetID(a.name).
|
WithSetID(a.name).
|
||||||
ApplyObjects(objs...)
|
ApplyObjects(objs...)
|
||||||
|
|
35
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/batch/factory.go
generated
vendored
35
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/batch/factory.go
generated
vendored
|
@ -52,18 +52,23 @@ func NewFactoryFromConfigOrDie(config *rest.Config) *Factory {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactoryFromConfig(config *rest.Config) (*Factory, error) {
|
func NewFactoryFromConfig(config *rest.Config) (*Factory, error) {
|
||||||
cs, err := clientset.NewForConfig(config)
|
return NewFactoryFromConfigWithOptions(config, nil)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
informerFactory := informers.NewSharedInformerFactory(cs, 2*time.Hour)
|
|
||||||
return NewFactory(cs, informerFactory), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {
|
func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {
|
||||||
if namespace == "" {
|
return NewFactoryFromConfigWithOptions(config, &FactoryOptions{
|
||||||
return NewFactoryFromConfig(config)
|
Namespace: namespace,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type FactoryOptions struct {
|
||||||
|
Namespace string
|
||||||
|
Resync time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &FactoryOptions{}
|
||||||
}
|
}
|
||||||
|
|
||||||
cs, err := clientset.NewForConfig(config)
|
cs, err := clientset.NewForConfig(config)
|
||||||
|
@ -71,7 +76,17 @@ func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
informerFactory := informers.NewSharedInformerFactoryWithOptions(cs, 2*time.Hour, informers.WithNamespace(namespace))
|
resync := opts.Resync
|
||||||
|
if resync == 0 {
|
||||||
|
resync = 2 * time.Hour
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Namespace == "" {
|
||||||
|
informerFactory := informers.NewSharedInformerFactory(cs, resync)
|
||||||
|
return NewFactory(cs, informerFactory), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
informerFactory := informers.NewSharedInformerFactoryWithOptions(cs, resync, informers.WithNamespace(opts.Namespace))
|
||||||
return NewFactory(cs, informerFactory), nil
|
return NewFactory(cs, informerFactory), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/batch/v1/job.go
generated
vendored
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/batch/v1/job.go
generated
vendored
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/rancher/wrangler/pkg/apply"
|
"github.com/rancher/wrangler/pkg/apply"
|
||||||
"github.com/rancher/wrangler/pkg/condition"
|
"github.com/rancher/wrangler/pkg/condition"
|
||||||
"github.com/rancher/wrangler/pkg/generic"
|
"github.com/rancher/wrangler/pkg/generic"
|
||||||
|
"github.com/rancher/wrangler/pkg/kv"
|
||||||
v1 "k8s.io/api/batch/v1"
|
v1 "k8s.io/api/batch/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
@ -267,6 +268,7 @@ func RegisterJobGeneratingHandler(ctx context.Context, controller JobController,
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
statusHandler.opts = *opts
|
statusHandler.opts = *opts
|
||||||
}
|
}
|
||||||
|
controller.OnChange(ctx, name, statusHandler.Remove)
|
||||||
RegisterJobStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
RegisterJobStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +283,7 @@ func (a *jobStatusHandler) sync(key string, obj *v1.Job) (*v1.Job, error) {
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
origStatus := obj.Status
|
origStatus := obj.Status.DeepCopy()
|
||||||
obj = obj.DeepCopy()
|
obj = obj.DeepCopy()
|
||||||
newStatus, err := a.handler(obj, obj.Status)
|
newStatus, err := a.handler(obj, obj.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -289,16 +291,16 @@ func (a *jobStatusHandler) sync(key string, obj *v1.Job) (*v1.Job, error) {
|
||||||
newStatus = *origStatus.DeepCopy()
|
newStatus = *origStatus.DeepCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.Status = newStatus
|
|
||||||
if a.condition != "" {
|
if a.condition != "" {
|
||||||
if errors.IsConflict(err) {
|
if errors.IsConflict(err) {
|
||||||
a.condition.SetError(obj, "", nil)
|
a.condition.SetError(&newStatus, "", nil)
|
||||||
} else {
|
} else {
|
||||||
a.condition.SetError(obj, "", err)
|
a.condition.SetError(&newStatus, "", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !equality.Semantic.DeepEqual(origStatus, obj.Status) {
|
if !equality.Semantic.DeepEqual(origStatus, &newStatus) {
|
||||||
var newErr error
|
var newErr error
|
||||||
|
obj.Status = newStatus
|
||||||
obj, newErr = a.client.UpdateStatus(obj)
|
obj, newErr = a.client.UpdateStatus(obj)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = newErr
|
err = newErr
|
||||||
|
@ -315,29 +317,28 @@ type jobGeneratingHandler struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *jobGeneratingHandler) Remove(key string, obj *v1.Job) (*v1.Job, error) {
|
||||||
|
if obj != nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = &v1.Job{}
|
||||||
|
obj.Namespace, obj.Name = kv.RSplit(key, "/")
|
||||||
|
obj.SetGroupVersionKind(a.gvk)
|
||||||
|
|
||||||
|
return nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
WithOwner(obj).
|
||||||
|
WithSetID(a.name).
|
||||||
|
ApplyObjects()
|
||||||
|
}
|
||||||
|
|
||||||
func (a *jobGeneratingHandler) Handle(obj *v1.Job, status v1.JobStatus) (v1.JobStatus, error) {
|
func (a *jobGeneratingHandler) Handle(obj *v1.Job, status v1.JobStatus) (v1.JobStatus, error) {
|
||||||
objs, newStatus, err := a.JobGeneratingHandler(obj, status)
|
objs, newStatus, err := a.JobGeneratingHandler(obj, status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newStatus, err
|
return newStatus, err
|
||||||
}
|
}
|
||||||
|
|
||||||
apply := a.apply
|
return newStatus, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
|
||||||
if !a.opts.DynamicLookup {
|
|
||||||
apply = apply.WithStrictCaching()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowCrossNamespace && !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithSetOwnerReference(true, false).
|
|
||||||
WithDefaultNamespace(obj.GetNamespace()).
|
|
||||||
WithListerNamespace(obj.GetNamespace())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithRestrictClusterScoped()
|
|
||||||
}
|
|
||||||
|
|
||||||
return newStatus, apply.
|
|
||||||
WithOwner(obj).
|
WithOwner(obj).
|
||||||
WithSetID(a.name).
|
WithSetID(a.name).
|
||||||
ApplyObjects(objs...)
|
ApplyObjects(objs...)
|
||||||
|
|
35
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/core/factory.go
generated
vendored
35
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/core/factory.go
generated
vendored
|
@ -52,18 +52,23 @@ func NewFactoryFromConfigOrDie(config *rest.Config) *Factory {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactoryFromConfig(config *rest.Config) (*Factory, error) {
|
func NewFactoryFromConfig(config *rest.Config) (*Factory, error) {
|
||||||
cs, err := clientset.NewForConfig(config)
|
return NewFactoryFromConfigWithOptions(config, nil)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
informerFactory := informers.NewSharedInformerFactory(cs, 2*time.Hour)
|
|
||||||
return NewFactory(cs, informerFactory), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {
|
func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {
|
||||||
if namespace == "" {
|
return NewFactoryFromConfigWithOptions(config, &FactoryOptions{
|
||||||
return NewFactoryFromConfig(config)
|
Namespace: namespace,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type FactoryOptions struct {
|
||||||
|
Namespace string
|
||||||
|
Resync time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &FactoryOptions{}
|
||||||
}
|
}
|
||||||
|
|
||||||
cs, err := clientset.NewForConfig(config)
|
cs, err := clientset.NewForConfig(config)
|
||||||
|
@ -71,7 +76,17 @@ func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
informerFactory := informers.NewSharedInformerFactoryWithOptions(cs, 2*time.Hour, informers.WithNamespace(namespace))
|
resync := opts.Resync
|
||||||
|
if resync == 0 {
|
||||||
|
resync = 2 * time.Hour
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Namespace == "" {
|
||||||
|
informerFactory := informers.NewSharedInformerFactory(cs, resync)
|
||||||
|
return NewFactory(cs, informerFactory), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
informerFactory := informers.NewSharedInformerFactoryWithOptions(cs, resync, informers.WithNamespace(opts.Namespace))
|
||||||
return NewFactory(cs, informerFactory), nil
|
return NewFactory(cs, informerFactory), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1/namespace.go
generated
vendored
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1/namespace.go
generated
vendored
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/rancher/wrangler/pkg/apply"
|
"github.com/rancher/wrangler/pkg/apply"
|
||||||
"github.com/rancher/wrangler/pkg/condition"
|
"github.com/rancher/wrangler/pkg/condition"
|
||||||
"github.com/rancher/wrangler/pkg/generic"
|
"github.com/rancher/wrangler/pkg/generic"
|
||||||
|
"github.com/rancher/wrangler/pkg/kv"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
@ -267,6 +268,7 @@ func RegisterNamespaceGeneratingHandler(ctx context.Context, controller Namespac
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
statusHandler.opts = *opts
|
statusHandler.opts = *opts
|
||||||
}
|
}
|
||||||
|
controller.OnChange(ctx, name, statusHandler.Remove)
|
||||||
RegisterNamespaceStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
RegisterNamespaceStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +283,7 @@ func (a *namespaceStatusHandler) sync(key string, obj *v1.Namespace) (*v1.Namesp
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
origStatus := obj.Status
|
origStatus := obj.Status.DeepCopy()
|
||||||
obj = obj.DeepCopy()
|
obj = obj.DeepCopy()
|
||||||
newStatus, err := a.handler(obj, obj.Status)
|
newStatus, err := a.handler(obj, obj.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -289,16 +291,16 @@ func (a *namespaceStatusHandler) sync(key string, obj *v1.Namespace) (*v1.Namesp
|
||||||
newStatus = *origStatus.DeepCopy()
|
newStatus = *origStatus.DeepCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.Status = newStatus
|
|
||||||
if a.condition != "" {
|
if a.condition != "" {
|
||||||
if errors.IsConflict(err) {
|
if errors.IsConflict(err) {
|
||||||
a.condition.SetError(obj, "", nil)
|
a.condition.SetError(&newStatus, "", nil)
|
||||||
} else {
|
} else {
|
||||||
a.condition.SetError(obj, "", err)
|
a.condition.SetError(&newStatus, "", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !equality.Semantic.DeepEqual(origStatus, obj.Status) {
|
if !equality.Semantic.DeepEqual(origStatus, &newStatus) {
|
||||||
var newErr error
|
var newErr error
|
||||||
|
obj.Status = newStatus
|
||||||
obj, newErr = a.client.UpdateStatus(obj)
|
obj, newErr = a.client.UpdateStatus(obj)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = newErr
|
err = newErr
|
||||||
|
@ -315,29 +317,28 @@ type namespaceGeneratingHandler struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *namespaceGeneratingHandler) Remove(key string, obj *v1.Namespace) (*v1.Namespace, error) {
|
||||||
|
if obj != nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = &v1.Namespace{}
|
||||||
|
obj.Namespace, obj.Name = kv.RSplit(key, "/")
|
||||||
|
obj.SetGroupVersionKind(a.gvk)
|
||||||
|
|
||||||
|
return nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
WithOwner(obj).
|
||||||
|
WithSetID(a.name).
|
||||||
|
ApplyObjects()
|
||||||
|
}
|
||||||
|
|
||||||
func (a *namespaceGeneratingHandler) Handle(obj *v1.Namespace, status v1.NamespaceStatus) (v1.NamespaceStatus, error) {
|
func (a *namespaceGeneratingHandler) Handle(obj *v1.Namespace, status v1.NamespaceStatus) (v1.NamespaceStatus, error) {
|
||||||
objs, newStatus, err := a.NamespaceGeneratingHandler(obj, status)
|
objs, newStatus, err := a.NamespaceGeneratingHandler(obj, status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newStatus, err
|
return newStatus, err
|
||||||
}
|
}
|
||||||
|
|
||||||
apply := a.apply
|
return newStatus, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
|
||||||
if !a.opts.DynamicLookup {
|
|
||||||
apply = apply.WithStrictCaching()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowCrossNamespace && !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithSetOwnerReference(true, false).
|
|
||||||
WithDefaultNamespace(obj.GetNamespace()).
|
|
||||||
WithListerNamespace(obj.GetNamespace())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithRestrictClusterScoped()
|
|
||||||
}
|
|
||||||
|
|
||||||
return newStatus, apply.
|
|
||||||
WithOwner(obj).
|
WithOwner(obj).
|
||||||
WithSetID(a.name).
|
WithSetID(a.name).
|
||||||
ApplyObjects(objs...)
|
ApplyObjects(objs...)
|
||||||
|
|
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1/node.go
generated
vendored
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1/node.go
generated
vendored
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/rancher/wrangler/pkg/apply"
|
"github.com/rancher/wrangler/pkg/apply"
|
||||||
"github.com/rancher/wrangler/pkg/condition"
|
"github.com/rancher/wrangler/pkg/condition"
|
||||||
"github.com/rancher/wrangler/pkg/generic"
|
"github.com/rancher/wrangler/pkg/generic"
|
||||||
|
"github.com/rancher/wrangler/pkg/kv"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
@ -267,6 +268,7 @@ func RegisterNodeGeneratingHandler(ctx context.Context, controller NodeControlle
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
statusHandler.opts = *opts
|
statusHandler.opts = *opts
|
||||||
}
|
}
|
||||||
|
controller.OnChange(ctx, name, statusHandler.Remove)
|
||||||
RegisterNodeStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
RegisterNodeStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +283,7 @@ func (a *nodeStatusHandler) sync(key string, obj *v1.Node) (*v1.Node, error) {
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
origStatus := obj.Status
|
origStatus := obj.Status.DeepCopy()
|
||||||
obj = obj.DeepCopy()
|
obj = obj.DeepCopy()
|
||||||
newStatus, err := a.handler(obj, obj.Status)
|
newStatus, err := a.handler(obj, obj.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -289,16 +291,16 @@ func (a *nodeStatusHandler) sync(key string, obj *v1.Node) (*v1.Node, error) {
|
||||||
newStatus = *origStatus.DeepCopy()
|
newStatus = *origStatus.DeepCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.Status = newStatus
|
|
||||||
if a.condition != "" {
|
if a.condition != "" {
|
||||||
if errors.IsConflict(err) {
|
if errors.IsConflict(err) {
|
||||||
a.condition.SetError(obj, "", nil)
|
a.condition.SetError(&newStatus, "", nil)
|
||||||
} else {
|
} else {
|
||||||
a.condition.SetError(obj, "", err)
|
a.condition.SetError(&newStatus, "", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !equality.Semantic.DeepEqual(origStatus, obj.Status) {
|
if !equality.Semantic.DeepEqual(origStatus, &newStatus) {
|
||||||
var newErr error
|
var newErr error
|
||||||
|
obj.Status = newStatus
|
||||||
obj, newErr = a.client.UpdateStatus(obj)
|
obj, newErr = a.client.UpdateStatus(obj)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = newErr
|
err = newErr
|
||||||
|
@ -315,29 +317,28 @@ type nodeGeneratingHandler struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *nodeGeneratingHandler) Remove(key string, obj *v1.Node) (*v1.Node, error) {
|
||||||
|
if obj != nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = &v1.Node{}
|
||||||
|
obj.Namespace, obj.Name = kv.RSplit(key, "/")
|
||||||
|
obj.SetGroupVersionKind(a.gvk)
|
||||||
|
|
||||||
|
return nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
WithOwner(obj).
|
||||||
|
WithSetID(a.name).
|
||||||
|
ApplyObjects()
|
||||||
|
}
|
||||||
|
|
||||||
func (a *nodeGeneratingHandler) Handle(obj *v1.Node, status v1.NodeStatus) (v1.NodeStatus, error) {
|
func (a *nodeGeneratingHandler) Handle(obj *v1.Node, status v1.NodeStatus) (v1.NodeStatus, error) {
|
||||||
objs, newStatus, err := a.NodeGeneratingHandler(obj, status)
|
objs, newStatus, err := a.NodeGeneratingHandler(obj, status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newStatus, err
|
return newStatus, err
|
||||||
}
|
}
|
||||||
|
|
||||||
apply := a.apply
|
return newStatus, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
|
||||||
if !a.opts.DynamicLookup {
|
|
||||||
apply = apply.WithStrictCaching()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowCrossNamespace && !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithSetOwnerReference(true, false).
|
|
||||||
WithDefaultNamespace(obj.GetNamespace()).
|
|
||||||
WithListerNamespace(obj.GetNamespace())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithRestrictClusterScoped()
|
|
||||||
}
|
|
||||||
|
|
||||||
return newStatus, apply.
|
|
||||||
WithOwner(obj).
|
WithOwner(obj).
|
||||||
WithSetID(a.name).
|
WithSetID(a.name).
|
||||||
ApplyObjects(objs...)
|
ApplyObjects(objs...)
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/rancher/wrangler/pkg/apply"
|
"github.com/rancher/wrangler/pkg/apply"
|
||||||
"github.com/rancher/wrangler/pkg/condition"
|
"github.com/rancher/wrangler/pkg/condition"
|
||||||
"github.com/rancher/wrangler/pkg/generic"
|
"github.com/rancher/wrangler/pkg/generic"
|
||||||
|
"github.com/rancher/wrangler/pkg/kv"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
@ -267,6 +268,7 @@ func RegisterPersistentVolumeClaimGeneratingHandler(ctx context.Context, control
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
statusHandler.opts = *opts
|
statusHandler.opts = *opts
|
||||||
}
|
}
|
||||||
|
controller.OnChange(ctx, name, statusHandler.Remove)
|
||||||
RegisterPersistentVolumeClaimStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
RegisterPersistentVolumeClaimStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +283,7 @@ func (a *persistentVolumeClaimStatusHandler) sync(key string, obj *v1.Persistent
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
origStatus := obj.Status
|
origStatus := obj.Status.DeepCopy()
|
||||||
obj = obj.DeepCopy()
|
obj = obj.DeepCopy()
|
||||||
newStatus, err := a.handler(obj, obj.Status)
|
newStatus, err := a.handler(obj, obj.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -289,16 +291,16 @@ func (a *persistentVolumeClaimStatusHandler) sync(key string, obj *v1.Persistent
|
||||||
newStatus = *origStatus.DeepCopy()
|
newStatus = *origStatus.DeepCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.Status = newStatus
|
|
||||||
if a.condition != "" {
|
if a.condition != "" {
|
||||||
if errors.IsConflict(err) {
|
if errors.IsConflict(err) {
|
||||||
a.condition.SetError(obj, "", nil)
|
a.condition.SetError(&newStatus, "", nil)
|
||||||
} else {
|
} else {
|
||||||
a.condition.SetError(obj, "", err)
|
a.condition.SetError(&newStatus, "", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !equality.Semantic.DeepEqual(origStatus, obj.Status) {
|
if !equality.Semantic.DeepEqual(origStatus, &newStatus) {
|
||||||
var newErr error
|
var newErr error
|
||||||
|
obj.Status = newStatus
|
||||||
obj, newErr = a.client.UpdateStatus(obj)
|
obj, newErr = a.client.UpdateStatus(obj)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = newErr
|
err = newErr
|
||||||
|
@ -315,29 +317,28 @@ type persistentVolumeClaimGeneratingHandler struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *persistentVolumeClaimGeneratingHandler) Remove(key string, obj *v1.PersistentVolumeClaim) (*v1.PersistentVolumeClaim, error) {
|
||||||
|
if obj != nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = &v1.PersistentVolumeClaim{}
|
||||||
|
obj.Namespace, obj.Name = kv.RSplit(key, "/")
|
||||||
|
obj.SetGroupVersionKind(a.gvk)
|
||||||
|
|
||||||
|
return nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
WithOwner(obj).
|
||||||
|
WithSetID(a.name).
|
||||||
|
ApplyObjects()
|
||||||
|
}
|
||||||
|
|
||||||
func (a *persistentVolumeClaimGeneratingHandler) Handle(obj *v1.PersistentVolumeClaim, status v1.PersistentVolumeClaimStatus) (v1.PersistentVolumeClaimStatus, error) {
|
func (a *persistentVolumeClaimGeneratingHandler) Handle(obj *v1.PersistentVolumeClaim, status v1.PersistentVolumeClaimStatus) (v1.PersistentVolumeClaimStatus, error) {
|
||||||
objs, newStatus, err := a.PersistentVolumeClaimGeneratingHandler(obj, status)
|
objs, newStatus, err := a.PersistentVolumeClaimGeneratingHandler(obj, status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newStatus, err
|
return newStatus, err
|
||||||
}
|
}
|
||||||
|
|
||||||
apply := a.apply
|
return newStatus, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
|
||||||
if !a.opts.DynamicLookup {
|
|
||||||
apply = apply.WithStrictCaching()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowCrossNamespace && !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithSetOwnerReference(true, false).
|
|
||||||
WithDefaultNamespace(obj.GetNamespace()).
|
|
||||||
WithListerNamespace(obj.GetNamespace())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithRestrictClusterScoped()
|
|
||||||
}
|
|
||||||
|
|
||||||
return newStatus, apply.
|
|
||||||
WithOwner(obj).
|
WithOwner(obj).
|
||||||
WithSetID(a.name).
|
WithSetID(a.name).
|
||||||
ApplyObjects(objs...)
|
ApplyObjects(objs...)
|
||||||
|
|
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1/pod.go
generated
vendored
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1/pod.go
generated
vendored
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/rancher/wrangler/pkg/apply"
|
"github.com/rancher/wrangler/pkg/apply"
|
||||||
"github.com/rancher/wrangler/pkg/condition"
|
"github.com/rancher/wrangler/pkg/condition"
|
||||||
"github.com/rancher/wrangler/pkg/generic"
|
"github.com/rancher/wrangler/pkg/generic"
|
||||||
|
"github.com/rancher/wrangler/pkg/kv"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
@ -267,6 +268,7 @@ func RegisterPodGeneratingHandler(ctx context.Context, controller PodController,
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
statusHandler.opts = *opts
|
statusHandler.opts = *opts
|
||||||
}
|
}
|
||||||
|
controller.OnChange(ctx, name, statusHandler.Remove)
|
||||||
RegisterPodStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
RegisterPodStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +283,7 @@ func (a *podStatusHandler) sync(key string, obj *v1.Pod) (*v1.Pod, error) {
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
origStatus := obj.Status
|
origStatus := obj.Status.DeepCopy()
|
||||||
obj = obj.DeepCopy()
|
obj = obj.DeepCopy()
|
||||||
newStatus, err := a.handler(obj, obj.Status)
|
newStatus, err := a.handler(obj, obj.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -289,16 +291,16 @@ func (a *podStatusHandler) sync(key string, obj *v1.Pod) (*v1.Pod, error) {
|
||||||
newStatus = *origStatus.DeepCopy()
|
newStatus = *origStatus.DeepCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.Status = newStatus
|
|
||||||
if a.condition != "" {
|
if a.condition != "" {
|
||||||
if errors.IsConflict(err) {
|
if errors.IsConflict(err) {
|
||||||
a.condition.SetError(obj, "", nil)
|
a.condition.SetError(&newStatus, "", nil)
|
||||||
} else {
|
} else {
|
||||||
a.condition.SetError(obj, "", err)
|
a.condition.SetError(&newStatus, "", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !equality.Semantic.DeepEqual(origStatus, obj.Status) {
|
if !equality.Semantic.DeepEqual(origStatus, &newStatus) {
|
||||||
var newErr error
|
var newErr error
|
||||||
|
obj.Status = newStatus
|
||||||
obj, newErr = a.client.UpdateStatus(obj)
|
obj, newErr = a.client.UpdateStatus(obj)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = newErr
|
err = newErr
|
||||||
|
@ -315,29 +317,28 @@ type podGeneratingHandler struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *podGeneratingHandler) Remove(key string, obj *v1.Pod) (*v1.Pod, error) {
|
||||||
|
if obj != nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = &v1.Pod{}
|
||||||
|
obj.Namespace, obj.Name = kv.RSplit(key, "/")
|
||||||
|
obj.SetGroupVersionKind(a.gvk)
|
||||||
|
|
||||||
|
return nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
WithOwner(obj).
|
||||||
|
WithSetID(a.name).
|
||||||
|
ApplyObjects()
|
||||||
|
}
|
||||||
|
|
||||||
func (a *podGeneratingHandler) Handle(obj *v1.Pod, status v1.PodStatus) (v1.PodStatus, error) {
|
func (a *podGeneratingHandler) Handle(obj *v1.Pod, status v1.PodStatus) (v1.PodStatus, error) {
|
||||||
objs, newStatus, err := a.PodGeneratingHandler(obj, status)
|
objs, newStatus, err := a.PodGeneratingHandler(obj, status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newStatus, err
|
return newStatus, err
|
||||||
}
|
}
|
||||||
|
|
||||||
apply := a.apply
|
return newStatus, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
|
||||||
if !a.opts.DynamicLookup {
|
|
||||||
apply = apply.WithStrictCaching()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowCrossNamespace && !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithSetOwnerReference(true, false).
|
|
||||||
WithDefaultNamespace(obj.GetNamespace()).
|
|
||||||
WithListerNamespace(obj.GetNamespace())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithRestrictClusterScoped()
|
|
||||||
}
|
|
||||||
|
|
||||||
return newStatus, apply.
|
|
||||||
WithOwner(obj).
|
WithOwner(obj).
|
||||||
WithSetID(a.name).
|
WithSetID(a.name).
|
||||||
ApplyObjects(objs...)
|
ApplyObjects(objs...)
|
||||||
|
|
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1/service.go
generated
vendored
45
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1/service.go
generated
vendored
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/rancher/wrangler/pkg/apply"
|
"github.com/rancher/wrangler/pkg/apply"
|
||||||
"github.com/rancher/wrangler/pkg/condition"
|
"github.com/rancher/wrangler/pkg/condition"
|
||||||
"github.com/rancher/wrangler/pkg/generic"
|
"github.com/rancher/wrangler/pkg/generic"
|
||||||
|
"github.com/rancher/wrangler/pkg/kv"
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
@ -267,6 +268,7 @@ func RegisterServiceGeneratingHandler(ctx context.Context, controller ServiceCon
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
statusHandler.opts = *opts
|
statusHandler.opts = *opts
|
||||||
}
|
}
|
||||||
|
controller.OnChange(ctx, name, statusHandler.Remove)
|
||||||
RegisterServiceStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
RegisterServiceStatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +283,7 @@ func (a *serviceStatusHandler) sync(key string, obj *v1.Service) (*v1.Service, e
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
origStatus := obj.Status
|
origStatus := obj.Status.DeepCopy()
|
||||||
obj = obj.DeepCopy()
|
obj = obj.DeepCopy()
|
||||||
newStatus, err := a.handler(obj, obj.Status)
|
newStatus, err := a.handler(obj, obj.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -289,16 +291,16 @@ func (a *serviceStatusHandler) sync(key string, obj *v1.Service) (*v1.Service, e
|
||||||
newStatus = *origStatus.DeepCopy()
|
newStatus = *origStatus.DeepCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.Status = newStatus
|
|
||||||
if a.condition != "" {
|
if a.condition != "" {
|
||||||
if errors.IsConflict(err) {
|
if errors.IsConflict(err) {
|
||||||
a.condition.SetError(obj, "", nil)
|
a.condition.SetError(&newStatus, "", nil)
|
||||||
} else {
|
} else {
|
||||||
a.condition.SetError(obj, "", err)
|
a.condition.SetError(&newStatus, "", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !equality.Semantic.DeepEqual(origStatus, obj.Status) {
|
if !equality.Semantic.DeepEqual(origStatus, &newStatus) {
|
||||||
var newErr error
|
var newErr error
|
||||||
|
obj.Status = newStatus
|
||||||
obj, newErr = a.client.UpdateStatus(obj)
|
obj, newErr = a.client.UpdateStatus(obj)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = newErr
|
err = newErr
|
||||||
|
@ -315,29 +317,28 @@ type serviceGeneratingHandler struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *serviceGeneratingHandler) Remove(key string, obj *v1.Service) (*v1.Service, error) {
|
||||||
|
if obj != nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = &v1.Service{}
|
||||||
|
obj.Namespace, obj.Name = kv.RSplit(key, "/")
|
||||||
|
obj.SetGroupVersionKind(a.gvk)
|
||||||
|
|
||||||
|
return nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
WithOwner(obj).
|
||||||
|
WithSetID(a.name).
|
||||||
|
ApplyObjects()
|
||||||
|
}
|
||||||
|
|
||||||
func (a *serviceGeneratingHandler) Handle(obj *v1.Service, status v1.ServiceStatus) (v1.ServiceStatus, error) {
|
func (a *serviceGeneratingHandler) Handle(obj *v1.Service, status v1.ServiceStatus) (v1.ServiceStatus, error) {
|
||||||
objs, newStatus, err := a.ServiceGeneratingHandler(obj, status)
|
objs, newStatus, err := a.ServiceGeneratingHandler(obj, status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newStatus, err
|
return newStatus, err
|
||||||
}
|
}
|
||||||
|
|
||||||
apply := a.apply
|
return newStatus, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
|
||||||
if !a.opts.DynamicLookup {
|
|
||||||
apply = apply.WithStrictCaching()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowCrossNamespace && !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithSetOwnerReference(true, false).
|
|
||||||
WithDefaultNamespace(obj.GetNamespace()).
|
|
||||||
WithListerNamespace(obj.GetNamespace())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithRestrictClusterScoped()
|
|
||||||
}
|
|
||||||
|
|
||||||
return newStatus, apply.
|
|
||||||
WithOwner(obj).
|
WithOwner(obj).
|
||||||
WithSetID(a.name).
|
WithSetID(a.name).
|
||||||
ApplyObjects(objs...)
|
ApplyObjects(objs...)
|
||||||
|
|
35
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/rbac/factory.go
generated
vendored
35
vendor/github.com/rancher/wrangler-api/pkg/generated/controllers/rbac/factory.go
generated
vendored
|
@ -52,18 +52,23 @@ func NewFactoryFromConfigOrDie(config *rest.Config) *Factory {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactoryFromConfig(config *rest.Config) (*Factory, error) {
|
func NewFactoryFromConfig(config *rest.Config) (*Factory, error) {
|
||||||
cs, err := clientset.NewForConfig(config)
|
return NewFactoryFromConfigWithOptions(config, nil)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
informerFactory := informers.NewSharedInformerFactory(cs, 2*time.Hour)
|
|
||||||
return NewFactory(cs, informerFactory), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {
|
func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {
|
||||||
if namespace == "" {
|
return NewFactoryFromConfigWithOptions(config, &FactoryOptions{
|
||||||
return NewFactoryFromConfig(config)
|
Namespace: namespace,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type FactoryOptions struct {
|
||||||
|
Namespace string
|
||||||
|
Resync time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &FactoryOptions{}
|
||||||
}
|
}
|
||||||
|
|
||||||
cs, err := clientset.NewForConfig(config)
|
cs, err := clientset.NewForConfig(config)
|
||||||
|
@ -71,7 +76,17 @@ func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
informerFactory := informers.NewSharedInformerFactoryWithOptions(cs, 2*time.Hour, informers.WithNamespace(namespace))
|
resync := opts.Resync
|
||||||
|
if resync == 0 {
|
||||||
|
resync = 2 * time.Hour
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Namespace == "" {
|
||||||
|
informerFactory := informers.NewSharedInformerFactory(cs, resync)
|
||||||
|
return NewFactory(cs, informerFactory), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
informerFactory := informers.NewSharedInformerFactoryWithOptions(cs, resync, informers.WithNamespace(opts.Namespace))
|
||||||
return NewFactory(cs, informerFactory), nil
|
return NewFactory(cs, informerFactory), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,18 +28,45 @@ type Reconciler func(oldObj runtime.Object, newObj runtime.Object) (bool, error)
|
||||||
|
|
||||||
type ClientFactory func(gvr schema.GroupVersionResource) (dynamic.NamespaceableResourceInterface, error)
|
type ClientFactory func(gvr schema.GroupVersionResource) (dynamic.NamespaceableResourceInterface, error)
|
||||||
|
|
||||||
|
type InformerFactory interface {
|
||||||
|
Get(gvk schema.GroupVersionKind, gvr schema.GroupVersionResource) (cache.SharedIndexInformer, error)
|
||||||
|
}
|
||||||
|
|
||||||
type InformerGetter interface {
|
type InformerGetter interface {
|
||||||
Informer() cache.SharedIndexInformer
|
Informer() cache.SharedIndexInformer
|
||||||
GroupVersionKind() schema.GroupVersionKind
|
GroupVersionKind() schema.GroupVersionKind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PatchByGVK map[schema.GroupVersionKind]map[objectset.ObjectKey]string
|
||||||
|
|
||||||
|
func (p PatchByGVK) Add(gvk schema.GroupVersionKind, namespace, name, patch string) {
|
||||||
|
d, ok := p[gvk]
|
||||||
|
if !ok {
|
||||||
|
d = map[objectset.ObjectKey]string{}
|
||||||
|
p[gvk] = d
|
||||||
|
}
|
||||||
|
d[objectset.ObjectKey{
|
||||||
|
Name: name,
|
||||||
|
Namespace: namespace,
|
||||||
|
}] = patch
|
||||||
|
}
|
||||||
|
|
||||||
|
type Plan struct {
|
||||||
|
Create objectset.ObjectKeyByGVK
|
||||||
|
Delete objectset.ObjectKeyByGVK
|
||||||
|
Update PatchByGVK
|
||||||
|
Objects []runtime.Object
|
||||||
|
}
|
||||||
|
|
||||||
type Apply interface {
|
type Apply interface {
|
||||||
Apply(set *objectset.ObjectSet) error
|
Apply(set *objectset.ObjectSet) error
|
||||||
ApplyObjects(objs ...runtime.Object) error
|
ApplyObjects(objs ...runtime.Object) error
|
||||||
WithContext(ctx context.Context) Apply
|
WithContext(ctx context.Context) Apply
|
||||||
WithCacheTypes(igs ...InformerGetter) Apply
|
WithCacheTypes(igs ...InformerGetter) Apply
|
||||||
|
WithCacheTypeFactory(factory InformerFactory) Apply
|
||||||
WithSetID(id string) Apply
|
WithSetID(id string) Apply
|
||||||
WithOwner(obj runtime.Object) Apply
|
WithOwner(obj runtime.Object) Apply
|
||||||
|
WithOwnerKey(key string, gvk schema.GroupVersionKind) Apply
|
||||||
WithInjector(injs ...injectors.ConfigInjector) Apply
|
WithInjector(injs ...injectors.ConfigInjector) Apply
|
||||||
WithInjectorName(injs ...string) Apply
|
WithInjectorName(injs ...string) Apply
|
||||||
WithPatcher(gvk schema.GroupVersionKind, patchers Patcher) Apply
|
WithPatcher(gvk schema.GroupVersionKind, patchers Patcher) Apply
|
||||||
|
@ -53,6 +80,10 @@ type Apply interface {
|
||||||
WithNoDelete() Apply
|
WithNoDelete() Apply
|
||||||
WithGVK(gvks ...schema.GroupVersionKind) Apply
|
WithGVK(gvks ...schema.GroupVersionKind) Apply
|
||||||
WithSetOwnerReference(controller, block bool) Apply
|
WithSetOwnerReference(controller, block bool) Apply
|
||||||
|
|
||||||
|
FindOwner(obj runtime.Object) (runtime.Object, error)
|
||||||
|
PurgeOrphan(obj runtime.Object) error
|
||||||
|
DryRun(objs ...runtime.Object) (Plan, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewForConfig(cfg *rest.Config) (Apply, error) {
|
func NewForConfig(cfg *rest.Config) (Apply, error) {
|
||||||
|
@ -70,6 +101,7 @@ func New(discovery discovery.DiscoveryInterface, cf ClientFactory, igs ...Inform
|
||||||
clientFactory: cf,
|
clientFactory: cf,
|
||||||
discovery: discovery,
|
discovery: discovery,
|
||||||
namespaced: map[schema.GroupVersionKind]bool{},
|
namespaced: map[schema.GroupVersionKind]bool{},
|
||||||
|
gvkToGVR: map[schema.GroupVersionKind]schema.GroupVersionResource{},
|
||||||
clients: map[schema.GroupVersionKind]dynamic.NamespaceableResourceInterface{},
|
clients: map[schema.GroupVersionKind]dynamic.NamespaceableResourceInterface{},
|
||||||
},
|
},
|
||||||
informers: map[schema.GroupVersionKind]cache.SharedIndexInformer{},
|
informers: map[schema.GroupVersionKind]cache.SharedIndexInformer{},
|
||||||
|
@ -93,6 +125,7 @@ type clients struct {
|
||||||
clientFactory ClientFactory
|
clientFactory ClientFactory
|
||||||
discovery discovery.DiscoveryInterface
|
discovery discovery.DiscoveryInterface
|
||||||
namespaced map[schema.GroupVersionKind]bool
|
namespaced map[schema.GroupVersionKind]bool
|
||||||
|
gvkToGVR map[schema.GroupVersionKind]schema.GroupVersionResource
|
||||||
clients map[schema.GroupVersionKind]dynamic.NamespaceableResourceInterface
|
clients map[schema.GroupVersionKind]dynamic.NamespaceableResourceInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +135,12 @@ func (c *clients) IsNamespaced(gvk schema.GroupVersionKind) bool {
|
||||||
return c.namespaced[gvk]
|
return c.namespaced[gvk]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *clients) gvr(gvk schema.GroupVersionKind) schema.GroupVersionResource {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
return c.gvkToGVR[gvk]
|
||||||
|
}
|
||||||
|
|
||||||
func (c *clients) client(gvk schema.GroupVersionKind) (dynamic.NamespaceableResourceInterface, error) {
|
func (c *clients) client(gvk schema.GroupVersionKind) (dynamic.NamespaceableResourceInterface, error) {
|
||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
@ -127,6 +166,7 @@ func (c *clients) client(gvk schema.GroupVersionKind) (dynamic.NamespaceableReso
|
||||||
|
|
||||||
c.namespaced[gvk] = resource.Namespaced
|
c.namespaced[gvk] = resource.Namespaced
|
||||||
c.clients[gvk] = client
|
c.clients[gvk] = client
|
||||||
|
c.gvkToGVR[gvk] = gvk.GroupVersion().WithResource(resource.Name)
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,6 +184,10 @@ func (a *apply) newDesiredSet() desiredSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *apply) DryRun(objs ...runtime.Object) (Plan, error) {
|
||||||
|
return a.newDesiredSet().DryRun(objs...)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *apply) Apply(set *objectset.ObjectSet) error {
|
func (a *apply) Apply(set *objectset.ObjectSet) error {
|
||||||
return a.newDesiredSet().Apply(set)
|
return a.newDesiredSet().Apply(set)
|
||||||
}
|
}
|
||||||
|
@ -162,6 +206,10 @@ func (a *apply) WithOwner(obj runtime.Object) Apply {
|
||||||
return a.newDesiredSet().WithOwner(obj)
|
return a.newDesiredSet().WithOwner(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *apply) WithOwnerKey(key string, gvk schema.GroupVersionKind) Apply {
|
||||||
|
return a.newDesiredSet().WithOwnerKey(key, gvk)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *apply) WithInjector(injs ...injectors.ConfigInjector) Apply {
|
func (a *apply) WithInjector(injs ...injectors.ConfigInjector) Apply {
|
||||||
return a.newDesiredSet().WithInjector(injs...)
|
return a.newDesiredSet().WithInjector(injs...)
|
||||||
}
|
}
|
||||||
|
@ -174,6 +222,10 @@ func (a *apply) WithCacheTypes(igs ...InformerGetter) Apply {
|
||||||
return a.newDesiredSet().WithCacheTypes(igs...)
|
return a.newDesiredSet().WithCacheTypes(igs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *apply) WithCacheTypeFactory(factory InformerFactory) Apply {
|
||||||
|
return a.newDesiredSet().WithCacheTypeFactory(factory)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *apply) WithGVK(gvks ...schema.GroupVersionKind) Apply {
|
func (a *apply) WithGVK(gvks ...schema.GroupVersionKind) Apply {
|
||||||
return a.newDesiredSet().WithGVK(gvks...)
|
return a.newDesiredSet().WithGVK(gvks...)
|
||||||
}
|
}
|
||||||
|
@ -221,3 +273,11 @@ func (a *apply) WithSetOwnerReference(controller, block bool) Apply {
|
||||||
func (a *apply) WithContext(ctx context.Context) Apply {
|
func (a *apply) WithContext(ctx context.Context) Apply {
|
||||||
return a.newDesiredSet().WithContext(ctx)
|
return a.newDesiredSet().WithContext(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *apply) FindOwner(obj runtime.Object) (runtime.Object, error) {
|
||||||
|
return a.newDesiredSet().FindOwner(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *apply) PurgeOrphan(obj runtime.Object) error {
|
||||||
|
return a.newDesiredSet().PurgeOrphan(obj)
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,10 @@ package apply
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/rancher/wrangler/pkg/apply/injectors"
|
"github.com/rancher/wrangler/pkg/apply/injectors"
|
||||||
|
"github.com/rancher/wrangler/pkg/kv"
|
||||||
"github.com/rancher/wrangler/pkg/merr"
|
"github.com/rancher/wrangler/pkg/merr"
|
||||||
"github.com/rancher/wrangler/pkg/objectset"
|
"github.com/rancher/wrangler/pkg/objectset"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
|
@ -12,7 +14,7 @@ import (
|
||||||
|
|
||||||
type desiredSet struct {
|
type desiredSet struct {
|
||||||
a *apply
|
a *apply
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
defaultNamespace string
|
defaultNamespace string
|
||||||
listerNamespace string
|
listerNamespace string
|
||||||
setOwnerReference bool
|
setOwnerReference bool
|
||||||
|
@ -23,6 +25,7 @@ type desiredSet struct {
|
||||||
pruneTypes map[schema.GroupVersionKind]cache.SharedIndexInformer
|
pruneTypes map[schema.GroupVersionKind]cache.SharedIndexInformer
|
||||||
patchers map[schema.GroupVersionKind]Patcher
|
patchers map[schema.GroupVersionKind]Patcher
|
||||||
reconcilers map[schema.GroupVersionKind]Reconciler
|
reconcilers map[schema.GroupVersionKind]Reconciler
|
||||||
|
informerFactory InformerFactory
|
||||||
remove bool
|
remove bool
|
||||||
noDelete bool
|
noDelete bool
|
||||||
setID string
|
setID string
|
||||||
|
@ -33,6 +36,9 @@ type desiredSet struct {
|
||||||
ratelimitingQps float32
|
ratelimitingQps float32
|
||||||
injectorNames []string
|
injectorNames []string
|
||||||
errs []error
|
errs []error
|
||||||
|
|
||||||
|
createPlan bool
|
||||||
|
plan Plan
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *desiredSet) err(err error) error {
|
func (o *desiredSet) err(err error) error {
|
||||||
|
@ -44,6 +50,12 @@ func (o desiredSet) Err() error {
|
||||||
return merr.NewErrors(append(o.errs, o.objs.Err())...)
|
return merr.NewErrors(append(o.errs, o.objs.Err())...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o desiredSet) DryRun(objs ...runtime.Object) (Plan, error) {
|
||||||
|
o.objs = objectset.NewObjectSet()
|
||||||
|
o.objs.Add(objs...)
|
||||||
|
return o.dryRun()
|
||||||
|
}
|
||||||
|
|
||||||
func (o desiredSet) Apply(set *objectset.ObjectSet) error {
|
func (o desiredSet) Apply(set *objectset.ObjectSet) error {
|
||||||
if set == nil {
|
if set == nil {
|
||||||
set = objectset.NewObjectSet()
|
set = objectset.NewObjectSet()
|
||||||
|
@ -76,6 +88,14 @@ func (o desiredSet) WithSetID(id string) Apply {
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o desiredSet) WithOwnerKey(key string, gvk schema.GroupVersionKind) Apply {
|
||||||
|
obj := &v1.PartialObjectMetadata{}
|
||||||
|
obj.Namespace, obj.Name = kv.RSplit(key, "/")
|
||||||
|
obj.SetGroupVersionKind(gvk)
|
||||||
|
o.owner = obj
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
func (o desiredSet) WithOwner(obj runtime.Object) Apply {
|
func (o desiredSet) WithOwner(obj runtime.Object) Apply {
|
||||||
o.owner = obj
|
o.owner = obj
|
||||||
return o
|
return o
|
||||||
|
@ -98,6 +118,11 @@ func (o desiredSet) WithInjectorName(injs ...string) Apply {
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o desiredSet) WithCacheTypeFactory(factory InformerFactory) Apply {
|
||||||
|
o.informerFactory = factory
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
func (o desiredSet) WithCacheTypes(igs ...InformerGetter) Apply {
|
func (o desiredSet) WithCacheTypes(igs ...InformerGetter) Apply {
|
||||||
pruneTypes := make(map[schema.GroupVersionKind]cache.SharedIndexInformer, len(igs))
|
pruneTypes := make(map[schema.GroupVersionKind]cache.SharedIndexInformer, len(igs))
|
||||||
for k, v := range o.pruneTypes {
|
for k, v := range o.pruneTypes {
|
||||||
|
@ -147,7 +172,11 @@ func (o desiredSet) WithRestrictClusterScoped() Apply {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o desiredSet) WithDefaultNamespace(ns string) Apply {
|
func (o desiredSet) WithDefaultNamespace(ns string) Apply {
|
||||||
o.defaultNamespace = ns
|
if ns == "" {
|
||||||
|
o.defaultNamespace = defaultNamespace
|
||||||
|
} else {
|
||||||
|
o.defaultNamespace = ns
|
||||||
|
}
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ const (
|
||||||
LabelName = "objectset.rio.cattle.io/owner-name"
|
LabelName = "objectset.rio.cattle.io/owner-name"
|
||||||
LabelNamespace = "objectset.rio.cattle.io/owner-namespace"
|
LabelNamespace = "objectset.rio.cattle.io/owner-namespace"
|
||||||
LabelHash = "objectset.rio.cattle.io/hash"
|
LabelHash = "objectset.rio.cattle.io/hash"
|
||||||
|
LabelPrefix = "objectset.rio.cattle.io/"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -58,6 +59,15 @@ func (o *desiredSet) getRateLimit(labelHash string) flowcontrol.RateLimiter {
|
||||||
return rl
|
return rl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *desiredSet) dryRun() (Plan, error) {
|
||||||
|
o.createPlan = true
|
||||||
|
o.plan.Create = objectset.ObjectKeyByGVK{}
|
||||||
|
o.plan.Update = PatchByGVK{}
|
||||||
|
o.plan.Delete = objectset.ObjectKeyByGVK{}
|
||||||
|
err := o.apply()
|
||||||
|
return o.plan, err
|
||||||
|
}
|
||||||
|
|
||||||
func (o *desiredSet) apply() error {
|
func (o *desiredSet) apply() error {
|
||||||
if o.objs == nil || o.objs.Len() == 0 {
|
if o.objs == nil || o.objs.Len() == 0 {
|
||||||
o.remove = true
|
o.remove = true
|
||||||
|
@ -67,7 +77,7 @@ func (o *desiredSet) apply() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
labelSet, annotationSet, err := o.getLabelsAndAnnotations()
|
labelSet, annotationSet, err := GetLabelsAndAnnotations(o.setID, o.owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return o.err(err)
|
return o.err(err)
|
||||||
}
|
}
|
||||||
|
@ -90,13 +100,13 @@ func (o *desiredSet) apply() error {
|
||||||
objs := o.collect(objList)
|
objs := o.collect(objList)
|
||||||
|
|
||||||
debugID := o.debugID()
|
debugID := o.debugID()
|
||||||
req, err := labels.NewRequirement(LabelHash, selection.Equals, []string{labelSet[LabelHash]})
|
sel, err := GetSelector(labelSet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return o.err(err)
|
return o.err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, gvk := range o.objs.GVKOrder(o.knownGVK()...) {
|
for _, gvk := range o.objs.GVKOrder(o.knownGVK()...) {
|
||||||
o.process(debugID, labels.NewSelector().Add(*req), gvk, objs[gvk])
|
o.process(debugID, sel, gvk, objs[gvk])
|
||||||
}
|
}
|
||||||
|
|
||||||
return o.Err()
|
return o.Err()
|
||||||
|
@ -161,18 +171,26 @@ func (o *desiredSet) runInjectors(objList []runtime.Object) ([]runtime.Object, e
|
||||||
return objList, nil
|
return objList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *desiredSet) getLabelsAndAnnotations() (map[string]string, map[string]string, error) {
|
func GetSelector(labelSet map[string]string) (labels.Selector, error) {
|
||||||
|
req, err := labels.NewRequirement(LabelHash, selection.Equals, []string{labelSet[LabelHash]})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return labels.NewSelector().Add(*req), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLabelsAndAnnotations(setID string, owner runtime.Object) (map[string]string, map[string]string, error) {
|
||||||
annotations := map[string]string{
|
annotations := map[string]string{
|
||||||
LabelID: o.setID,
|
LabelID: setID,
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.owner != nil {
|
if owner != nil {
|
||||||
gvk, err := gvk2.Get(o.owner)
|
gvk, err := gvk2.Get(owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
annotations[LabelGVK] = gvk.String()
|
annotations[LabelGVK] = gvk.String()
|
||||||
metadata, err := meta.Accessor(o.owner)
|
metadata, err := meta.Accessor(owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to get metadata for %s", gvk)
|
return nil, nil, fmt.Errorf("failed to get metadata for %s", gvk)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,9 @@ import (
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
data2 "github.com/rancher/wrangler/pkg/data"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rancher/wrangler/pkg/data/convert"
|
"github.com/rancher/wrangler/pkg/data/convert"
|
||||||
|
@ -95,7 +98,7 @@ func emptyMaps(data map[string]interface{}, keys ...string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func sanitizePatch(patch []byte) ([]byte, error) {
|
func sanitizePatch(patch []byte, removeObjectSetAnnotation bool) ([]byte, error) {
|
||||||
mod := false
|
mod := false
|
||||||
data := map[string]interface{}{}
|
data := map[string]interface{}{}
|
||||||
err := json.Unmarshal(patch, &data)
|
err := json.Unmarshal(patch, &data)
|
||||||
|
@ -117,6 +120,23 @@ func sanitizePatch(patch []byte) ([]byte, error) {
|
||||||
mod = true
|
mod = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if removeObjectSetAnnotation {
|
||||||
|
metadata := convert.ToMapInterface(data2.GetValueN(data, "metadata"))
|
||||||
|
annotations := convert.ToMapInterface(data2.GetValueN(data, "metadata", "annotations"))
|
||||||
|
for k := range annotations {
|
||||||
|
if strings.HasPrefix(k, LabelPrefix) {
|
||||||
|
mod = true
|
||||||
|
delete(annotations, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mod && len(annotations) == 0 {
|
||||||
|
delete(metadata, "annotations")
|
||||||
|
if len(metadata) == 0 {
|
||||||
|
delete(data, "metadata")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if emptyMaps(data, "metadata", "annotations") {
|
if emptyMaps(data, "metadata", "annotations") {
|
||||||
return []byte("{}"), nil
|
return []byte("{}"), nil
|
||||||
}
|
}
|
||||||
|
@ -152,7 +172,7 @@ func applyPatch(gvk schema.GroupVersionKind, reconciler Reconciler, patcher Patc
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
patch, err = sanitizePatch(patch)
|
patch, err = sanitizePatch(patch, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -172,6 +192,9 @@ func applyPatch(gvk schema.GroupVersionKind, reconciler Reconciler, patcher Patc
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
if originalObject == nil {
|
||||||
|
originalObject = oldObject
|
||||||
|
}
|
||||||
handled, err := reconciler(originalObject, newObject)
|
handled, err := reconciler(originalObject, newObject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -187,13 +210,17 @@ func applyPatch(gvk schema.GroupVersionKind, reconciler Reconciler, patcher Patc
|
||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *desiredSet) compareObjects(gvk schema.GroupVersionKind, patcher Patcher, client dynamic.NamespaceableResourceInterface, debugID string, oldObject, newObject runtime.Object, force bool) error {
|
func (o *desiredSet) compareObjects(gvk schema.GroupVersionKind, reconciler Reconciler, patcher Patcher, client dynamic.NamespaceableResourceInterface, debugID string, oldObject, newObject runtime.Object, force bool) error {
|
||||||
oldMetadata, err := meta.Accessor(oldObject)
|
oldMetadata, err := meta.Accessor(oldObject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ran, err := applyPatch(gvk, o.reconcilers[gvk], patcher, debugID, oldObject, newObject); err != nil {
|
if o.createPlan {
|
||||||
|
o.plan.Objects = append(o.plan.Objects, oldObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ran, err := applyPatch(gvk, reconciler, patcher, debugID, oldObject, newObject); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if !ran {
|
} else if !ran {
|
||||||
logrus.Debugf("DesiredSet - No change(2) %s %s/%s for %s", gvk, oldMetadata.GetNamespace(), oldMetadata.GetName(), debugID)
|
logrus.Debugf("DesiredSet - No change(2) %s %s/%s for %s", gvk, oldMetadata.GetNamespace(), oldMetadata.GetName(), debugID)
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
package apply
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rancher/wrangler/pkg/gvk"
|
||||||
|
|
||||||
|
"github.com/rancher/wrangler/pkg/kv"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
namer "github.com/rancher/wrangler/pkg/name"
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/client-go/dynamic"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrOwnerNotFound = errors.New("owner not found")
|
||||||
|
)
|
||||||
|
|
||||||
|
func notFound(name string, gvk schema.GroupVersionKind) error {
|
||||||
|
// this is not proper, but does it really matter that much? If you find this
|
||||||
|
// line while researching a bug, then the answer is probably yes.
|
||||||
|
resource := namer.GuessPluralName(strings.ToLower(gvk.Kind))
|
||||||
|
return apierrors.NewNotFound(schema.GroupResource{
|
||||||
|
Group: gvk.Group,
|
||||||
|
Resource: resource,
|
||||||
|
}, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGVK(gvkLabel string, gvk *schema.GroupVersionKind) error {
|
||||||
|
parts := strings.Split(gvkLabel, ", Kind=")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return fmt.Errorf("invalid GVK format: %s", gvkLabel)
|
||||||
|
}
|
||||||
|
gvk.Group, gvk.Version = kv.Split(parts[0], "/")
|
||||||
|
gvk.Kind = parts[1]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o desiredSet) FindOwner(obj runtime.Object) (runtime.Object, error) {
|
||||||
|
if obj == nil {
|
||||||
|
return nil, ErrOwnerNotFound
|
||||||
|
}
|
||||||
|
meta, err := meta.Accessor(obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
debugID = fmt.Sprintf("%s/%s", meta.GetNamespace(), meta.GetName())
|
||||||
|
gvkLabel = meta.GetAnnotations()[LabelGVK]
|
||||||
|
namespace = meta.GetAnnotations()[LabelNamespace]
|
||||||
|
name = meta.GetAnnotations()[LabelName]
|
||||||
|
gvk schema.GroupVersionKind
|
||||||
|
)
|
||||||
|
|
||||||
|
if gvkLabel == "" {
|
||||||
|
return nil, ErrOwnerNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := getGVK(gvkLabel, &gvk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cache, client, err := o.getControllerAndClient(debugID, gvk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cache != nil {
|
||||||
|
return o.fromCache(cache, namespace, name, gvk)
|
||||||
|
}
|
||||||
|
|
||||||
|
return o.fromClient(client, namespace, name, gvk)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *desiredSet) fromClient(client dynamic.NamespaceableResourceInterface, namespace, name string, gvk schema.GroupVersionKind) (runtime.Object, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
obj interface{}
|
||||||
|
)
|
||||||
|
if namespace == "" {
|
||||||
|
obj, err = client.Get(o.ctx, name, metav1.GetOptions{})
|
||||||
|
} else {
|
||||||
|
obj, err = client.Namespace(namespace).Get(o.ctx, name, metav1.GetOptions{})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ro, ok := obj.(runtime.Object); ok {
|
||||||
|
return ro, nil
|
||||||
|
}
|
||||||
|
return nil, notFound(name, gvk)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *desiredSet) fromCache(cache cache.SharedInformer, namespace, name string, gvk schema.GroupVersionKind) (runtime.Object, error) {
|
||||||
|
var key string
|
||||||
|
if namespace == "" {
|
||||||
|
key = name
|
||||||
|
} else {
|
||||||
|
key = namespace + "/" + name
|
||||||
|
}
|
||||||
|
item, ok, err := cache.GetStore().GetByKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !ok {
|
||||||
|
return nil, notFound(name, gvk)
|
||||||
|
} else if ro, ok := item.(runtime.Object); ok {
|
||||||
|
return ro, nil
|
||||||
|
}
|
||||||
|
return nil, notFound(name, gvk)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o desiredSet) PurgeOrphan(obj runtime.Object) error {
|
||||||
|
if obj == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
meta, err := meta.Accessor(obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := o.FindOwner(obj); apierrors.IsNotFound(err) {
|
||||||
|
gvk, err := gvk.Get(obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
o.strictCaching = false
|
||||||
|
_, client, err := o.getControllerAndClient(meta.GetName(), gvk)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if meta.GetNamespace() == "" {
|
||||||
|
return client.Delete(o.ctx, meta.GetName(), metav1.DeleteOptions{})
|
||||||
|
} else {
|
||||||
|
return client.Namespace(meta.GetNamespace()).Delete(o.ctx, meta.GetName(), metav1.DeleteOptions{})
|
||||||
|
}
|
||||||
|
} else if err == ErrOwnerNotFound {
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -28,19 +28,27 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (o *desiredSet) getControllerAndClient(debugID string, gvk schema.GroupVersionKind) (cache.SharedIndexInformer, dynamic.NamespaceableResourceInterface, error) {
|
func (o *desiredSet) getControllerAndClient(debugID string, gvk schema.GroupVersionKind) (cache.SharedIndexInformer, dynamic.NamespaceableResourceInterface, error) {
|
||||||
|
// client needs to be accessed first so that the gvk->gvr mapping gets cached
|
||||||
|
client, err := o.a.clients.client(gvk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
informer, ok := o.pruneTypes[gvk]
|
informer, ok := o.pruneTypes[gvk]
|
||||||
if !ok {
|
if !ok {
|
||||||
informer = o.a.informers[gvk]
|
informer = o.a.informers[gvk]
|
||||||
}
|
}
|
||||||
|
if informer == nil && o.informerFactory != nil {
|
||||||
|
newInformer, err := o.informerFactory.Get(gvk, o.a.clients.gvr(gvk))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrapf(err, "failed to construct informer for %v for %s", gvk, debugID)
|
||||||
|
}
|
||||||
|
informer = newInformer
|
||||||
|
}
|
||||||
if informer == nil && o.strictCaching {
|
if informer == nil && o.strictCaching {
|
||||||
return nil, nil, fmt.Errorf("failed to find informer for %s for %s", gvk, debugID)
|
return nil, nil, fmt.Errorf("failed to find informer for %s for %s", gvk, debugID)
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := o.a.clients.client(gvk)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return informer, client, nil
|
return informer, client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,6 +214,8 @@ func (o *desiredSet) process(debugID string, set labels.Selector, gvk schema.Gro
|
||||||
patcher = o.createPatcher(client)
|
patcher = o.createPatcher(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reconciler := o.reconcilers[gvk]
|
||||||
|
|
||||||
existing, err := o.list(controller, client, set)
|
existing, err := o.list(controller, client, set)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
o.err(errors.Wrapf(err, "failed to list %s for %s", gvk, debugID))
|
o.err(errors.Wrapf(err, "failed to list %s for %s", gvk, debugID))
|
||||||
|
@ -214,6 +224,26 @@ func (o *desiredSet) process(debugID string, set labels.Selector, gvk schema.Gro
|
||||||
|
|
||||||
toCreate, toDelete, toUpdate := compareSets(existing, objs)
|
toCreate, toDelete, toUpdate := compareSets(existing, objs)
|
||||||
|
|
||||||
|
if o.createPlan {
|
||||||
|
o.plan.Create[gvk] = toCreate
|
||||||
|
o.plan.Delete[gvk] = toDelete
|
||||||
|
|
||||||
|
reconciler = nil
|
||||||
|
patcher = func(namespace, name string, pt types2.PatchType, data []byte) (runtime.Object, error) {
|
||||||
|
data, err := sanitizePatch(data, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if string(data) != "{}" {
|
||||||
|
o.plan.Update.Add(gvk, namespace, name, string(data))
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
toCreate = nil
|
||||||
|
toDelete = nil
|
||||||
|
}
|
||||||
|
|
||||||
createF := func(k objectset.ObjectKey) {
|
createF := func(k objectset.ObjectKey) {
|
||||||
obj := objs[k]
|
obj := objs[k]
|
||||||
obj, err := prepareObjectForCreate(gvk, obj)
|
obj, err := prepareObjectForCreate(gvk, obj)
|
||||||
|
@ -248,7 +278,7 @@ func (o *desiredSet) process(debugID string, set labels.Selector, gvk schema.Gro
|
||||||
}
|
}
|
||||||
|
|
||||||
updateF := func(k objectset.ObjectKey) {
|
updateF := func(k objectset.ObjectKey) {
|
||||||
err := o.compareObjects(gvk, patcher, client, debugID, existing[k], objs[k], len(toCreate) > 0 || len(toDelete) > 0)
|
err := o.compareObjects(gvk, reconciler, patcher, client, debugID, existing[k], objs[k], len(toCreate) > 0 || len(toDelete) > 0)
|
||||||
if err == ErrReplace {
|
if err == ErrReplace {
|
||||||
deleteF(k, true)
|
deleteF(k, true)
|
||||||
o.err(fmt.Errorf("DesiredSet - Replace Wait %s %s for %s", gvk, k, debugID))
|
o.err(fmt.Errorf("DesiredSet - Replace Wait %s %s for %s", gvk, k, debugID))
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/rancher/wrangler/pkg/generic"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ func (c Cond) GetStatus(obj interface{}) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Cond) SetError(obj interface{}, reason string, err error) {
|
func (c Cond) SetError(obj interface{}, reason string, err error) {
|
||||||
if err == nil {
|
if err == nil || err == generic.ErrSkip {
|
||||||
c.True(obj)
|
c.True(obj)
|
||||||
c.Message(obj, "")
|
c.Message(obj, "")
|
||||||
c.Reason(obj, reason)
|
c.Reason(obj, reason)
|
||||||
|
@ -153,6 +154,9 @@ func getTS(obj interface{}, condName string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setStatus(obj interface{}, condName, status string) {
|
func setStatus(obj interface{}, condName, status string) {
|
||||||
|
if reflect.TypeOf(obj).Kind() != reflect.Ptr {
|
||||||
|
panic("obj passed must be a pointer")
|
||||||
|
}
|
||||||
cond := findOrCreateCond(obj, condName)
|
cond := findOrCreateCond(obj, condName)
|
||||||
setValue(cond, "Status", status)
|
setValue(cond, "Status", status)
|
||||||
}
|
}
|
||||||
|
@ -167,6 +171,9 @@ func setValue(cond reflect.Value, fieldName, newValue string) {
|
||||||
|
|
||||||
func findOrNotCreateCond(obj interface{}, condName string) *reflect.Value {
|
func findOrNotCreateCond(obj interface{}, condName string) *reflect.Value {
|
||||||
condSlice := getValue(obj, "Status", "Conditions")
|
condSlice := getValue(obj, "Status", "Conditions")
|
||||||
|
if !condSlice.IsValid() {
|
||||||
|
condSlice = getValue(obj, "Conditions")
|
||||||
|
}
|
||||||
return findCond(obj, condSlice, condName)
|
return findCond(obj, condSlice, condName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,6 +231,9 @@ func getValue(obj interface{}, name ...string) reflect.Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFieldValue(v reflect.Value, name ...string) reflect.Value {
|
func getFieldValue(v reflect.Value, name ...string) reflect.Value {
|
||||||
|
if !v.IsValid() {
|
||||||
|
return v
|
||||||
|
}
|
||||||
field := v.FieldByName(name[0])
|
field := v.FieldByName(name[0])
|
||||||
if len(name) == 1 {
|
if len(name) == 1 {
|
||||||
return field
|
return field
|
||||||
|
|
35
vendor/github.com/rancher/wrangler/pkg/controller-gen/generators/factory_go.go
generated
vendored
35
vendor/github.com/rancher/wrangler/pkg/controller-gen/generators/factory_go.go
generated
vendored
|
@ -85,18 +85,23 @@ func NewFactoryFromConfigOrDie(config *rest.Config) *Factory {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactoryFromConfig(config *rest.Config) (*Factory, error) {
|
func NewFactoryFromConfig(config *rest.Config) (*Factory, error) {
|
||||||
cs, err := clientset.NewForConfig(config)
|
return NewFactoryFromConfigWithOptions(config, nil)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
informerFactory := informers.NewSharedInformerFactory(cs, 2*time.Hour)
|
|
||||||
return NewFactory(cs, informerFactory), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {
|
func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*Factory, error) {
|
||||||
if namespace == "" {
|
return NewFactoryFromConfigWithOptions(config, &FactoryOptions{
|
||||||
return NewFactoryFromConfig(config)
|
Namespace: namespace,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type FactoryOptions struct {
|
||||||
|
Namespace string
|
||||||
|
Resync time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFactoryFromConfigWithOptions(config *rest.Config, opts *FactoryOptions) (*Factory, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &FactoryOptions{}
|
||||||
}
|
}
|
||||||
|
|
||||||
cs, err := clientset.NewForConfig(config)
|
cs, err := clientset.NewForConfig(config)
|
||||||
|
@ -104,7 +109,17 @@ func NewFactoryFromConfigWithNamespace(config *rest.Config, namespace string) (*
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
informerFactory := informers.NewSharedInformerFactoryWithOptions(cs, 2*time.Hour, informers.WithNamespace(namespace))
|
resync := opts.Resync
|
||||||
|
if resync == 0 {
|
||||||
|
resync = 2*time.Hour
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Namespace == "" {
|
||||||
|
informerFactory := informers.NewSharedInformerFactory(cs, resync)
|
||||||
|
return NewFactory(cs, informerFactory), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
informerFactory := informers.NewSharedInformerFactoryWithOptions(cs, resync, informers.WithNamespace(opts.Namespace))
|
||||||
return NewFactory(cs, informerFactory), nil
|
return NewFactory(cs, informerFactory), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -333,6 +333,7 @@ func Register{{.type}}GeneratingHandler(ctx context.Context, controller {{.type}
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
statusHandler.opts = *opts
|
statusHandler.opts = *opts
|
||||||
}
|
}
|
||||||
|
controller.OnChange(ctx, name, statusHandler.Remove)
|
||||||
Register{{.type}}StatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
Register{{.type}}StatusHandler(ctx, controller, condition, name, statusHandler.Handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,7 +348,7 @@ func (a *{{.lowerName}}StatusHandler) sync(key string, obj *{{.version}}.{{.type
|
||||||
return obj, nil
|
return obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
origStatus := obj.Status
|
origStatus := obj.Status.DeepCopy()
|
||||||
obj = obj.DeepCopy()
|
obj = obj.DeepCopy()
|
||||||
newStatus, err := a.handler(obj, obj.Status)
|
newStatus, err := a.handler(obj, obj.Status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -355,16 +356,16 @@ func (a *{{.lowerName}}StatusHandler) sync(key string, obj *{{.version}}.{{.type
|
||||||
newStatus = *origStatus.DeepCopy()
|
newStatus = *origStatus.DeepCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.Status = newStatus
|
|
||||||
if a.condition != "" {
|
if a.condition != "" {
|
||||||
if errors.IsConflict(err) {
|
if errors.IsConflict(err) {
|
||||||
a.condition.SetError(obj, "", nil)
|
a.condition.SetError(&newStatus, "", nil)
|
||||||
} else {
|
} else {
|
||||||
a.condition.SetError(obj, "", err)
|
a.condition.SetError(&newStatus, "", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !equality.Semantic.DeepEqual(origStatus, obj.Status) {
|
if !equality.Semantic.DeepEqual(origStatus, &newStatus) {
|
||||||
var newErr error
|
var newErr error
|
||||||
|
obj.Status = newStatus
|
||||||
obj, newErr = a.client.UpdateStatus(obj)
|
obj, newErr = a.client.UpdateStatus(obj)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = newErr
|
err = newErr
|
||||||
|
@ -381,29 +382,28 @@ type {{.lowerName}}GeneratingHandler struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *{{.lowerName}}GeneratingHandler) Remove(key string, obj *{{.version}}.{{.type}}) (*{{.version}}.{{.type}}, error) {
|
||||||
|
if obj != nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = &{{.version}}.{{.type}}{}
|
||||||
|
obj.Namespace, obj.Name = kv.RSplit(key, "/")
|
||||||
|
obj.SetGroupVersionKind(a.gvk)
|
||||||
|
|
||||||
|
return nil, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
WithOwner(obj).
|
||||||
|
WithSetID(a.name).
|
||||||
|
ApplyObjects()
|
||||||
|
}
|
||||||
|
|
||||||
func (a *{{.lowerName}}GeneratingHandler) Handle(obj *{{.version}}.{{.type}}, status {{.version}}.{{.statusType}}) ({{.version}}.{{.statusType}}, error) {
|
func (a *{{.lowerName}}GeneratingHandler) Handle(obj *{{.version}}.{{.type}}, status {{.version}}.{{.statusType}}) ({{.version}}.{{.statusType}}, error) {
|
||||||
objs, newStatus, err := a.{{.type}}GeneratingHandler(obj, status)
|
objs, newStatus, err := a.{{.type}}GeneratingHandler(obj, status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newStatus, err
|
return newStatus, err
|
||||||
}
|
}
|
||||||
|
|
||||||
apply := a.apply
|
return newStatus, generic.ConfigureApplyForObject(a.apply, obj, &a.opts).
|
||||||
|
|
||||||
if !a.opts.DynamicLookup {
|
|
||||||
apply = apply.WithStrictCaching()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowCrossNamespace && !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithSetOwnerReference(true, false).
|
|
||||||
WithDefaultNamespace(obj.GetNamespace()).
|
|
||||||
WithListerNamespace(obj.GetNamespace())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !a.opts.AllowClusterScoped {
|
|
||||||
apply = apply.WithRestrictClusterScoped()
|
|
||||||
}
|
|
||||||
|
|
||||||
return newStatus, apply.
|
|
||||||
WithOwner(obj).
|
WithOwner(obj).
|
||||||
WithSetID(a.name).
|
WithSetID(a.name).
|
||||||
ApplyObjects(objs...)
|
ApplyObjects(objs...)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package crd
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -16,7 +17,7 @@ import (
|
||||||
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
"k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
@ -52,8 +53,107 @@ func (c CRD) WithSchemaFromStruct(obj interface{}) CRD {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c CRD) WithCustomColumn(columns []v1beta1.CustomResourceColumnDefinition) CRD {
|
func (c CRD) WithColumn(name, path string) CRD {
|
||||||
c.Columns = columns
|
c.Columns = append(c.Columns, v1beta1.CustomResourceColumnDefinition{
|
||||||
|
Name: name,
|
||||||
|
Type: "string",
|
||||||
|
Priority: 0,
|
||||||
|
JSONPath: path,
|
||||||
|
})
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func getType(obj interface{}) reflect.Type {
|
||||||
|
if t, ok := obj.(reflect.Type); ok {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
t := reflect.TypeOf(obj)
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CRD) WithColumnsFromStruct(obj interface{}) CRD {
|
||||||
|
c.Columns = append(c.Columns, readCustomColumns(getType(obj), ".")...)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func fieldName(f reflect.StructField) string {
|
||||||
|
jsonTag := f.Tag.Get("json")
|
||||||
|
if jsonTag == "-" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
name := strings.Split(jsonTag, ",")[0]
|
||||||
|
if name == "" {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func tagToColumn(f reflect.StructField) (v1beta1.CustomResourceColumnDefinition, bool) {
|
||||||
|
c := v1beta1.CustomResourceColumnDefinition{
|
||||||
|
Name: f.Name,
|
||||||
|
Type: "string",
|
||||||
|
}
|
||||||
|
|
||||||
|
columnDef, ok := f.Tag.Lookup("column")
|
||||||
|
if !ok {
|
||||||
|
return c, false
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range kv.SplitMap(columnDef, ",") {
|
||||||
|
switch k {
|
||||||
|
case "name":
|
||||||
|
c.Name = v
|
||||||
|
case "type":
|
||||||
|
c.Type = v
|
||||||
|
case "format":
|
||||||
|
c.Format = v
|
||||||
|
case "description":
|
||||||
|
c.Description = v
|
||||||
|
case "priority":
|
||||||
|
p, _ := strconv.Atoi(v)
|
||||||
|
c.Priority = int32(p)
|
||||||
|
case "jsonpath":
|
||||||
|
c.JSONPath = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func readCustomColumns(t reflect.Type, path string) (result []v1beta1.CustomResourceColumnDefinition) {
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
fieldName := fieldName(f)
|
||||||
|
if fieldName == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t := f.Type
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
if t.Kind() == reflect.Struct {
|
||||||
|
if f.Anonymous {
|
||||||
|
result = append(result, readCustomColumns(t, path)...)
|
||||||
|
} else {
|
||||||
|
result = append(result, readCustomColumns(t, path+"."+fieldName)...)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if col, ok := tagToColumn(f); ok {
|
||||||
|
result = append(result, col)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CRD) WithCustomColumn(columns ...v1beta1.CustomResourceColumnDefinition) CRD {
|
||||||
|
c.Columns = append(c.Columns, columns...)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,10 +179,7 @@ func (c CRD) WithShortNames(shortNames ...string) CRD {
|
||||||
|
|
||||||
func (c CRD) ToCustomResourceDefinition() (apiext.CustomResourceDefinition, error) {
|
func (c CRD) ToCustomResourceDefinition() (apiext.CustomResourceDefinition, error) {
|
||||||
if c.SchemaObject != nil && c.GVK.Kind == "" {
|
if c.SchemaObject != nil && c.GVK.Kind == "" {
|
||||||
t := reflect.TypeOf(c.SchemaObject)
|
t := getType(c.SchemaObject)
|
||||||
if t.Kind() == reflect.Ptr {
|
|
||||||
t = t.Elem()
|
|
||||||
}
|
|
||||||
c.GVK.Kind = t.Name()
|
c.GVK.Kind = t.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,6 +347,13 @@ func (f *Factory) CreateCRDs(ctx context.Context, crds ...CRD) (map[schema.Group
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ok, err := f.ensureAccess(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !ok {
|
||||||
|
logrus.Infof("No access to list CRDs, assuming CRDs are pre-created.")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
crdStatus := map[schema.GroupVersionKind]*apiext.CustomResourceDefinition{}
|
crdStatus := map[schema.GroupVersionKind]*apiext.CustomResourceDefinition{}
|
||||||
|
|
||||||
ready, err := f.getReadyCRDs(ctx)
|
ready, err := f.getReadyCRDs(ctx)
|
||||||
|
@ -341,7 +445,7 @@ func (f *Factory) createCRD(ctx context.Context, crdDef CRD, ready map[string]*a
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Infof("Creating CRD %s", crd.Name)
|
logrus.Infof("Creating CRD %s", crd.Name)
|
||||||
if newCrd, err := f.CRDClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(ctx, &crd, metav1.CreateOptions{}); errors.IsAlreadyExists(err) {
|
if newCrd, err := f.CRDClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(ctx, &crd, metav1.CreateOptions{}); apierrors.IsAlreadyExists(err) {
|
||||||
return f.CRDClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(ctx, crd.Name, metav1.GetOptions{})
|
return f.CRDClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(ctx, crd.Name, metav1.GetOptions{})
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -350,6 +454,14 @@ func (f *Factory) createCRD(ctx context.Context, crdDef CRD, ready map[string]*a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Factory) ensureAccess(ctx context.Context) (bool, error) {
|
||||||
|
_, err := f.CRDClient.ApiextensionsV1beta1().CustomResourceDefinitions().List(ctx, metav1.ListOptions{})
|
||||||
|
if apierrors.IsForbidden(err) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Factory) getReadyCRDs(ctx context.Context) (map[string]*apiext.CustomResourceDefinition, error) {
|
func (f *Factory) getReadyCRDs(ctx context.Context) (map[string]*apiext.CustomResourceDefinition, error) {
|
||||||
list, err := f.CRDClient.ApiextensionsV1beta1().CustomResourceDefinitions().List(ctx, metav1.ListOptions{})
|
list, err := f.CRDClient.ApiextensionsV1beta1().CustomResourceDefinitions().List(ctx, metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package data
|
||||||
|
|
||||||
|
func MergeMaps(base, overlay map[string]interface{}) map[string]interface{} {
|
||||||
|
result := map[string]interface{}{}
|
||||||
|
for k, v := range base {
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
for k, v := range overlay {
|
||||||
|
if baseMap, overlayMap, bothMaps := bothMaps(result[k], v); bothMaps {
|
||||||
|
v = MergeMaps(baseMap, overlayMap)
|
||||||
|
}
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func bothMaps(left, right interface{}) (map[string]interface{}, map[string]interface{}, bool) {
|
||||||
|
leftMap, ok := left.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, false
|
||||||
|
}
|
||||||
|
rightMap, ok := right.(map[string]interface{})
|
||||||
|
return leftMap, rightMap, ok
|
||||||
|
}
|
|
@ -1,7 +1,39 @@
|
||||||
package generic
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rancher/wrangler/pkg/apply"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
type GeneratingHandlerOptions struct {
|
type GeneratingHandlerOptions struct {
|
||||||
AllowCrossNamespace bool
|
AllowCrossNamespace bool
|
||||||
AllowClusterScoped bool
|
AllowClusterScoped bool
|
||||||
|
NoOwnerReference bool
|
||||||
DynamicLookup bool
|
DynamicLookup bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ConfigureApplyForObject(apply apply.Apply, obj metav1.Object, opts *GeneratingHandlerOptions) apply.Apply {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &GeneratingHandlerOptions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.DynamicLookup {
|
||||||
|
apply = apply.WithDynamicLookup()
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.NoOwnerReference {
|
||||||
|
apply = apply.WithSetOwnerReference(true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.AllowCrossNamespace && !opts.AllowClusterScoped {
|
||||||
|
apply = apply.
|
||||||
|
WithDefaultNamespace(obj.GetNamespace()).
|
||||||
|
WithListerNamespace(obj.GetNamespace())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !opts.AllowClusterScoped {
|
||||||
|
apply = apply.WithRestrictClusterScoped()
|
||||||
|
}
|
||||||
|
|
||||||
|
return apply
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
errors2 "github.com/pkg/errors"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ func (h *Handlers) Handle(key string, obj runtime.Object) (runtime.Object, error
|
||||||
|
|
||||||
for _, handler := range h.handlers {
|
for _, handler := range h.handlers {
|
||||||
newObj, err := handler.handler(key, obj)
|
newObj, err := handler.handler(key, obj)
|
||||||
if err != nil {
|
if err != nil && errors2.Cause(err) != ErrSkip {
|
||||||
errs = append(errs, &handlerError{
|
errs = append(errs, &handlerError{
|
||||||
HandlerName: handler.name,
|
HandlerName: handler.name,
|
||||||
Err: err,
|
Err: err,
|
||||||
|
|
|
@ -27,7 +27,16 @@ func Get(obj runtime.Object) (schema.GroupVersionKind, error) {
|
||||||
return gvks[0], nil
|
return gvks[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Set(obj runtime.Object) error {
|
func Set(objs ...runtime.Object) error {
|
||||||
|
for _, obj := range objs {
|
||||||
|
if err := setObject(obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setObject(obj runtime.Object) error {
|
||||||
gvk := obj.GetObjectKind().GroupVersionKind()
|
gvk := obj.GetObjectKind().GroupVersionKind()
|
||||||
if gvk.Kind != "" {
|
if gvk.Kind != "" {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -34,6 +34,8 @@ func (o ObjectKey) String() string {
|
||||||
return fmt.Sprintf("%s/%s", o.Namespace, o.Name)
|
return fmt.Sprintf("%s/%s", o.Namespace, o.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ObjectKeyByGVK map[schema.GroupVersionKind][]ObjectKey
|
||||||
|
|
||||||
type ObjectByGVK map[schema.GroupVersionKind]map[ObjectKey]runtime.Object
|
type ObjectByGVK map[schema.GroupVersionKind]map[ObjectKey]runtime.Object
|
||||||
|
|
||||||
func (o ObjectByGVK) Add(obj runtime.Object) (schema.GroupVersionKind, error) {
|
func (o ObjectByGVK) Add(obj runtime.Object) (schema.GroupVersionKind, error) {
|
||||||
|
@ -69,14 +71,19 @@ type ObjectSet struct {
|
||||||
gvkSeen map[schema.GroupVersionKind]bool
|
gvkSeen map[schema.GroupVersionKind]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewObjectSet() *ObjectSet {
|
func NewObjectSet(objs ...runtime.Object) *ObjectSet {
|
||||||
return &ObjectSet{
|
os := &ObjectSet{
|
||||||
objects: ObjectByGVK{},
|
objects: ObjectByGVK{},
|
||||||
gvkSeen: map[schema.GroupVersionKind]bool{},
|
gvkSeen: map[schema.GroupVersionKind]bool{},
|
||||||
}
|
}
|
||||||
|
os.Add(objs...)
|
||||||
|
return os
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ObjectSet) ObjectsByGVK() ObjectByGVK {
|
func (o *ObjectSet) ObjectsByGVK() ObjectByGVK {
|
||||||
|
if o == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return o.objects
|
return o.objects
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,6 +133,10 @@ func (o *ObjectSet) Len() int {
|
||||||
return len(o.objects)
|
return len(o.objects)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *ObjectSet) GVKs() []schema.GroupVersionKind {
|
||||||
|
return o.GVKOrder()
|
||||||
|
}
|
||||||
|
|
||||||
func (o *ObjectSet) GVKOrder(known ...schema.GroupVersionKind) []schema.GroupVersionKind {
|
func (o *ObjectSet) GVKOrder(known ...schema.GroupVersionKind) []schema.GroupVersionKind {
|
||||||
var rest []schema.GroupVersionKind
|
var rest []schema.GroupVersionKind
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ func applyStrategicMergePatch(original, patch []byte, lookup strategicpatch.Look
|
||||||
if err := json.Unmarshal(patch, &patchMap); err != nil {
|
if err := json.Unmarshal(patch, &patchMap); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
patchedMap, err := strategicpatch.StrategicMergeMapPatch(originalMap, patchMap, lookup)
|
patchedMap, err := strategicpatch.StrategicMergeMapPatchUsingLookupPatchMeta(originalMap, patchMap, lookup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package openapi
|
package openapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
types "github.com/rancher/wrangler/pkg/schemas"
|
types "github.com/rancher/wrangler/pkg/schemas"
|
||||||
|
@ -8,6 +9,14 @@ import (
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
blacklistFields = map[string]bool{
|
||||||
|
"kind": true,
|
||||||
|
"apiVersion": true,
|
||||||
|
"metadata": true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
func MustGenerate(obj interface{}) *v1beta1.JSONSchemaProps {
|
func MustGenerate(obj interface{}) *v1beta1.JSONSchemaProps {
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -42,178 +51,169 @@ func toOpenAPI(name string, schemas *types.Schemas) (*v1beta1.JSONSchemaProps, e
|
||||||
delete(newSchema.ResourceFields, "kind")
|
delete(newSchema.ResourceFields, "kind")
|
||||||
delete(newSchema.ResourceFields, "apiVersion")
|
delete(newSchema.ResourceFields, "apiVersion")
|
||||||
delete(newSchema.ResourceFields, "metadata")
|
delete(newSchema.ResourceFields, "metadata")
|
||||||
return parseSchema(newSchema, schemas)
|
|
||||||
|
return schemaToProps(newSchema, schemas, map[string]bool{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSchema(schema *types.Schema, schemas *types.Schemas) (*v1beta1.JSONSchemaProps, error) {
|
func populateField(fieldJSP *v1beta1.JSONSchemaProps, f *types.Field) error {
|
||||||
jsp := &v1beta1.JSONSchemaProps{
|
fieldJSP.Description = f.Description
|
||||||
Description: schema.Description,
|
// don't reset this to not nullable
|
||||||
Type: "object",
|
if f.Nullable {
|
||||||
Properties: map[string]v1beta1.JSONSchemaProps{},
|
fieldJSP.Nullable = f.Nullable
|
||||||
|
}
|
||||||
|
fieldJSP.MinLength = f.MinLength
|
||||||
|
fieldJSP.MaxLength = f.MaxLength
|
||||||
|
|
||||||
|
if f.Type == "string" && len(f.Options) > 0 {
|
||||||
|
for _, opt := range append(f.Options, "") {
|
||||||
|
bytes, err := json.Marshal(&opt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fieldJSP.Enum = append(fieldJSP.Enum, v1beta1.JSON{
|
||||||
|
Raw: bytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, f := range schema.ResourceFields {
|
if len(f.InvalidChars) > 0 {
|
||||||
fieldJSP := v1beta1.JSONSchemaProps{
|
fieldJSP.Pattern = fmt.Sprintf("^[^%s]*$", f.InvalidChars)
|
||||||
Description: f.Description,
|
}
|
||||||
Nullable: f.Nullable,
|
|
||||||
MinLength: f.MinLength,
|
if len(f.ValidChars) > 0 {
|
||||||
MaxLength: f.MaxLength,
|
fieldJSP.Pattern = fmt.Sprintf("^[%s]*$", f.ValidChars)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Min != nil {
|
||||||
|
fl := float64(*f.Min)
|
||||||
|
fieldJSP.Minimum = &fl
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Max != nil {
|
||||||
|
fl := float64(*f.Max)
|
||||||
|
fieldJSP.Maximum = &fl
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeToProps(typeName string, schemas *types.Schemas, inflight map[string]bool) (*v1beta1.JSONSchemaProps, error) {
|
||||||
|
t, subType, schema, err := typeAndSchema(typeName, schemas)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema != nil {
|
||||||
|
return schemaToProps(schema, schemas, inflight)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsp := &v1beta1.JSONSchemaProps{}
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case "map":
|
||||||
|
additionalProps, err := typeToProps(subType, schemas, inflight)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
jsp.Type = "object"
|
||||||
if len(f.Options) > 0 {
|
jsp.Nullable = true
|
||||||
for _, opt := range f.Options {
|
jsp.AdditionalProperties = &v1beta1.JSONSchemaPropsOrBool{
|
||||||
fieldJSP.Enum = append(fieldJSP.Enum, v1beta1.JSON{
|
Schema: additionalProps,
|
||||||
Raw: []byte(opt),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
case "array":
|
||||||
if len(f.InvalidChars) > 0 {
|
items, err := typeToProps(subType, schemas, inflight)
|
||||||
fieldJSP.Pattern = fmt.Sprintf("^[^%s]*$", f.InvalidChars)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
jsp.Type = "array"
|
||||||
if len(f.ValidChars) > 0 {
|
jsp.Nullable = true
|
||||||
fieldJSP.Pattern = fmt.Sprintf("^[%s]*$", f.ValidChars)
|
jsp.Items = &v1beta1.JSONSchemaPropsOrArray{
|
||||||
|
Schema: items,
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
if f.Min != nil {
|
jsp.Type = t
|
||||||
fl := float64(*f.Min)
|
|
||||||
fieldJSP.Minimum = &fl
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Max != nil {
|
|
||||||
fl := float64(*f.Max)
|
|
||||||
fieldJSP.Maximum = &fl
|
|
||||||
}
|
|
||||||
|
|
||||||
// default is not support by k8s
|
|
||||||
//
|
|
||||||
//if f.Default != nil {
|
|
||||||
// bytes, err := json.Marshal(f.Default)
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// fieldJSP.Default = &v1beta1.JSON{
|
|
||||||
// Raw: bytes,
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
if f.Required {
|
|
||||||
fieldJSP.Required = append(fieldJSP.Required, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if definition.IsMapType(f.Type) {
|
|
||||||
fieldJSP.Type = "object"
|
|
||||||
subType := definition.SubType(f.Type)
|
|
||||||
|
|
||||||
subType, schema, err := typeAndSchema(subType, schemas)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if schema == nil {
|
|
||||||
fieldJSP.AdditionalProperties = &v1beta1.JSONSchemaPropsOrBool{
|
|
||||||
Schema: &v1beta1.JSONSchemaProps{
|
|
||||||
Type: subType,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
subObject, err := parseSchema(schema, schemas)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldJSP.AdditionalProperties = &v1beta1.JSONSchemaPropsOrBool{
|
|
||||||
Schema: subObject,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if definition.IsArrayType(f.Type) {
|
|
||||||
fieldJSP.Type = "array"
|
|
||||||
subType := definition.SubType(f.Type)
|
|
||||||
|
|
||||||
subType, schema, err := typeAndSchema(subType, schemas)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if schema == nil {
|
|
||||||
fieldJSP.Items = &v1beta1.JSONSchemaPropsOrArray{
|
|
||||||
Schema: &v1beta1.JSONSchemaProps{
|
|
||||||
Type: subType,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
subObject, err := parseSchema(schema, schemas)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldJSP.Items = &v1beta1.JSONSchemaPropsOrArray{
|
|
||||||
Schema: subObject,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
typeName, schema, err := typeAndSchema(f.Type, schemas)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if schema == nil {
|
|
||||||
fieldJSP.Type = typeName
|
|
||||||
} else {
|
|
||||||
fieldJSP.Type = "object"
|
|
||||||
subObject, err := parseSchema(schema, schemas)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
fieldJSP.Properties = subObject.Properties
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jsp.Properties[name] = fieldJSP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsp, nil
|
return jsp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func typeAndSchema(typeName string, schemas *types.Schemas) (string, *types.Schema, error) {
|
func schemaToProps(schema *types.Schema, schemas *types.Schemas, inflight map[string]bool) (*v1beta1.JSONSchemaProps, error) {
|
||||||
switch typeName {
|
jsp := &v1beta1.JSONSchemaProps{
|
||||||
// TODO: in v1 set the x- header for this
|
Description: schema.Description,
|
||||||
case "intOrString":
|
Type: "object",
|
||||||
return "string", nil, nil
|
|
||||||
case "int":
|
|
||||||
return "integer", nil, nil
|
|
||||||
case "float":
|
|
||||||
return "number", nil, nil
|
|
||||||
case "string":
|
|
||||||
return "string", nil, nil
|
|
||||||
case "date":
|
|
||||||
return "string", nil, nil
|
|
||||||
case "enum":
|
|
||||||
return "string", nil, nil
|
|
||||||
case "password":
|
|
||||||
return "string", nil, nil
|
|
||||||
case "hostname":
|
|
||||||
return "string", nil, nil
|
|
||||||
case "boolean":
|
|
||||||
return "boolean", nil, nil
|
|
||||||
case "json":
|
|
||||||
return "object", nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if inflight[schema.ID] {
|
||||||
|
return jsp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
inflight[schema.ID] = true
|
||||||
|
defer delete(inflight, schema.ID)
|
||||||
|
|
||||||
|
jsp.Properties = map[string]v1beta1.JSONSchemaProps{}
|
||||||
|
|
||||||
|
for name, f := range schema.ResourceFields {
|
||||||
|
fieldJSP, err := typeToProps(f.Type, schemas, inflight)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := populateField(fieldJSP, &f); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if f.Required {
|
||||||
|
jsp.Required = append(jsp.Required, name)
|
||||||
|
}
|
||||||
|
jsp.Properties[name] = *fieldJSP
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeAndSchema(typeName string, schemas *types.Schemas) (string, string, *types.Schema, error) {
|
||||||
if definition.IsReferenceType(typeName) {
|
if definition.IsReferenceType(typeName) {
|
||||||
return "string", nil, nil
|
return "string", "", nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if definition.IsArrayType(typeName) {
|
if definition.IsArrayType(typeName) {
|
||||||
return "array", nil, nil
|
return "array", definition.SubType(typeName), nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if definition.IsMapType(typeName) {
|
||||||
|
return "map", definition.SubType(typeName), nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typeName {
|
||||||
|
// TODO: in v1 set the x- header for this
|
||||||
|
case "intOrString":
|
||||||
|
return "string", "", nil, nil
|
||||||
|
case "int":
|
||||||
|
return "integer", "", nil, nil
|
||||||
|
case "float":
|
||||||
|
return "number", "", nil, nil
|
||||||
|
case "string":
|
||||||
|
return "string", "", nil, nil
|
||||||
|
case "date":
|
||||||
|
return "string", "", nil, nil
|
||||||
|
case "enum":
|
||||||
|
return "string", "", nil, nil
|
||||||
|
case "base64":
|
||||||
|
return "string", "", nil, nil
|
||||||
|
case "password":
|
||||||
|
return "string", "", nil, nil
|
||||||
|
case "hostname":
|
||||||
|
return "string", "", nil, nil
|
||||||
|
case "boolean":
|
||||||
|
return "boolean", "", nil, nil
|
||||||
|
case "json":
|
||||||
|
return "object", "", nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
schema := schemas.Schema(typeName)
|
schema := schemas.Schema(typeName)
|
||||||
if schema == nil {
|
if schema == nil {
|
||||||
return "", nil, fmt.Errorf("failed to find schema %s", typeName)
|
return "", "", nil, fmt.Errorf("failed to find schema %s", typeName)
|
||||||
}
|
}
|
||||||
if schema.InternalSchema != nil {
|
if schema.InternalSchema != nil {
|
||||||
return "", schema.InternalSchema, nil
|
return "", "", schema.InternalSchema, nil
|
||||||
}
|
}
|
||||||
return "", schema, nil
|
return "", "", schema, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,23 +64,25 @@ func (s *Schemas) MustImportAndCustomize(obj interface{}, f func(*Schema), exter
|
||||||
MustCustomizeType(obj, f)
|
MustCustomizeType(obj, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getType(obj interface{}) reflect.Type {
|
||||||
|
if t, ok := obj.(reflect.Type); ok {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
t := reflect.TypeOf(obj)
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Schemas) Import(obj interface{}, externalOverrides ...interface{}) (*Schema, error) {
|
func (s *Schemas) Import(obj interface{}, externalOverrides ...interface{}) (*Schema, error) {
|
||||||
var types []reflect.Type
|
var types []reflect.Type
|
||||||
for _, override := range externalOverrides {
|
for _, override := range externalOverrides {
|
||||||
types = append(types, reflect.TypeOf(override))
|
types = append(types, getType(override))
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
v = reflect.ValueOf(obj)
|
|
||||||
t reflect.Type
|
|
||||||
)
|
|
||||||
|
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
t = v.Elem().Type()
|
|
||||||
} else {
|
|
||||||
t = v.Type()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t := getType(obj)
|
||||||
return s.importType(t, types...)
|
return s.importType(t, types...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,6 +322,9 @@ func (s *Schemas) readFields(schema *Schema, t reflect.Type) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasType && hasMeta {
|
if hasType && hasMeta {
|
||||||
|
delete(schema.ResourceFields, "kind")
|
||||||
|
delete(schema.ResourceFields, "apiVersion")
|
||||||
|
delete(schema.ResourceFields, "metadata")
|
||||||
schema.CollectionMethods = []string{"GET", "POST"}
|
schema.CollectionMethods = []string{"GET", "POST"}
|
||||||
schema.ResourceMethods = []string{"GET", "PUT", "DELETE"}
|
schema.ResourceMethods = []string{"GET", "PUT", "DELETE"}
|
||||||
}
|
}
|
||||||
|
@ -357,7 +362,11 @@ func (s *Schemas) processFieldsMappers(t reflect.Type, fieldName string, schema
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyTag(structField *reflect.StructField, field *Field) error {
|
func applyTag(structField *reflect.StructField, field *Field) error {
|
||||||
for _, part := range strings.Split(structField.Tag.Get("norman"), ",") {
|
t, ok := structField.Tag.Lookup("wrangler")
|
||||||
|
if !ok {
|
||||||
|
t = structField.Tag.Get("norman")
|
||||||
|
}
|
||||||
|
for _, part := range strings.Split(t, ",") {
|
||||||
if part == "" {
|
if part == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,7 +135,7 @@ github.com/blang/semver
|
||||||
github.com/bronze1man/goStrongswanVici
|
github.com/bronze1man/goStrongswanVici
|
||||||
# github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23
|
# github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23
|
||||||
github.com/buger/jsonparser
|
github.com/buger/jsonparser
|
||||||
# github.com/canonical/go-dqlite v1.3.0
|
# github.com/canonical/go-dqlite v1.5.1
|
||||||
github.com/canonical/go-dqlite
|
github.com/canonical/go-dqlite
|
||||||
github.com/canonical/go-dqlite/client
|
github.com/canonical/go-dqlite/client
|
||||||
github.com/canonical/go-dqlite/driver
|
github.com/canonical/go-dqlite/driver
|
||||||
|
@ -716,7 +716,7 @@ github.com/rancher/dynamiclistener/factory
|
||||||
github.com/rancher/dynamiclistener/storage/file
|
github.com/rancher/dynamiclistener/storage/file
|
||||||
github.com/rancher/dynamiclistener/storage/kubernetes
|
github.com/rancher/dynamiclistener/storage/kubernetes
|
||||||
github.com/rancher/dynamiclistener/storage/memory
|
github.com/rancher/dynamiclistener/storage/memory
|
||||||
# github.com/rancher/helm-controller v0.4.2-0.20200326195131-eb51d4fa9d8d
|
# github.com/rancher/helm-controller v0.5.0
|
||||||
github.com/rancher/helm-controller/pkg/apis/helm.cattle.io
|
github.com/rancher/helm-controller/pkg/apis/helm.cattle.io
|
||||||
github.com/rancher/helm-controller/pkg/apis/helm.cattle.io/v1
|
github.com/rancher/helm-controller/pkg/apis/helm.cattle.io/v1
|
||||||
github.com/rancher/helm-controller/pkg/generated/clientset/versioned
|
github.com/rancher/helm-controller/pkg/generated/clientset/versioned
|
||||||
|
@ -730,7 +730,7 @@ github.com/rancher/helm-controller/pkg/generated/informers/externalversions/helm
|
||||||
github.com/rancher/helm-controller/pkg/generated/informers/externalversions/internalinterfaces
|
github.com/rancher/helm-controller/pkg/generated/informers/externalversions/internalinterfaces
|
||||||
github.com/rancher/helm-controller/pkg/generated/listers/helm.cattle.io/v1
|
github.com/rancher/helm-controller/pkg/generated/listers/helm.cattle.io/v1
|
||||||
github.com/rancher/helm-controller/pkg/helm
|
github.com/rancher/helm-controller/pkg/helm
|
||||||
# github.com/rancher/kine v0.3.5
|
# github.com/rancher/kine v0.4.0
|
||||||
github.com/rancher/kine/pkg/broadcaster
|
github.com/rancher/kine/pkg/broadcaster
|
||||||
github.com/rancher/kine/pkg/client
|
github.com/rancher/kine/pkg/client
|
||||||
github.com/rancher/kine/pkg/drivers/dqlite
|
github.com/rancher/kine/pkg/drivers/dqlite
|
||||||
|
@ -745,7 +745,7 @@ github.com/rancher/kine/pkg/server
|
||||||
github.com/rancher/kine/pkg/tls
|
github.com/rancher/kine/pkg/tls
|
||||||
# github.com/rancher/remotedialer v0.2.0
|
# github.com/rancher/remotedialer v0.2.0
|
||||||
github.com/rancher/remotedialer
|
github.com/rancher/remotedialer
|
||||||
# github.com/rancher/wrangler v0.5.4-0.20200326191509-4054411d9736
|
# github.com/rancher/wrangler v0.6.1
|
||||||
github.com/rancher/wrangler/pkg/apply
|
github.com/rancher/wrangler/pkg/apply
|
||||||
github.com/rancher/wrangler/pkg/apply/injectors
|
github.com/rancher/wrangler/pkg/apply/injectors
|
||||||
github.com/rancher/wrangler/pkg/cleanup
|
github.com/rancher/wrangler/pkg/cleanup
|
||||||
|
@ -773,7 +773,7 @@ github.com/rancher/wrangler/pkg/schemes
|
||||||
github.com/rancher/wrangler/pkg/signals
|
github.com/rancher/wrangler/pkg/signals
|
||||||
github.com/rancher/wrangler/pkg/slice
|
github.com/rancher/wrangler/pkg/slice
|
||||||
github.com/rancher/wrangler/pkg/start
|
github.com/rancher/wrangler/pkg/start
|
||||||
# github.com/rancher/wrangler-api v0.5.1-0.20200326194427-c13310506d04
|
# github.com/rancher/wrangler-api v0.6.0
|
||||||
github.com/rancher/wrangler-api/pkg/generated/controllers/apps
|
github.com/rancher/wrangler-api/pkg/generated/controllers/apps
|
||||||
github.com/rancher/wrangler-api/pkg/generated/controllers/apps/v1
|
github.com/rancher/wrangler-api/pkg/generated/controllers/apps/v1
|
||||||
github.com/rancher/wrangler-api/pkg/generated/controllers/batch
|
github.com/rancher/wrangler-api/pkg/generated/controllers/batch
|
||||||
|
|
Loading…
Reference in New Issue