From 7f3c415de204f533a6535539c742402cc226d1fc Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Tue, 10 Nov 2020 17:37:50 +0200 Subject: [PATCH 01/22] Added startedAt property to /status handler response --- platform/kubernetes.go | 2 ++ platform/platform.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/platform/kubernetes.go b/platform/kubernetes.go index a5491f9..c6b2dc8 100644 --- a/platform/kubernetes.go +++ b/platform/kubernetes.go @@ -292,6 +292,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { CancelFunc: func() { cancel() }, + StartedAt: pod.CreationTimestamp.Time, } return svc, nil @@ -334,6 +335,7 @@ func (cl *Client) List() ([]*Service, error) { CancelFunc: func() { cl.Delete(podName) }, + StartedAt: pod.CreationTimestamp.Time, } services = append(services, s) } diff --git a/platform/platform.go b/platform/platform.go index acd07ec..baeda1b 100644 --- a/platform/platform.go +++ b/platform/platform.go @@ -4,6 +4,7 @@ import ( "context" "io" "net/url" + "time" "github.com/alcounit/selenosis/selenium" apiv1 "k8s.io/api/core/v1" @@ -49,6 +50,7 @@ type Service struct { Labels map[string]string `json:"labels"` OnTimeout chan struct{} `json:"-"` CancelFunc func() `json:"-"` + StartedAt time.Time `json:"startedAt"` } //Platform ... From 0d3169fbdedc2ea3d8faa690c03102597696b4ac Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Tue, 10 Nov 2020 20:26:59 +0200 Subject: [PATCH 02/22] Added private registry support. Status handler output improvements. Fixed app crash on incomming connection --- README.md | 14 +++-- cmd/selenosis/main.go | 16 ++++-- handlers.go | 49 ++++++++++++++++-- platform/kubernetes.go | 113 ++++++++++++++++++++++++++--------------- platform/platform.go | 4 +- 5 files changed, 138 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 1b7312f..a07c5b0 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Usage: Flags: --port string port for selenosis (default ":4444") --proxy-port string proxy continer port (default "4445") - --browsers-config string browsers config (default "config/browsers.yaml") + --browsers-config string browsers config (default "./config/browsers.yaml") --browser-limit int active sessions max limit (default 10) --namespace string kubernetes namespace (default "default") --service-name string kubernetes service name for browsers (default "selenosis") @@ -21,7 +21,9 @@ Flags: --session-wait-timeout duration time in seconds that a session will be ready (default 1m0s) --session-iddle-timeout duration time in seconds that a session will iddle (default 5m0s) --session-retry-count int session retry count (default 3) - --graceful-shutdown-timeout duration time in seconds gracefull shutdown timeout (default 5m0s) + --graceful-shutdown-timeout duration time in seconds gracefull shutdown timeout (default 30s) + --image-pull-secret-name string secret name to private registry + --proxy-image string in case you use private registry replace with image from private registry (default "alcounit/seleniferous:latest") -h, --help help for selenosis ``` @@ -112,8 +114,6 @@ opera: '71.0': image: selenoid/vnc:opera_71.0 ``` - - Browser name and browser version are taken from Selenium desired capabilities.
Each browser can have default spec/annotations/labels, they will merged to all browsers listed in the versions section. @@ -440,4 +440,8 @@ Selenosis doesn't store any session info. All connections to the browsers are au Selenosis supports hot config reload, to do so update you configMap ```bash kubectl edit configmap -n selenosis selenosis-config -o yaml -``` \ No newline at end of file +``` + +### UI for debug +Selenosis itself doesn't have ui. If you need such functionality you can use [selenoid-ui](https://github.com/aerokube/selenoid-ui) with special [adapter container](https://github.com/alcounit/adaptee). +Deployment steps and minifests you can find in [selenosis-deploy](https://github.com/alcounit/selenosis-deploy) repository. diff --git a/cmd/selenosis/main.go b/cmd/selenosis/main.go index 6030312..3f14fc3 100644 --- a/cmd/selenosis/main.go +++ b/cmd/selenosis/main.go @@ -30,6 +30,8 @@ func command() *cobra.Command { proxyPort string namespace string service string + imagePullSecretName string + proxyImage string sessionRetryCount int limit int browserWaitTimeout time.Duration @@ -58,11 +60,13 @@ func command() *cobra.Command { logger.Info("config watcher started") client, err := platform.NewClient(platform.ClientConfig{ - Namespace: namespace, - Service: service, - ReadinessTimeout: browserWaitTimeout, - IddleTimeout: sessionIddleTimeout, - ServicePort: proxyPort, + Namespace: namespace, + Service: service, + ReadinessTimeout: browserWaitTimeout, + IddleTimeout: sessionIddleTimeout, + ServicePort: proxyPort, + ImagePullSecretName: imagePullSecretName, + ProxyImage: proxyImage, }) if err != nil { @@ -137,6 +141,8 @@ func command() *cobra.Command { cmd.Flags().DurationVar(&sessionIddleTimeout, "session-iddle-timeout", 5*time.Minute, "time in seconds that a session will iddle") cmd.Flags().IntVar(&sessionRetryCount, "session-retry-count", 3, "session retry count") cmd.Flags().DurationVar(&shutdownTimeout, "graceful-shutdown-timeout", 30*time.Second, "time in seconds gracefull shutdown timeout") + cmd.Flags().StringVar(&imagePullSecretName, "image-pull-secret-name", "", "secret name to private registry") + cmd.Flags().StringVar(&proxyImage, "proxy-image", "alcounit/seleniferous:latest", "in case you use private registry replace with image from private registry") cmd.Flags().SortFlags = false return cmd diff --git a/handlers.go b/handlers.go index 070dde7..5b343c9 100644 --- a/handlers.go +++ b/handlers.go @@ -10,6 +10,7 @@ import ( "net" "net/http" "net/http/httputil" + "net/url" "regexp" "strings" "time" @@ -287,6 +288,11 @@ func (app *App) HandleVNC() websocket.Handler { "request": fmt.Sprintf("%s %s", wsconn.Request().Method, wsconn.Request().URL.Path), }) + if !statusOk(sessionID, app.serviceName, app.sidecarPort) { + logger.Errorf("container host is unreachable") + return + } + host := tools.BuildHostPort(sessionID, app.serviceName, "5900") logger.Infof("vnc request: %s", host) @@ -322,21 +328,26 @@ func (app *App) HandleLogs() websocket.Handler { "request": fmt.Sprintf("%s %s", wsconn.Request().Method, wsconn.Request().URL.Path), }) + if !statusOk(sessionID, app.serviceName, app.sidecarPort) { + logger.Errorf("container host is unreachable") + return + } + logger.Infof("stream logs request: %s", fmt.Sprintf("%s.%s", sessionID, app.serviceName)) - r, err := app.client.Logs(wsconn.Request().Context(), sessionID) + conn, err := app.client.Logs(wsconn.Request().Context(), sessionID) if err != nil { logger.Errorf("stream logs error: %v", err) } - defer r.Close() + defer conn.Close() wsconn.PayloadType = websocket.BinaryFrame go func() { - io.Copy(wsconn, r) + io.Copy(wsconn, conn) wsconn.Close() logger.Errorf("stream logs connection closed") }() - io.Copy(wsconn, r) + io.Copy(wsconn, conn) logger.Errorf("stream logs disconnected") } } @@ -347,6 +358,8 @@ func (app *App) HandleStatus(w http.ResponseWriter, r *http.Request) { type Status struct { Total int `json:"total"` + Active int `json:"active"` + Pending int `json:"pending"` Browsers map[string][]string `json:"config,omitempty"` Sessions []*platform.Service `json:"sessions,omitempty"` } @@ -358,6 +371,7 @@ func (app *App) HandleStatus(w http.ResponseWriter, r *http.Request) { } sessions, err := app.client.List() + if err != nil { app.logger.Errorf("hub status: %v", err) json.NewEncoder(w).Encode( @@ -373,13 +387,23 @@ func (app *App) HandleStatus(w http.ResponseWriter, r *http.Request) { return } + ready := make([]*platform.Service, 0) + + for _, s := range sessions { + if s.Ready { + ready = append(ready, s) + } + } + json.NewEncoder(w).Encode( Response{ Status: http.StatusOK, Selenosis: Status{ Total: app.sessionLimit, + Active: len(ready), + Pending: len(sessions) - len(ready), Browsers: app.browsers.GetBrowserVersions(), - Sessions: sessions, + Sessions: ready, }, }, ) @@ -393,3 +417,18 @@ func parseImage(image string) (container string) { } return pref.ReplaceAllString(image, "-") } + +func statusOk(session, service, port string) bool { + u := &url.URL{ + Scheme: "http", + Host: tools.BuildHostPort(session, service, port), + Path: "/status", + } + + resp, err := http.Get(u.String()) + if err != nil || resp.StatusCode != http.StatusOK { + return false + } + + return true +} diff --git a/platform/kubernetes.go b/platform/kubernetes.go index c6b2dc8..ffa50df 100644 --- a/platform/kubernetes.go +++ b/platform/kubernetes.go @@ -10,6 +10,7 @@ import ( "path" "time" + "github.com/alcounit/selenosis/tools" apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" @@ -44,21 +45,25 @@ var ( //ClientConfig ... type ClientConfig struct { - Namespace string - Service string - ReadinessTimeout time.Duration - IddleTimeout time.Duration - ServicePort string + Namespace string + Service string + ServicePort string + ImagePullSecretName string + ProxyImage string + ReadinessTimeout time.Duration + IddleTimeout time.Duration } //Client ... type Client struct { - ns string - svc string - svcPort intstr.IntOrString - readinessTimeout time.Duration - iddleTimeout time.Duration - clientset v1.CoreV1Interface + ns string + svc string + svcPort intstr.IntOrString + imagePullSecretName string + proxyImage string + readinessTimeout time.Duration + iddleTimeout time.Duration + clientset v1.CoreV1Interface } //NewClient ... @@ -75,12 +80,14 @@ func NewClient(c ClientConfig) (Platform, error) { } return &Client{ - ns: c.Namespace, - clientset: clientset.CoreV1(), - svc: c.Service, - svcPort: intstr.FromString(c.ServicePort), - readinessTimeout: c.ReadinessTimeout, - iddleTimeout: c.IddleTimeout, + ns: c.Namespace, + clientset: clientset.CoreV1(), + svc: c.Service, + svcPort: intstr.FromString(c.ServicePort), + imagePullSecretName: c.ImagePullSecretName, + proxyImage: c.ProxyImage, + readinessTimeout: c.ReadinessTimeout, + iddleTimeout: c.IddleTimeout, }, nil } @@ -200,7 +207,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { }, { Name: "seleniferous", - Image: "alcounit/seleniferous:latest", + Image: cl.proxyImage, SecurityContext: &apiv1.SecurityContext{ Privileged: pointer.BoolPtr(true), }, @@ -220,11 +227,12 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { }, }, }, - NodeSelector: layout.Template.Spec.NodeSelector, - HostAliases: layout.Template.Spec.HostAliases, - RestartPolicy: apiv1.RestartPolicyNever, - Affinity: &layout.Template.Spec.Affinity, - DNSConfig: &layout.Template.Spec.DNSConfig, + NodeSelector: layout.Template.Spec.NodeSelector, + HostAliases: layout.Template.Spec.HostAliases, + RestartPolicy: apiv1.RestartPolicyNever, + Affinity: &layout.Template.Spec.Affinity, + DNSConfig: &layout.Template.Spec.DNSConfig, + ImagePullSecrets: getImagePullSecretList(cl.imagePullSecretName), }, } @@ -292,7 +300,8 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { CancelFunc: func() { cancel() }, - StartedAt: pod.CreationTimestamp.Time, + Started: pod.CreationTimestamp.Time, + Uptime: tools.TimeElapsed(pod.CreationTimestamp.Time), } return svc, nil @@ -321,24 +330,34 @@ func (cl *Client) List() ([]*Service, error) { var services []*Service for _, pod := range pods.Items { - if pod.Status.Phase == apiv1.PodRunning { - podName := pod.GetName() - host := fmt.Sprintf("%s.%s", podName, cl.svc) - - s := &Service{ - SessionID: podName, - URL: &url.URL{ - Scheme: "http", - Host: net.JoinHostPort(host, cl.svcPort.StrVal), - }, - Labels: pod.GetLabels(), - CancelFunc: func() { - cl.Delete(podName) - }, - StartedAt: pod.CreationTimestamp.Time, - } - services = append(services, s) + podName := pod.GetName() + host := fmt.Sprintf("%s.%s", podName, cl.svc) + + var ready bool + switch pod.Status.Phase { + case apiv1.PodRunning: + ready = true + case apiv1.PodPending: + ready = false + default: + continue } + + s := &Service{ + SessionID: podName, + URL: &url.URL{ + Scheme: "http", + Host: net.JoinHostPort(host, cl.svcPort.StrVal), + }, + Labels: pod.GetLabels(), + CancelFunc: func() { + cl.Delete(podName) + }, + Ready: ready, + Started: pod.CreationTimestamp.Time, + Uptime: tools.TimeElapsed(pod.CreationTimestamp.Time), + } + services = append(services, s) } return services, nil @@ -377,7 +396,17 @@ func getSidecarPorts(p intstr.IntOrString) []apiv1.ContainerPort { return port } -//code credits to https://github.com/aerokube/selenoid/blob/master/service/service.go#L97 +func getImagePullSecretList(secret string) []apiv1.LocalObjectReference { + refList := make([]apiv1.LocalObjectReference, 0) + if secret != "" { + ref := apiv1.LocalObjectReference{ + Name: secret, + } + refList = append(refList, ref) + } + return refList +} + func waitForService(u *url.URL, t time.Duration) error { up := make(chan struct{}) done := make(chan struct{}) diff --git a/platform/platform.go b/platform/platform.go index baeda1b..907a082 100644 --- a/platform/platform.go +++ b/platform/platform.go @@ -50,7 +50,9 @@ type Service struct { Labels map[string]string `json:"labels"` OnTimeout chan struct{} `json:"-"` CancelFunc func() `json:"-"` - StartedAt time.Time `json:"startedAt"` + Ready bool `json:"-"` + Started time.Time `json:"started"` + Uptime string `json:"uptime"` } //Platform ... From c5aabaa6063940af5aeb6db3ee43d586211f80b2 Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Fri, 13 Nov 2020 15:44:34 +0200 Subject: [PATCH 03/22] Aded ImagePullPolicy, change browser container name --- platform/kubernetes.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/kubernetes.go b/platform/kubernetes.go index ffa50df..f33333a 100644 --- a/platform/kubernetes.go +++ b/platform/kubernetes.go @@ -185,7 +185,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { Subdomain: cl.svc, Containers: []apiv1.Container{ { - Name: layout.SessionID, + Name: "browser", Image: layout.Template.Image, SecurityContext: &apiv1.SecurityContext{ Privileged: pointer.BoolPtr(false), @@ -204,6 +204,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { MountPath: "/dev/shm", }, }, + ImagePullPolicy: apiv1.PullIfNotPresent, }, { Name: "seleniferous", @@ -215,6 +216,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { Command: []string{ "/seleniferous", "--listhen-port", cl.svcPort.StrVal, "--proxy-default-path", path.Join(layout.Template.Path, "session"), "--iddle-timeout", cl.iddleTimeout.String(), "--namespace", cl.ns, }, + ImagePullPolicy: apiv1.PullIfNotPresent, }, }, Volumes: []apiv1.Volume{ From 556a4b9833fa995fd34ac2d4f7e3a249be7c4e4b Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Sat, 14 Nov 2020 17:44:09 +0200 Subject: [PATCH 04/22] README update --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a07c5b0..ce5d4fd 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Scalable, stateless selenium hub for Kubernetes cluster. ## Overview ### Available flags ``` -[user@host]# ./selenosis --help +[user@host]$ ./selenosis --help Scallable, stateless selenium grid for Kubernetes cluster Usage: @@ -394,8 +394,6 @@ Files and steps required for selenosis deployment available in [selenosis-deploy DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setBrowserName("chrome"); capabilities.setVersion("85.0"); -capabilities.setCapability("enableVNC", true); -capabilities.setCapability("enableVideo", false); RemoteWebDriver driver = new RemoteWebDriver( URI.create("http://:/wd/hub").toURL(), @@ -408,8 +406,6 @@ from selenium import webdriver capabilities = { "browserName": "chrome", "version": "85.0", - "enableVNC": True, - "enableVideo": False } driver = webdriver.Remote( @@ -417,6 +413,8 @@ driver = webdriver.Remote( desired_capabilities=capabilities) ``` + Note: you can omit browser version in your desired capabilities, make sure you set defaultVersion property in the config file. + ## Features ### Scalability By default selenosis starts with 2 replica sets. To change it, edit selenosis deployment file: 03-selenosis.yaml From 465c7f81723daa3812fc33d080a1e0bd84508de9 Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Sun, 15 Nov 2020 16:18:37 +0200 Subject: [PATCH 05/22] Fix readme config examples --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ce5d4fd..74eefe7 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,10 @@ Basic configuration be like (all fields in this example are mandatory): "path": "/", "versions": { "85.0": { - "image": "selenoid/vnc:chrome:85.0" + "image": "selenoid/vnc:chrome_85.0" }, "86.0": { - "image": "selenoid/vnc:chrome:86.0" + "image": "selenoid/vnc:chrome_86.0" } } }, @@ -94,9 +94,9 @@ chrome: path: "/" versions: '85.0': - image: selenoid/vnc:chrome:85.0 + image: selenoid/vnc:chrome_85.0 '86.0': - image: selenoid/vnc:chrome:86.0 + image: selenoid/vnc:chrome_86.0 firefox: defaultVersion: "82.0" path: "/wd/hub" @@ -177,10 +177,10 @@ Each browser can have default spec/annotations/labels, they will merged t }, "versions": { "85.0": { - "image": "selenoid/vnc:chrome:85.0" + "image": "selenoid/vnc:chrome_85.0" }, "86.0": { - "image": "selenoid/vnc:chrome:86.0" + "image": "selenoid/vnc:chrome_86.0" } } } @@ -225,9 +225,9 @@ chrome: value: 'true' versions: '85.0': - image: selenoid/vnc:chrome:85.0 + image: selenoid/vnc:chrome_85.0 '86.0': - image: selenoid/vnc:chrome:86.0 + image: selenoid/vnc:chrome_86.0 ``` You can override default browser spec/annotation/labels by providing individual spec/annotation/labels to browser version ``` json @@ -289,7 +289,7 @@ You can override default browser spec/annotation/labels by providing indi }, "versions": { "85.0": { - "image": "selenoid/vnc:chrome:85.0", + "image": "selenoid/vnc:chrome_85.0", "spec": { "resources": { "requests": { @@ -304,7 +304,7 @@ You can override default browser spec/annotation/labels by providing indi } }, "86.0": { - "image": "selenoid/vnc:chrome:86.0", + "image": "selenoid/vnc:chrome_86.0", "spec": { "hostAliases": [ { @@ -364,7 +364,7 @@ chrome: value: 'true' versions: '85.0': - image: selenoid/vnc:chrome:85.0 + image: selenoid/vnc:chrome_85.0 spec: resources: requests: @@ -374,7 +374,7 @@ chrome: memory: 1500Gi cpu: '1' '86.0': - image: selenoid/vnc:chrome:86.0 + image: selenoid/vnc:chrome_86.0 spec: hostAliases: - ip: 127.0.0.1 From 22110d2204bb1d9110139f23775e79c653c158dd Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Mon, 16 Nov 2020 15:14:50 +0200 Subject: [PATCH 06/22] Update cli props --- README.md | 4 ++-- cmd/selenosis/main.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 74eefe7..8707899 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ Flags: --proxy-port string proxy continer port (default "4445") --browsers-config string browsers config (default "./config/browsers.yaml") --browser-limit int active sessions max limit (default 10) - --namespace string kubernetes namespace (default "default") - --service-name string kubernetes service name for browsers (default "selenosis") + --namespace string kubernetes namespace (default "selenosis") + --service-name string kubernetes service name for browsers (default "seleniferous") --browser-wait-timeout duration time in seconds that a browser will be ready (default 30s) --session-wait-timeout duration time in seconds that a session will be ready (default 1m0s) --session-iddle-timeout duration time in seconds that a session will iddle (default 5m0s) diff --git a/cmd/selenosis/main.go b/cmd/selenosis/main.go index 3f14fc3..c3aef33 100644 --- a/cmd/selenosis/main.go +++ b/cmd/selenosis/main.go @@ -134,8 +134,8 @@ func command() *cobra.Command { cmd.Flags().StringVar(&proxyPort, "proxy-port", "4445", "proxy continer port") cmd.Flags().StringVar(&cfgFile, "browsers-config", "./config/browsers.yaml", "browsers config") cmd.Flags().IntVar(&limit, "browser-limit", 10, "active sessions max limit") - cmd.Flags().StringVar(&namespace, "namespace", "default", "kubernetes namespace") - cmd.Flags().StringVar(&service, "service-name", "selenosis", "kubernetes service name for browsers") + cmd.Flags().StringVar(&namespace, "namespace", "selenosis", "kubernetes namespace") + cmd.Flags().StringVar(&service, "service-name", "seleniferous", "kubernetes service name for browsers") cmd.Flags().DurationVar(&browserWaitTimeout, "browser-wait-timeout", 30*time.Second, "time in seconds that a browser will be ready") cmd.Flags().DurationVar(&sessionWaitTimeout, "session-wait-timeout", 60*time.Second, "time in seconds that a session will be ready") cmd.Flags().DurationVar(&sessionIddleTimeout, "session-iddle-timeout", 5*time.Minute, "time in seconds that a session will iddle") From ba2ea8458c334b047a357d2201152f566ef469a4 Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Tue, 17 Nov 2020 13:39:21 +0200 Subject: [PATCH 07/22] Fix panic on logs stream, added build version prop --- Dockerfile | 4 +++- cmd/selenosis/main.go | 4 +++- handlers.go | 1 + platform/kubernetes.go | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8f4f6db..c452c0a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,7 @@ FROM golang:alpine AS builder +ARG BUILD_VERSION + RUN apk add --quiet --no-cache build-base git WORKDIR /src @@ -13,7 +15,7 @@ RUN go mod download ADD . . RUN cd cmd/selenosis && \ - go install -ldflags="-linkmode external -extldflags '-static' -s -w" + go install -ldflags="-X main.buildVersion=$BUILD_VERSION -linkmode external -extldflags '-static' -s -w" FROM scratch diff --git a/cmd/selenosis/main.go b/cmd/selenosis/main.go index c3aef33..c5d9877 100644 --- a/cmd/selenosis/main.go +++ b/cmd/selenosis/main.go @@ -21,6 +21,8 @@ import ( "golang.org/x/net/websocket" ) +var buildVersion = "HEAD" + //Command ... func command() *cobra.Command { @@ -46,7 +48,7 @@ func command() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { logger := logrus.New() - logger.Info("starting selenosis") + logger.Infof("starting selenosis %s", buildVersion) browsers, err := config.NewBrowsersConfig(cfgFile) if err != nil { diff --git a/handlers.go b/handlers.go index 5b343c9..57fc653 100644 --- a/handlers.go +++ b/handlers.go @@ -338,6 +338,7 @@ func (app *App) HandleLogs() websocket.Handler { conn, err := app.client.Logs(wsconn.Request().Context(), sessionID) if err != nil { logger.Errorf("stream logs error: %v", err) + return } defer conn.Close() wsconn.PayloadType = websocket.BinaryFrame diff --git a/platform/kubernetes.go b/platform/kubernetes.go index f33333a..212c5c1 100644 --- a/platform/kubernetes.go +++ b/platform/kubernetes.go @@ -369,7 +369,7 @@ func (cl *Client) List() ([]*Service, error) { //Logs ... func (cl *Client) Logs(ctx context.Context, name string) (io.ReadCloser, error) { req := cl.clientset.Pods(cl.ns).GetLogs(name, &apiv1.PodLogOptions{ - Container: name, + Container: "browser", Follow: true, Previous: false, Timestamps: false, From 2c6f05d6545eb409fa8ab8e2cd0741a1fa2087f6 Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Tue, 17 Nov 2020 14:42:56 +0200 Subject: [PATCH 08/22] Fix log output for vnc and log handlers --- handlers.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/handlers.go b/handlers.go index 57fc653..e8d88d3 100644 --- a/handlers.go +++ b/handlers.go @@ -307,10 +307,10 @@ func (app *App) HandleVNC() websocket.Handler { go func() { io.Copy(wsconn, conn) wsconn.Close() - logger.Errorf("vnc connection closed") + logger.Warnf("vnc connection closed") }() io.Copy(conn, wsconn) - logger.Errorf("vnc client disconnected") + logger.Infof("vnc client disconnected") } } @@ -346,10 +346,10 @@ func (app *App) HandleLogs() websocket.Handler { go func() { io.Copy(wsconn, conn) wsconn.Close() - logger.Errorf("stream logs connection closed") + logger.Warnf("stream logs connection closed") }() io.Copy(wsconn, conn) - logger.Errorf("stream logs disconnected") + logger.Infof("stream logs disconnected") } } From f1a7c7c60c059defd2336d809a19bc3e8534229f Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Sun, 13 Dec 2020 14:06:54 +0200 Subject: [PATCH 09/22] Small iprovements in pod status check, session limit check. Fix tests for handlers --- cmd/selenosis/main.go | 2 +- go.sum | 60 +++--------------------------------------- handlers.go | 39 +++++++++++++++++---------- handlers_test.go | 5 ---- platform/kubernetes.go | 54 ++++++++++++++++--------------------- selenium/selenium.go | 2 -- selenosis.go | 16 +++++------ 7 files changed, 60 insertions(+), 118 deletions(-) diff --git a/cmd/selenosis/main.go b/cmd/selenosis/main.go index c5d9877..2ead281 100644 --- a/cmd/selenosis/main.go +++ b/cmd/selenosis/main.go @@ -90,7 +90,7 @@ func command() *cobra.Command { }) router := mux.NewRouter() - router.HandleFunc("/wd/hub/session", app.HandleSession).Methods(http.MethodPost) + router.HandleFunc("/wd/hub/session", app.CheckLimit(app.HandleSession)).Methods(http.MethodPost) router.PathPrefix("/wd/hub/session/{sessionId}").HandlerFunc(app.HandleProxy) router.HandleFunc("/wd/hub/status", app.HadleHubStatus).Methods(http.MethodGet) router.PathPrefix("/vnc/{sessionId}").Handler(websocket.Handler(app.HandleVNC())) diff --git a/go.sum b/go.sum index 0c60b43..6f7ab9e 100644 --- a/go.sum +++ b/go.sum @@ -32,7 +32,6 @@ github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdko github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -46,9 +45,7 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= @@ -64,7 +61,6 @@ github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkg github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -90,14 +86,12 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -115,6 +109,7 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= @@ -129,15 +124,11 @@ github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -168,11 +159,9 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -183,13 +172,11 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -224,8 +211,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -245,8 +232,6 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -256,8 +241,6 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -265,20 +248,16 @@ github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzu github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -288,13 +267,11 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -318,7 +295,6 @@ golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCc golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -332,20 +308,15 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201029055024-942e2f445f3c h1:rpcgRPA7OvNEOdprt2Wx8/Re2cBTd8NPo/lvo3AyMqk= golang.org/x/net v0.0.0-20201029055024-942e2f445f3c/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6 h1:pE8b58s1HRDMi8RDc79m0HISf9D4TzseP40cEA6IGfs= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -355,7 +326,6 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -363,33 +333,27 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 h1:5/PjkGUjvEU5Gl6BxmvKRPpqo2uNMv4rcHBMwzk/st8= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -442,7 +406,6 @@ google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -475,46 +438,29 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/api v0.18.1 h1:pnHr0LH69kvL29eHldoepUDKTuiOejNZI2A1gaxve3Q= -k8s.io/api v0.18.1/go.mod h1:3My4jorQWzSs5a+l7Ge6JBbIxChLnY8HnuT58ZWolss= k8s.io/api v0.19.3 h1:GN6ntFnv44Vptj/b+OnMW7FmzkpDoIDLZRvKX3XH9aU= k8s.io/api v0.19.3/go.mod h1:VF+5FT1B74Pw3KxMdKyinLo+zynBaMBiAfGMuldcNDs= -k8s.io/apimachinery v0.18.1/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= -k8s.io/apimachinery v0.19.2 h1:5Gy9vQpAGTKHPVOh5c4plE274X8D/6cuEiTO2zve7tc= -k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= k8s.io/apimachinery v0.19.3 h1:bpIQXlKjB4cB/oNpnNnV+BybGPR7iP5oYpsOTEJ4hgc= k8s.io/apimachinery v0.19.3/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= -k8s.io/client-go v0.18.1 h1:2+fnu4LwKJjZVOwijkm1UqZG9aQoFsKEpipOzdfcTD8= -k8s.io/client-go v0.18.1/go.mod h1:iCikYRiXOj/yRRFE/aWqrpPtDt4P2JVWhtHkmESTcfY= k8s.io/client-go v0.19.3 h1:ctqR1nQ52NUs6LpI0w+a5U+xjYwflFwA13OJKcicMxg= k8s.io/client-go v0.19.3/go.mod h1:+eEMktZM+MG0KO+PTkci8xnbCZHvj9TqR6Q1XDUIJOM= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= -k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20201027101359-01387209bb0d h1:1qqs/6lQQGCeZhCu0tO7La4lAazDXic6BiCmpjWcWUo= k8s.io/utils v0.0.0-20201027101359-01387209bb0d/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA= sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/handlers.go b/handlers.go index e8d88d3..e3ebb9f 100644 --- a/handlers.go +++ b/handlers.go @@ -33,6 +33,31 @@ var ( } ) +//CheckLimit ... +func (app *App) CheckLimit(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + logger := app.logger.WithFields(logrus.Fields{ + "request_id": uuid.New(), + "request": fmt.Sprintf("%s %s", r.Method, r.URL.Path), + }) + + l, err := app.client.List() + if err != nil { + logger.Errorf("failed to get active session list: %v", err) + tools.JSONError(w, "Failed to get browsers list", http.StatusInternalServerError) + return + } + + if len(l) >= app.sessionLimit { + logger.Warnf("active session limit reached: total %d, limit %d", len(l), app.sessionLimit) + tools.JSONError(w, "session limit reached", http.StatusInternalServerError) + return + } + + next.ServeHTTP(w, r) + } +} + //HandleSession ... func (app *App) HandleSession(w http.ResponseWriter, r *http.Request) { start := time.Now() @@ -40,20 +65,6 @@ func (app *App) HandleSession(w http.ResponseWriter, r *http.Request) { "request_id": uuid.New(), "request": fmt.Sprintf("%s %s", r.Method, r.URL.Path), }) - - l, err := app.client.List() - if err != nil { - logger.Errorf("failed to get active session list: %v", err) - tools.JSONError(w, "Failed to get browsers list", http.StatusInternalServerError) - return - } - - if len(l) >= app.sessionLimit { - logger.Warnf("active session limit reached: total %d, limit %d", len(l), app.sessionLimit) - tools.JSONError(w, "session limit reached", http.StatusInternalServerError) - return - } - logger.WithField("time_elapsed", tools.TimeElapsed(start)).Info("session") body, err := ioutil.ReadAll(r.Body) diff --git a/handlers_test.go b/handlers_test.go index 14521dc..afa32f8 100644 --- a/handlers_test.go +++ b/handlers_test.go @@ -58,11 +58,6 @@ func TestNewSessionRequestErrors(t *testing.T) { respCode: http.StatusBadRequest, respBody: `{"code":400,"value":{"message":"unknown browser name amigo"}}`, }, - "Verify new session call with unknown browser version in request": { - body: bytes.NewReader([]byte(`{"capabilities":{"firstMatch":[{"browserName":"chrome", "browserVersion":"0.00"}]}}`)), - respCode: http.StatusBadRequest, - respBody: `{"code":400,"value":{"message":"unknown browser version 0.00"}}`, - }, } for name, test := range tests { diff --git a/platform/kubernetes.go b/platform/kubernetes.go index 212c5c1..ba0dc3e 100644 --- a/platform/kubernetes.go +++ b/platform/kubernetes.go @@ -2,6 +2,7 @@ package platform import ( "context" + "errors" "fmt" "io" "net" @@ -13,8 +14,8 @@ import ( "github.com/alcounit/selenosis/tools" apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" v1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/rest" @@ -250,37 +251,26 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { cl.Delete(podName) } - var status apiv1.PodStatus - w, err := cl.clientset.Pods(cl.ns).Watch(context, metav1.ListOptions{ - FieldSelector: fields.OneTermEqualSelector("metadata.name", podName).String(), - }) + statusFn := func() (bool, error) { + pod, err := cl.clientset.Pods(cl.ns).Get(context, podName, metav1.GetOptions{}) + if err != nil { + return false, err + } - if err != nil { - cancel() - return nil, fmt.Errorf("watch pod: %v", err) + switch pod.Status.Phase { + case apiv1.PodRunning: + return true, nil + case apiv1.PodFailed, apiv1.PodSucceeded: + return false, errors.New("pod not ready") + } + return false, nil } - func() { - for { - select { - case events, ok := <-w.ResultChan(): - if !ok { - return - } - pod = events.Object.(*apiv1.Pod) - status = pod.Status - if pod.Status.Phase != apiv1.PodPending { - w.Stop() - } - case <-time.After(cl.iddleTimeout): - w.Stop() - } - } - }() + err = wait.PollImmediate(time.Second, cl.iddleTimeout, statusFn) - if status.Phase != apiv1.PodRunning { + if err != nil { cancel() - return nil, fmt.Errorf("pod status: %v", status.Phase) + return nil, errors.New("pod never entered running phase") } host := fmt.Sprintf("%s.%s", podName, cl.svc) @@ -289,7 +279,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { Host: net.JoinHostPort(host, browserPorts.selenium.StrVal), } - if err := waitForService(u, cl.readinessTimeout); err != nil { + if err := waitForService(*u, cl.readinessTimeout); err != nil { cancel() return nil, fmt.Errorf("container service is not ready %v", u.String()) } @@ -409,9 +399,10 @@ func getImagePullSecretList(secret string) []apiv1.LocalObjectReference { return refList } -func waitForService(u *url.URL, t time.Duration) error { +func waitForService(u url.URL, t time.Duration) error { up := make(chan struct{}) done := make(chan struct{}) + u.Path = "/status" go func() { for { select { @@ -419,7 +410,8 @@ func waitForService(u *url.URL, t time.Duration) error { return default: } - req, _ := http.NewRequest(http.MethodHead, u.String(), nil) + + req, _ := http.NewRequest(http.MethodGet, u.String(), nil) req.Close = true resp, err := http.DefaultClient.Do(req) if resp != nil { @@ -436,7 +428,7 @@ func waitForService(u *url.URL, t time.Duration) error { select { case <-time.After(t): close(done) - return fmt.Errorf("%s does not respond in %v", u, t) + return fmt.Errorf("no responce after %v", t) case <-up: } return nil diff --git a/selenium/selenium.go b/selenium/selenium.go index 9c5e902..1292db5 100644 --- a/selenium/selenium.go +++ b/selenium/selenium.go @@ -28,8 +28,6 @@ type Capabilities struct { DNSServers []string `json:"dnsServers,omitempty"` Labels map[string]string `json:"labels,omitempty"` SessionTimeout string `json:"sessionTimeout,omitempty"` - S3KeyPattern string `json:"s3KeyPattern,omitempty"` - ExtensionCapabilities *Capabilities `json:"selenoid:options,omitempty"` } //ValidateCapabilities ... diff --git a/selenosis.go b/selenosis.go index 57165a0..1cc2355 100644 --- a/selenosis.go +++ b/selenosis.go @@ -34,17 +34,17 @@ type App struct { } //New ... -func New(logger *log.Logger, client platform.Platform, browsers *config.BrowsersConfig, settings Configuration) *App { +func New(logger *log.Logger, client platform.Platform, browsers *config.BrowsersConfig, cfg Configuration) *App { return &App{ logger: logger, client: client, browsers: browsers, - selenosisHost: settings.SelenosisHost, - serviceName: settings.ServiceName, - sidecarPort: settings.SidecarPort, - sessionLimit: settings.SessionLimit, - sessionRetryCount: settings.SessionRetryCount, - browserWaitTimeout: settings.BrowserWaitTimeout, - sessionIddleTimeout: settings.SessionIddleTimeout, + selenosisHost: cfg.SelenosisHost, + serviceName: cfg.ServiceName, + sidecarPort: cfg.SidecarPort, + sessionLimit: cfg.SessionLimit, + sessionRetryCount: cfg.SessionRetryCount, + browserWaitTimeout: cfg.BrowserWaitTimeout, + sessionIddleTimeout: cfg.SessionIddleTimeout, } } From f79fb55263ea12c38a28d05a1d657f738b20404a Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Sun, 13 Dec 2020 22:56:49 +0200 Subject: [PATCH 10/22] Readme update --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 8707899..34b6762 100644 --- a/README.md +++ b/README.md @@ -443,3 +443,6 @@ kubectl edit configmap -n selenosis selenosis-config -o yaml ### UI for debug Selenosis itself doesn't have ui. If you need such functionality you can use [selenoid-ui](https://github.com/aerokube/selenoid-ui) with special [adapter container](https://github.com/alcounit/adaptee). Deployment steps and minifests you can find in [selenosis-deploy](https://github.com/alcounit/selenosis-deploy) repository. + +
+Currently this project is under development and can be unstable, in case of any bugs or ideas please report From 698c89fe9215b3f7159bc51c8018686718dc6192 Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Sun, 20 Dec 2020 01:03:20 +0200 Subject: [PATCH 11/22] Change pod readiness logic back to watch events --- README.md | 11 ++++++++ go.mod | 1 + go.sum | 4 +++ platform/kubernetes.go | 57 +++++++++++++++++++++++++++++++----------- 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 8707899..40201cb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Docker Pulls](https://img.shields.io/docker/pulls/alcounit/selenosis)](https://hub.docker.com/r/alcounit/selenosis/tags?page=1&ordering=last_updated) # selenosis Scalable, stateless selenium hub for Kubernetes cluster. @@ -430,6 +431,12 @@ spec: selector: ... ``` +
+by using kubectl + +```bash +kubectl scale deployment selenosis -n selenosis --replicas=3 +``` ### Stateless Selenosis doesn't store any session info. All connections to the browsers are automatically assigned via headless service. @@ -443,3 +450,7 @@ kubectl edit configmap -n selenosis selenosis-config -o yaml ### UI for debug Selenosis itself doesn't have ui. If you need such functionality you can use [selenoid-ui](https://github.com/aerokube/selenoid-ui) with special [adapter container](https://github.com/alcounit/adaptee). Deployment steps and minifests you can find in [selenosis-deploy](https://github.com/alcounit/selenosis-deploy) repository. + + +
+This project is under development and can be unstable, in case of any bugs or ideas please report diff --git a/go.mod b/go.mod index 231b7a2..8cc1369 100644 --- a/go.mod +++ b/go.mod @@ -15,5 +15,6 @@ require ( k8s.io/api v0.19.3 k8s.io/apimachinery v0.19.3 k8s.io/client-go v0.19.3 + k8s.io/kubernetes v0.19.3 // indirect k8s.io/utils v0.0.0-20201027101359-01387209bb0d ) diff --git a/go.sum b/go.sum index 6f7ab9e..4bf9fac 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,7 @@ github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6L github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/GoogleCloudPlatform/kubernetes v1.20.1 h1:yOtsBn+WIN5mbRRaMvGPuiNtO1ctwCKFaBGYI6QBSE4= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -85,6 +86,7 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -457,6 +459,8 @@ k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= +k8s.io/kubernetes v0.19.3 h1:x6Q6M9nNBm9thoKj+PJr3HDPfHlz7FR35Ri61xpNfgg= +k8s.io/kubernetes v0.19.3/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20201027101359-01387209bb0d h1:1qqs/6lQQGCeZhCu0tO7La4lAazDXic6BiCmpjWcWUo= k8s.io/utils v0.0.0-20201027101359-01387209bb0d/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= diff --git a/platform/kubernetes.go b/platform/kubernetes.go index ba0dc3e..cd7c745 100644 --- a/platform/kubernetes.go +++ b/platform/kubernetes.go @@ -15,10 +15,11 @@ import ( apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/kubernetes" v1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/rest" + "k8s.io/kubernetes/pkg/fields" "k8s.io/utils/pointer" ) @@ -251,26 +252,52 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { cl.Delete(podName) } - statusFn := func() (bool, error) { - pod, err := cl.clientset.Pods(cl.ns).Get(context, podName, metav1.GetOptions{}) - if err != nil { - return false, err - } + w, err := cl.clientset.Pods(cl.ns).Watch(context, metav1.ListOptions{ + FieldSelector: fields.OneTermEqualSelector("metadata.name", podName).String(), + TimeoutSeconds: pointer.Int64Ptr(cl.readinessTimeout.Milliseconds()), + }) - switch pod.Status.Phase { - case apiv1.PodRunning: - return true, nil - case apiv1.PodFailed, apiv1.PodSucceeded: - return false, errors.New("pod not ready") - } - return false, nil + if err != nil { + return nil, fmt.Errorf("failed to watch pod status: %v", err) } - err = wait.PollImmediate(time.Second, cl.iddleTimeout, statusFn) + ready := func() error { + defer w.Stop() + var watchedPod *apiv1.Pod + + for event := range w.ResultChan() { + switch event.Type { + case watch.Error: + return fmt.Errorf("received error while watching pod: %s", + event.Object.GetObjectKind().GroupVersionKind().String()) + case watch.Deleted, watch.Added, watch.Modified: + watchedPod = event.Object.(*apiv1.Pod) + default: + return fmt.Errorf("received unknown event type %s while watching pod", event.Type) + } + if event.Type == watch.Deleted { + return errors.New("pod was deleted before becoming available") + } + switch watchedPod.Status.Phase { + case apiv1.PodPending: + continue + case apiv1.PodSucceeded, apiv1.PodFailed: + return fmt.Errorf("pod exited early with status %s", watchedPod.Status.Phase) + case apiv1.PodRunning: + return nil + case apiv1.PodUnknown: + return errors.New("couldn't obtain pod state") + default: + return errors.New("pod has unknown status") + } + } + return fmt.Errorf("pod wasn't running") + } + err = ready() if err != nil { cancel() - return nil, errors.New("pod never entered running phase") + return nil, fmt.Errorf("failed to create pod: %v", err) } host := fmt.Sprintf("%s.%s", podName, cl.svc) From 294823febb3246b65be1b70edf69209abaf28f09 Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Wed, 23 Dec 2020 10:25:02 +0200 Subject: [PATCH 12/22] Update README with instructions how to deal with coredns config if pods are failed on start --- README.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 10 +++++++++- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 40201cb..164f930 100644 --- a/README.md +++ b/README.md @@ -416,6 +416,57 @@ driver = webdriver.Remote( Note: you can omit browser version in your desired capabilities, make sure you set defaultVersion property in the config file. + +## Browser pods are deleted right after start +Depends on you cluster version in some cases you can face with issue when some browser pods are deleted right after their start and selenosis log will contains lines like this: +```log +time="2020-12-21T10:28:20Z" level=error msg="session failed: Post \"http://selenoid-vnc-chrome-87-0-af3177a0-5052-45be-b4e4-9462146e4633.seleniferous:4445/wd/hub/session\": dial tcp: lookup selenoid-vnc-chrome-87-0-af3177a0-5052-45be-b4e4-9462146e4633.seleniferous on 10.96.0.10:53: no such host" request="POST /wd/hub/session" request_id=fa150040-86c1-4224-9e5c-21416b1d9f5c time_elapsed=5.73s +``` +To fix this issue do the following: +```bash +kubectl edit cm coredns -n kube-system +``` +add following record to your coredns config +```config + selenosis.svc.cluster.local:53 { + errors + kubernetes cluster.local { + namespaces selenosis + } + } +``` +this option will turn off dns caching for selenosis namespace, resulting config update should be as following: +```yaml +apiVersion: v1 +data: + Corefile: | + selenosis.svc.cluster.local:53 { + errors + kubernetes cluster.local { + namespaces selenosis + } + } + .:53 { + errors + health + ready + kubernetes cluster.local in-addr.arpa ip6.arpa { + pods insecure + fallthrough in-addr.arpa ip6.arpa + } + prometheus :9153 + forward . /etc/resolv.conf + cache 30 + loop + reload + loadbalance + import custom/*.override + } + import custom/*.server +kind: ConfigMap +... +``` + ## Features ### Scalability By default selenosis starts with 2 replica sets. To change it, edit selenosis deployment file: 03-selenosis.yaml diff --git a/go.mod b/go.mod index 8cc1369..61e98e5 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,6 @@ require ( k8s.io/api v0.19.3 k8s.io/apimachinery v0.19.3 k8s.io/client-go v0.19.3 - k8s.io/kubernetes v0.19.3 // indirect + k8s.io/kubernetes v0.19.3 k8s.io/utils v0.0.0-20201027101359-01387209bb0d ) diff --git a/go.sum b/go.sum index 4bf9fac..6c80c66 100644 --- a/go.sum +++ b/go.sum @@ -25,7 +25,6 @@ github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6L github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/GoogleCloudPlatform/kubernetes v1.20.1 h1:yOtsBn+WIN5mbRRaMvGPuiNtO1ctwCKFaBGYI6QBSE4= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -161,6 +160,7 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -176,8 +176,10 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -215,6 +217,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -256,6 +259,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -383,6 +387,7 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -394,6 +399,7 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -425,6 +431,7 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= @@ -440,6 +447,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= From 882c020451c1d3c943f3480019801785dd43c40d Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Wed, 6 Jan 2021 20:58:39 +0200 Subject: [PATCH 13/22] Added support for session local storage --- go.sum | 2 + handlers.go | 70 +++++++++++---------------- handlers_test.go | 5 ++ platform/kubernetes.go | 107 +++++++++++++++++++++++++++++++++-------- platform/platform.go | 25 +++++++++- selenosis.go | 34 +++++++++++++ storage/storage.go | 56 +++++++++++++++++++++ 7 files changed, 236 insertions(+), 63 deletions(-) create mode 100644 storage/storage.go diff --git a/go.sum b/go.sum index 6c80c66..2171596 100644 --- a/go.sum +++ b/go.sum @@ -89,6 +89,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -149,6 +150,7 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= diff --git a/handlers.go b/handlers.go index e3ebb9f..7bb29b5 100644 --- a/handlers.go +++ b/handlers.go @@ -41,15 +41,10 @@ func (app *App) CheckLimit(next http.HandlerFunc) http.HandlerFunc { "request": fmt.Sprintf("%s %s", r.Method, r.URL.Path), }) - l, err := app.client.List() - if err != nil { - logger.Errorf("failed to get active session list: %v", err) - tools.JSONError(w, "Failed to get browsers list", http.StatusInternalServerError) - return - } + total := app.stats.Len() - if len(l) >= app.sessionLimit { - logger.Warnf("active session limit reached: total %d, limit %d", len(l), app.sessionLimit) + if total >= app.sessionLimit { + logger.Warnf("active session limit reached: total %d, limit %d", total, app.sessionLimit) tools.JSONError(w, "session limit reached", http.StatusInternalServerError) return } @@ -243,21 +238,18 @@ func (app *App) HadleHubStatus(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - l, err := app.client.List() - if err != nil { - logger.Errorf("hub status: %v", err) - tools.JSONError(w, "Failed to get browsers list", http.StatusInternalServerError) - } + active, pending := getSessionStats(app.stats.List()) + total := len(active) + len(pending) json.NewEncoder(w).Encode( map[string]interface{}{ "value": map[string]interface{}{ "message": "selenosis up and running", - "ready": len(l), + "ready": total, }, }) - logger.WithField("active_sessions", len(l)).Infof("hub status") + logger.WithField("active_sessions", total).Infof("hub status") } //HandleReverseProxy ... @@ -373,7 +365,7 @@ func (app *App) HandleStatus(w http.ResponseWriter, r *http.Request) { Active int `json:"active"` Pending int `json:"pending"` Browsers map[string][]string `json:"config,omitempty"` - Sessions []*platform.Service `json:"sessions,omitempty"` + Sessions []platform.Service `json:"sessions,omitempty"` } type Response struct { @@ -382,40 +374,17 @@ func (app *App) HandleStatus(w http.ResponseWriter, r *http.Request) { Selenosis Status `json:"selenosis,omitempty"` } - sessions, err := app.client.List() - - if err != nil { - app.logger.Errorf("hub status: %v", err) - json.NewEncoder(w).Encode( - Response{ - Status: http.StatusInternalServerError, - Error: fmt.Sprintf("%v", err), - Selenosis: Status{ - Total: app.sessionLimit, - Browsers: app.browsers.GetBrowserVersions(), - }, - }, - ) - return - } - - ready := make([]*platform.Service, 0) - - for _, s := range sessions { - if s.Ready { - ready = append(ready, s) - } - } + active, pending := getSessionStats(app.stats.List()) json.NewEncoder(w).Encode( Response{ Status: http.StatusOK, Selenosis: Status{ Total: app.sessionLimit, - Active: len(ready), - Pending: len(sessions) - len(ready), + Active: len(active), + Pending: len(pending), Browsers: app.browsers.GetBrowserVersions(), - Sessions: ready, + Sessions: active, }, }, ) @@ -444,3 +413,18 @@ func statusOk(session, service, port string) bool { return true } + +func getSessionStats(sessions []platform.Service) (active []platform.Service, pending []platform.Service) { + active = make([]platform.Service, 0) + pending = make([]platform.Service, 0) + + for _, s := range sessions { + switch s.Status { + case platform.Running: + active = append(active, s) + case platform.Pending: + pending = append(pending, s) + } + } + return +} diff --git a/handlers_test.go b/handlers_test.go index afa32f8..5a07891 100644 --- a/handlers_test.go +++ b/handlers_test.go @@ -569,6 +569,11 @@ func (p *PlatformMock) List() ([]*platform.Service, error) { return nil, nil } +func (p *PlatformMock) Watch() <-chan platform.Event { + ch := make(chan platform.Event) + return ch +} + func (p *PlatformMock) Logs(ctx context.Context, name string) (io.ReadCloser, error) { return nil, nil } diff --git a/platform/kubernetes.go b/platform/kubernetes.go index cd7c745..2775252 100644 --- a/platform/kubernetes.go +++ b/platform/kubernetes.go @@ -16,9 +16,10 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" - v1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" "k8s.io/kubernetes/pkg/fields" "k8s.io/utils/pointer" ) @@ -65,7 +66,7 @@ type Client struct { proxyImage string readinessTimeout time.Duration iddleTimeout time.Duration - clientset v1.CoreV1Interface + clientset *kubernetes.Clientset } //NewClient ... @@ -83,7 +84,7 @@ func NewClient(c ClientConfig) (Platform, error) { return &Client{ ns: c.Namespace, - clientset: clientset.CoreV1(), + clientset: clientset, svc: c.Service, svcPort: intstr.FromString(c.ServicePort), imagePullSecretName: c.ImagePullSecretName, @@ -109,7 +110,7 @@ func NewDefaultClient(namespace string) (Platform, error) { return &Client{ ns: namespace, - clientset: clientset.CoreV1(), + clientset: clientset, }, nil } @@ -241,7 +242,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { } context := context.Background() - pod, err := cl.clientset.Pods(cl.ns).Create(context, pod, metav1.CreateOptions{}) + pod, err := cl.clientset.CoreV1().Pods(cl.ns).Create(context, pod, metav1.CreateOptions{}) if err != nil { return nil, fmt.Errorf("failed to create pod %v", err) @@ -252,7 +253,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { cl.Delete(podName) } - w, err := cl.clientset.Pods(cl.ns).Watch(context, metav1.ListOptions{ + w, err := cl.clientset.CoreV1().Pods(cl.ns).Watch(context, metav1.ListOptions{ FieldSelector: fields.OneTermEqualSelector("metadata.name", podName).String(), TimeoutSeconds: pointer.Int64Ptr(cl.readinessTimeout.Milliseconds()), }) @@ -261,7 +262,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { return nil, fmt.Errorf("failed to watch pod status: %v", err) } - ready := func() error { + statusFn := func() error { defer w.Stop() var watchedPod *apiv1.Pod @@ -294,8 +295,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { return fmt.Errorf("pod wasn't running") } - err = ready() - if err != nil { + if statusFn() != nil { cancel() return nil, fmt.Errorf("failed to create pod: %v", err) } @@ -330,7 +330,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { func (cl *Client) Delete(name string) error { context := context.Background() - return cl.clientset.Pods(cl.ns).Delete(context, name, metav1.DeleteOptions{ + return cl.clientset.CoreV1().Pods(cl.ns).Delete(context, name, metav1.DeleteOptions{ GracePeriodSeconds: pointer.Int64Ptr(15), }) } @@ -338,7 +338,7 @@ func (cl *Client) Delete(name string) error { //List ... func (cl *Client) List() ([]*Service, error) { context := context.Background() - pods, err := cl.clientset.Pods(cl.ns).List(context, metav1.ListOptions{ + pods, err := cl.clientset.CoreV1().Pods(cl.ns).List(context, metav1.ListOptions{ LabelSelector: "type=browser", }) @@ -352,17 +352,17 @@ func (cl *Client) List() ([]*Service, error) { podName := pod.GetName() host := fmt.Sprintf("%s.%s", podName, cl.svc) - var ready bool + var status ServiceStatus switch pod.Status.Phase { case apiv1.PodRunning: - ready = true + status = Running case apiv1.PodPending: - ready = false + status = Pending default: - continue + status = Unknown } - s := &Service{ + service := &Service{ SessionID: podName, URL: &url.URL{ Scheme: "http", @@ -372,20 +372,89 @@ func (cl *Client) List() ([]*Service, error) { CancelFunc: func() { cl.Delete(podName) }, - Ready: ready, + Status: status, Started: pod.CreationTimestamp.Time, Uptime: tools.TimeElapsed(pod.CreationTimestamp.Time), } - services = append(services, s) + services = append(services, service) } return services, nil } +//Watch ... +func (cl Client) Watch() <-chan Event { + ch := make(chan Event) + + convert := func(obj interface{}) *Service { + pod := obj.(*apiv1.Pod) + podName := pod.GetName() + host := fmt.Sprintf("%s.%s", podName, cl.svc) + + var status ServiceStatus + switch pod.Status.Phase { + case apiv1.PodRunning: + status = Running + case apiv1.PodPending: + status = Pending + default: + status = Unknown + } + + return &Service{ + SessionID: podName, + URL: &url.URL{ + Scheme: "http", + Host: net.JoinHostPort(host, cl.svcPort.StrVal), + }, + Labels: pod.GetLabels(), + CancelFunc: func() { + cl.Delete(podName) + }, + Status: status, + Started: pod.CreationTimestamp.Time, + Uptime: tools.TimeElapsed(pod.CreationTimestamp.Time), + } + } + + namespace := informers.WithNamespace(cl.ns) + labels := informers.WithTweakListOptions(func(list *metav1.ListOptions) { + list.LabelSelector = "type=browser" + }) + + sharedIformer := informers.NewSharedInformerFactoryWithOptions(cl.clientset, 30*time.Second, namespace, labels) + sharedIformer.Core().V1().Pods().Informer().AddEventHandler( + cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + ch <- Event{ + Type: Added, + Service: convert(obj), + } + }, + UpdateFunc: func(old interface{}, new interface{}) { + ch <- Event{ + Type: Updated, + Service: convert(new), + } + }, + DeleteFunc: func(obj interface{}) { + ch <- Event{ + Type: Deleted, + Service: convert(obj), + } + }, + }, + ) + + var neverStop <-chan struct{} = make(chan struct{}) + sharedIformer.Start(neverStop) + return ch +} + //Logs ... func (cl *Client) Logs(ctx context.Context, name string) (io.ReadCloser, error) { - req := cl.clientset.Pods(cl.ns).GetLogs(name, &apiv1.PodLogOptions{ + req := cl.clientset.CoreV1().Pods(cl.ns).GetLogs(name, &apiv1.PodLogOptions{ Container: "browser", Follow: true, Previous: false, diff --git a/platform/platform.go b/platform/platform.go index 907a082..37adac1 100644 --- a/platform/platform.go +++ b/platform/platform.go @@ -50,15 +50,38 @@ type Service struct { Labels map[string]string `json:"labels"` OnTimeout chan struct{} `json:"-"` CancelFunc func() `json:"-"` - Ready bool `json:"-"` + Status ServiceStatus `json:"-"` Started time.Time `json:"started"` Uptime string `json:"uptime"` } +//ServiceStatus ... +type ServiceStatus string + +//Event ... +type Event struct { + Type EventType + Service *Service +} + +//EventType ... +type EventType string + +const ( + Added EventType = "Added" + Updated EventType = "Updated" + Deleted EventType = "Deleted" + + Pending ServiceStatus = "Pending" + Running ServiceStatus = "Running" + Unknown ServiceStatus = "Unknown" +) + //Platform ... type Platform interface { Create(*ServiceSpec) (*Service, error) Delete(string) error List() ([]*Service, error) + Watch() <-chan Event Logs(context.Context, string) (io.ReadCloser, error) } diff --git a/selenosis.go b/selenosis.go index 1cc2355..a7b421e 100644 --- a/selenosis.go +++ b/selenosis.go @@ -5,6 +5,7 @@ import ( "github.com/alcounit/selenosis/config" "github.com/alcounit/selenosis/platform" + "github.com/alcounit/selenosis/storage" log "github.com/sirupsen/logrus" ) @@ -31,10 +32,42 @@ type App struct { sessionRetryCount int sessionIddleTimeout time.Duration browserWaitTimeout time.Duration + stats *storage.Storage } //New ... func New(logger *log.Logger, client platform.Platform, browsers *config.BrowsersConfig, cfg Configuration) *App { + + storage := storage.New() + + services, err := client.List() + if err != nil { + logger.Errorf("failed to get list of active pods: %v", err) + } + + for _, service := range services { + storage.Put(service.SessionID, service) + } + + ch := client.Watch() + go func() { + for { + select { + case event := <-ch: + switch event.Type { + case platform.Added: + storage.Put(event.Service.SessionID, event.Service) + case platform.Updated: + storage.Put(event.Service.SessionID, event.Service) + case platform.Deleted: + storage.Delete(event.Service.SessionID) + } + default: + break + } + } + }() + return &App{ logger: logger, client: client, @@ -46,5 +79,6 @@ func New(logger *log.Logger, client platform.Platform, browsers *config.Browsers sessionRetryCount: cfg.SessionRetryCount, browserWaitTimeout: cfg.BrowserWaitTimeout, sessionIddleTimeout: cfg.SessionIddleTimeout, + stats: storage, } } diff --git a/storage/storage.go b/storage/storage.go new file mode 100644 index 0000000..4a409fd --- /dev/null +++ b/storage/storage.go @@ -0,0 +1,56 @@ +package storage + +import ( + "sync" + + "github.com/alcounit/selenosis/platform" +) + +//Storage ... +type Storage struct { + sessions map[string]*platform.Service + sync.RWMutex +} + +//New ... +func New() *Storage { + return &Storage{ + sessions: make(map[string]*platform.Service), + } +} + +//Put ... +func (s *Storage) Put(sessionID string, service *platform.Service) { + s.Lock() + defer s.Unlock() + s.sessions[sessionID] = service +} + +//Delete ... +func (s *Storage) Delete(sessionID string) { + s.Lock() + defer s.Unlock() + delete(s.sessions, sessionID) +} + +//List ... +func (s *Storage) List() []platform.Service { + s.Lock() + defer s.Unlock() + var l []platform.Service + + for _, p := range s.sessions { + c := *p + l = append(l, c) + } + return l + +} + +//Len ... +func (s *Storage) Len() int { + s.Lock() + defer s.Unlock() + + return len(s.sessions) +} From 6a067b2ae7d5bd4749b24b8b02db1c9d03f3f5af Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Wed, 6 Jan 2021 20:58:39 +0200 Subject: [PATCH 14/22] Added support for session local storage --- go.sum | 2 + handlers.go | 70 +++++++++++---------------- handlers_test.go | 5 ++ platform/kubernetes.go | 107 +++++++++++++++++++++++++++++++++-------- platform/platform.go | 25 +++++++++- selenosis.go | 34 +++++++++++++ storage/storage.go | 56 +++++++++++++++++++++ 7 files changed, 236 insertions(+), 63 deletions(-) create mode 100644 storage/storage.go diff --git a/go.sum b/go.sum index 6c80c66..2171596 100644 --- a/go.sum +++ b/go.sum @@ -89,6 +89,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -149,6 +150,7 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= diff --git a/handlers.go b/handlers.go index e3ebb9f..7bb29b5 100644 --- a/handlers.go +++ b/handlers.go @@ -41,15 +41,10 @@ func (app *App) CheckLimit(next http.HandlerFunc) http.HandlerFunc { "request": fmt.Sprintf("%s %s", r.Method, r.URL.Path), }) - l, err := app.client.List() - if err != nil { - logger.Errorf("failed to get active session list: %v", err) - tools.JSONError(w, "Failed to get browsers list", http.StatusInternalServerError) - return - } + total := app.stats.Len() - if len(l) >= app.sessionLimit { - logger.Warnf("active session limit reached: total %d, limit %d", len(l), app.sessionLimit) + if total >= app.sessionLimit { + logger.Warnf("active session limit reached: total %d, limit %d", total, app.sessionLimit) tools.JSONError(w, "session limit reached", http.StatusInternalServerError) return } @@ -243,21 +238,18 @@ func (app *App) HadleHubStatus(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - l, err := app.client.List() - if err != nil { - logger.Errorf("hub status: %v", err) - tools.JSONError(w, "Failed to get browsers list", http.StatusInternalServerError) - } + active, pending := getSessionStats(app.stats.List()) + total := len(active) + len(pending) json.NewEncoder(w).Encode( map[string]interface{}{ "value": map[string]interface{}{ "message": "selenosis up and running", - "ready": len(l), + "ready": total, }, }) - logger.WithField("active_sessions", len(l)).Infof("hub status") + logger.WithField("active_sessions", total).Infof("hub status") } //HandleReverseProxy ... @@ -373,7 +365,7 @@ func (app *App) HandleStatus(w http.ResponseWriter, r *http.Request) { Active int `json:"active"` Pending int `json:"pending"` Browsers map[string][]string `json:"config,omitempty"` - Sessions []*platform.Service `json:"sessions,omitempty"` + Sessions []platform.Service `json:"sessions,omitempty"` } type Response struct { @@ -382,40 +374,17 @@ func (app *App) HandleStatus(w http.ResponseWriter, r *http.Request) { Selenosis Status `json:"selenosis,omitempty"` } - sessions, err := app.client.List() - - if err != nil { - app.logger.Errorf("hub status: %v", err) - json.NewEncoder(w).Encode( - Response{ - Status: http.StatusInternalServerError, - Error: fmt.Sprintf("%v", err), - Selenosis: Status{ - Total: app.sessionLimit, - Browsers: app.browsers.GetBrowserVersions(), - }, - }, - ) - return - } - - ready := make([]*platform.Service, 0) - - for _, s := range sessions { - if s.Ready { - ready = append(ready, s) - } - } + active, pending := getSessionStats(app.stats.List()) json.NewEncoder(w).Encode( Response{ Status: http.StatusOK, Selenosis: Status{ Total: app.sessionLimit, - Active: len(ready), - Pending: len(sessions) - len(ready), + Active: len(active), + Pending: len(pending), Browsers: app.browsers.GetBrowserVersions(), - Sessions: ready, + Sessions: active, }, }, ) @@ -444,3 +413,18 @@ func statusOk(session, service, port string) bool { return true } + +func getSessionStats(sessions []platform.Service) (active []platform.Service, pending []platform.Service) { + active = make([]platform.Service, 0) + pending = make([]platform.Service, 0) + + for _, s := range sessions { + switch s.Status { + case platform.Running: + active = append(active, s) + case platform.Pending: + pending = append(pending, s) + } + } + return +} diff --git a/handlers_test.go b/handlers_test.go index afa32f8..5a07891 100644 --- a/handlers_test.go +++ b/handlers_test.go @@ -569,6 +569,11 @@ func (p *PlatformMock) List() ([]*platform.Service, error) { return nil, nil } +func (p *PlatformMock) Watch() <-chan platform.Event { + ch := make(chan platform.Event) + return ch +} + func (p *PlatformMock) Logs(ctx context.Context, name string) (io.ReadCloser, error) { return nil, nil } diff --git a/platform/kubernetes.go b/platform/kubernetes.go index cd7c745..2775252 100644 --- a/platform/kubernetes.go +++ b/platform/kubernetes.go @@ -16,9 +16,10 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" - v1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" "k8s.io/kubernetes/pkg/fields" "k8s.io/utils/pointer" ) @@ -65,7 +66,7 @@ type Client struct { proxyImage string readinessTimeout time.Duration iddleTimeout time.Duration - clientset v1.CoreV1Interface + clientset *kubernetes.Clientset } //NewClient ... @@ -83,7 +84,7 @@ func NewClient(c ClientConfig) (Platform, error) { return &Client{ ns: c.Namespace, - clientset: clientset.CoreV1(), + clientset: clientset, svc: c.Service, svcPort: intstr.FromString(c.ServicePort), imagePullSecretName: c.ImagePullSecretName, @@ -109,7 +110,7 @@ func NewDefaultClient(namespace string) (Platform, error) { return &Client{ ns: namespace, - clientset: clientset.CoreV1(), + clientset: clientset, }, nil } @@ -241,7 +242,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { } context := context.Background() - pod, err := cl.clientset.Pods(cl.ns).Create(context, pod, metav1.CreateOptions{}) + pod, err := cl.clientset.CoreV1().Pods(cl.ns).Create(context, pod, metav1.CreateOptions{}) if err != nil { return nil, fmt.Errorf("failed to create pod %v", err) @@ -252,7 +253,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { cl.Delete(podName) } - w, err := cl.clientset.Pods(cl.ns).Watch(context, metav1.ListOptions{ + w, err := cl.clientset.CoreV1().Pods(cl.ns).Watch(context, metav1.ListOptions{ FieldSelector: fields.OneTermEqualSelector("metadata.name", podName).String(), TimeoutSeconds: pointer.Int64Ptr(cl.readinessTimeout.Milliseconds()), }) @@ -261,7 +262,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { return nil, fmt.Errorf("failed to watch pod status: %v", err) } - ready := func() error { + statusFn := func() error { defer w.Stop() var watchedPod *apiv1.Pod @@ -294,8 +295,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { return fmt.Errorf("pod wasn't running") } - err = ready() - if err != nil { + if statusFn() != nil { cancel() return nil, fmt.Errorf("failed to create pod: %v", err) } @@ -330,7 +330,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { func (cl *Client) Delete(name string) error { context := context.Background() - return cl.clientset.Pods(cl.ns).Delete(context, name, metav1.DeleteOptions{ + return cl.clientset.CoreV1().Pods(cl.ns).Delete(context, name, metav1.DeleteOptions{ GracePeriodSeconds: pointer.Int64Ptr(15), }) } @@ -338,7 +338,7 @@ func (cl *Client) Delete(name string) error { //List ... func (cl *Client) List() ([]*Service, error) { context := context.Background() - pods, err := cl.clientset.Pods(cl.ns).List(context, metav1.ListOptions{ + pods, err := cl.clientset.CoreV1().Pods(cl.ns).List(context, metav1.ListOptions{ LabelSelector: "type=browser", }) @@ -352,17 +352,17 @@ func (cl *Client) List() ([]*Service, error) { podName := pod.GetName() host := fmt.Sprintf("%s.%s", podName, cl.svc) - var ready bool + var status ServiceStatus switch pod.Status.Phase { case apiv1.PodRunning: - ready = true + status = Running case apiv1.PodPending: - ready = false + status = Pending default: - continue + status = Unknown } - s := &Service{ + service := &Service{ SessionID: podName, URL: &url.URL{ Scheme: "http", @@ -372,20 +372,89 @@ func (cl *Client) List() ([]*Service, error) { CancelFunc: func() { cl.Delete(podName) }, - Ready: ready, + Status: status, Started: pod.CreationTimestamp.Time, Uptime: tools.TimeElapsed(pod.CreationTimestamp.Time), } - services = append(services, s) + services = append(services, service) } return services, nil } +//Watch ... +func (cl Client) Watch() <-chan Event { + ch := make(chan Event) + + convert := func(obj interface{}) *Service { + pod := obj.(*apiv1.Pod) + podName := pod.GetName() + host := fmt.Sprintf("%s.%s", podName, cl.svc) + + var status ServiceStatus + switch pod.Status.Phase { + case apiv1.PodRunning: + status = Running + case apiv1.PodPending: + status = Pending + default: + status = Unknown + } + + return &Service{ + SessionID: podName, + URL: &url.URL{ + Scheme: "http", + Host: net.JoinHostPort(host, cl.svcPort.StrVal), + }, + Labels: pod.GetLabels(), + CancelFunc: func() { + cl.Delete(podName) + }, + Status: status, + Started: pod.CreationTimestamp.Time, + Uptime: tools.TimeElapsed(pod.CreationTimestamp.Time), + } + } + + namespace := informers.WithNamespace(cl.ns) + labels := informers.WithTweakListOptions(func(list *metav1.ListOptions) { + list.LabelSelector = "type=browser" + }) + + sharedIformer := informers.NewSharedInformerFactoryWithOptions(cl.clientset, 30*time.Second, namespace, labels) + sharedIformer.Core().V1().Pods().Informer().AddEventHandler( + cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + ch <- Event{ + Type: Added, + Service: convert(obj), + } + }, + UpdateFunc: func(old interface{}, new interface{}) { + ch <- Event{ + Type: Updated, + Service: convert(new), + } + }, + DeleteFunc: func(obj interface{}) { + ch <- Event{ + Type: Deleted, + Service: convert(obj), + } + }, + }, + ) + + var neverStop <-chan struct{} = make(chan struct{}) + sharedIformer.Start(neverStop) + return ch +} + //Logs ... func (cl *Client) Logs(ctx context.Context, name string) (io.ReadCloser, error) { - req := cl.clientset.Pods(cl.ns).GetLogs(name, &apiv1.PodLogOptions{ + req := cl.clientset.CoreV1().Pods(cl.ns).GetLogs(name, &apiv1.PodLogOptions{ Container: "browser", Follow: true, Previous: false, diff --git a/platform/platform.go b/platform/platform.go index 907a082..37adac1 100644 --- a/platform/platform.go +++ b/platform/platform.go @@ -50,15 +50,38 @@ type Service struct { Labels map[string]string `json:"labels"` OnTimeout chan struct{} `json:"-"` CancelFunc func() `json:"-"` - Ready bool `json:"-"` + Status ServiceStatus `json:"-"` Started time.Time `json:"started"` Uptime string `json:"uptime"` } +//ServiceStatus ... +type ServiceStatus string + +//Event ... +type Event struct { + Type EventType + Service *Service +} + +//EventType ... +type EventType string + +const ( + Added EventType = "Added" + Updated EventType = "Updated" + Deleted EventType = "Deleted" + + Pending ServiceStatus = "Pending" + Running ServiceStatus = "Running" + Unknown ServiceStatus = "Unknown" +) + //Platform ... type Platform interface { Create(*ServiceSpec) (*Service, error) Delete(string) error List() ([]*Service, error) + Watch() <-chan Event Logs(context.Context, string) (io.ReadCloser, error) } diff --git a/selenosis.go b/selenosis.go index 1cc2355..a7b421e 100644 --- a/selenosis.go +++ b/selenosis.go @@ -5,6 +5,7 @@ import ( "github.com/alcounit/selenosis/config" "github.com/alcounit/selenosis/platform" + "github.com/alcounit/selenosis/storage" log "github.com/sirupsen/logrus" ) @@ -31,10 +32,42 @@ type App struct { sessionRetryCount int sessionIddleTimeout time.Duration browserWaitTimeout time.Duration + stats *storage.Storage } //New ... func New(logger *log.Logger, client platform.Platform, browsers *config.BrowsersConfig, cfg Configuration) *App { + + storage := storage.New() + + services, err := client.List() + if err != nil { + logger.Errorf("failed to get list of active pods: %v", err) + } + + for _, service := range services { + storage.Put(service.SessionID, service) + } + + ch := client.Watch() + go func() { + for { + select { + case event := <-ch: + switch event.Type { + case platform.Added: + storage.Put(event.Service.SessionID, event.Service) + case platform.Updated: + storage.Put(event.Service.SessionID, event.Service) + case platform.Deleted: + storage.Delete(event.Service.SessionID) + } + default: + break + } + } + }() + return &App{ logger: logger, client: client, @@ -46,5 +79,6 @@ func New(logger *log.Logger, client platform.Platform, browsers *config.Browsers sessionRetryCount: cfg.SessionRetryCount, browserWaitTimeout: cfg.BrowserWaitTimeout, sessionIddleTimeout: cfg.SessionIddleTimeout, + stats: storage, } } diff --git a/storage/storage.go b/storage/storage.go new file mode 100644 index 0000000..4a409fd --- /dev/null +++ b/storage/storage.go @@ -0,0 +1,56 @@ +package storage + +import ( + "sync" + + "github.com/alcounit/selenosis/platform" +) + +//Storage ... +type Storage struct { + sessions map[string]*platform.Service + sync.RWMutex +} + +//New ... +func New() *Storage { + return &Storage{ + sessions: make(map[string]*platform.Service), + } +} + +//Put ... +func (s *Storage) Put(sessionID string, service *platform.Service) { + s.Lock() + defer s.Unlock() + s.sessions[sessionID] = service +} + +//Delete ... +func (s *Storage) Delete(sessionID string) { + s.Lock() + defer s.Unlock() + delete(s.sessions, sessionID) +} + +//List ... +func (s *Storage) List() []platform.Service { + s.Lock() + defer s.Unlock() + var l []platform.Service + + for _, p := range s.sessions { + c := *p + l = append(l, c) + } + return l + +} + +//Len ... +func (s *Storage) Len() int { + s.Lock() + defer s.Unlock() + + return len(s.sessions) +} From 2730ad0da98cfb11461b682740416bafa9306e41 Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Thu, 11 Feb 2021 10:35:42 +0200 Subject: [PATCH 15/22] Fix typos, small improvements --- README.md | 2 +- cmd/selenosis/main.go | 22 ++++++++-------- handlers.go | 33 +++++++++++++++++++----- handlers_test.go | 12 ++++----- platform/kubernetes.go | 8 +++--- selenosis.go | 58 +++++++++++++++++++++--------------------- 6 files changed, 77 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 4fb71ee..793dd9f 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Flags: --service-name string kubernetes service name for browsers (default "seleniferous") --browser-wait-timeout duration time in seconds that a browser will be ready (default 30s) --session-wait-timeout duration time in seconds that a session will be ready (default 1m0s) - --session-iddle-timeout duration time in seconds that a session will iddle (default 5m0s) + --session-idle-timeout duration time in seconds that a session will idle (default 5m0s) --session-retry-count int session retry count (default 3) --graceful-shutdown-timeout duration time in seconds gracefull shutdown timeout (default 30s) --image-pull-secret-name string secret name to private registry diff --git a/cmd/selenosis/main.go b/cmd/selenosis/main.go index 2ead281..bead920 100644 --- a/cmd/selenosis/main.go +++ b/cmd/selenosis/main.go @@ -38,7 +38,7 @@ func command() *cobra.Command { limit int browserWaitTimeout time.Duration sessionWaitTimeout time.Duration - sessionIddleTimeout time.Duration + sessionIdleTimeout time.Duration shutdownTimeout time.Duration ) @@ -65,7 +65,7 @@ func command() *cobra.Command { Namespace: namespace, Service: service, ReadinessTimeout: browserWaitTimeout, - IddleTimeout: sessionIddleTimeout, + IdleTimeout: sessionIdleTimeout, ServicePort: proxyPort, ImagePullSecretName: imagePullSecretName, ProxyImage: proxyImage, @@ -80,19 +80,19 @@ func command() *cobra.Command { hostname, _ := os.Hostname() app := selenosis.New(logger, client, browsers, selenosis.Configuration{ - SelenosisHost: hostname, - ServiceName: service, - SidecarPort: proxyPort, - SessionLimit: limit, - SessionRetryCount: sessionRetryCount, - BrowserWaitTimeout: browserWaitTimeout, - SessionIddleTimeout: sessionIddleTimeout, + SelenosisHost: hostname, + ServiceName: service, + SidecarPort: proxyPort, + SessionLimit: limit, + SessionRetryCount: sessionRetryCount, + BrowserWaitTimeout: browserWaitTimeout, + SessionIdleTimeout: sessionIdleTimeout, }) router := mux.NewRouter() router.HandleFunc("/wd/hub/session", app.CheckLimit(app.HandleSession)).Methods(http.MethodPost) router.PathPrefix("/wd/hub/session/{sessionId}").HandlerFunc(app.HandleProxy) - router.HandleFunc("/wd/hub/status", app.HadleHubStatus).Methods(http.MethodGet) + router.HandleFunc("/wd/hub/status", app.HandleHubStatus).Methods(http.MethodGet) router.PathPrefix("/vnc/{sessionId}").Handler(websocket.Handler(app.HandleVNC())) router.PathPrefix("/logs/{sessionId}").Handler(websocket.Handler(app.HandleLogs())) router.PathPrefix("/devtools/{sessionId}").HandlerFunc(app.HandleReverseProxy) @@ -140,7 +140,7 @@ func command() *cobra.Command { cmd.Flags().StringVar(&service, "service-name", "seleniferous", "kubernetes service name for browsers") cmd.Flags().DurationVar(&browserWaitTimeout, "browser-wait-timeout", 30*time.Second, "time in seconds that a browser will be ready") cmd.Flags().DurationVar(&sessionWaitTimeout, "session-wait-timeout", 60*time.Second, "time in seconds that a session will be ready") - cmd.Flags().DurationVar(&sessionIddleTimeout, "session-iddle-timeout", 5*time.Minute, "time in seconds that a session will iddle") + cmd.Flags().DurationVar(&sessionIdleTimeout, "session-idle-timeout", 5*time.Minute, "time in seconds that a session will idle") cmd.Flags().IntVar(&sessionRetryCount, "session-retry-count", 3, "session retry count") cmd.Flags().DurationVar(&shutdownTimeout, "graceful-shutdown-timeout", 30*time.Second, "time in seconds gracefull shutdown timeout") cmd.Flags().StringVar(&imagePullSecretName, "image-pull-secret-name", "", "secret name to private registry") diff --git a/handlers.go b/handlers.go index 7bb29b5..e122351 100644 --- a/handlers.go +++ b/handlers.go @@ -204,9 +204,14 @@ func (app *App) HandleSession(w http.ResponseWriter, r *http.Request) { //HandleProxy ... func (app *App) HandleProxy(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - sessionID := vars["sessionId"] - host := tools.BuildHostPort(sessionID, app.serviceName, app.sidecarPort) + sessionID, ok := vars["sessionId"] + if !ok { + app.logger.Error("session id not found") + tools.JSONError(w, "session id not found", http.StatusBadRequest) + return + } + host := tools.BuildHostPort(sessionID, app.serviceName, app.sidecarPort) logger := app.logger.WithFields(logrus.Fields{ "request_id": uuid.New(), "session_id": sessionID, @@ -229,8 +234,8 @@ func (app *App) HandleProxy(w http.ResponseWriter, r *http.Request) { } -//HadleHubStatus ... -func (app *App) HadleHubStatus(w http.ResponseWriter, r *http.Request) { +//HandleHubStatus ... +func (app *App) HandleHubStatus(w http.ResponseWriter, r *http.Request) { logger := app.logger.WithFields(logrus.Fields{ "request_id": uuid.New(), "request": fmt.Sprintf("%s %s", r.Method, r.URL.Path), @@ -255,7 +260,13 @@ func (app *App) HadleHubStatus(w http.ResponseWriter, r *http.Request) { //HandleReverseProxy ... func (app *App) HandleReverseProxy(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - sessionID := vars["sessionId"] + sessionID, ok := vars["sessionId"] + if !ok { + app.logger.Error("session id not found") + tools.JSONError(w, "session id not found", http.StatusBadRequest) + return + } + fragments := strings.Split(r.URL.Path, "/") logger := app.logger.WithFields(logrus.Fields{ "request_id": uuid.New(), @@ -283,7 +294,11 @@ func (app *App) HandleVNC() websocket.Handler { defer wsconn.Close() vars := mux.Vars(wsconn.Request()) - sessionID := vars["sessionId"] + sessionID, ok := vars["sessionId"] + if !ok { + app.logger.Error("session id not found") + return + } logger := app.logger.WithFields(logrus.Fields{ "request_id": uuid.New(), @@ -323,7 +338,11 @@ func (app *App) HandleLogs() websocket.Handler { defer wsconn.Close() vars := mux.Vars(wsconn.Request()) - sessionID := vars["sessionId"] + sessionID, ok := vars["sessionId"] + if !ok { + app.logger.Error("session id not found") + return + } logger := app.logger.WithFields(logrus.Fields{ "request_id": uuid.New(), diff --git a/handlers_test.go b/handlers_test.go index 5a07891..6d090c2 100644 --- a/handlers_test.go +++ b/handlers_test.go @@ -530,12 +530,12 @@ func initApp(p *PlatformMock) *App { logger := &logrus.Logger{} client := NewPlatformMock(p) conf := Configuration{ - SelenosisHost: "hostname", - ServiceName: "selenosis", - SidecarPort: "4445", - BrowserWaitTimeout: 300 * time.Millisecond, - SessionIddleTimeout: 600 * time.Millisecond, - SessionRetryCount: 2, + SelenosisHost: "hostname", + ServiceName: "selenosis", + SidecarPort: "4445", + BrowserWaitTimeout: 300 * time.Millisecond, + SessionIdleTimeout: 600 * time.Millisecond, + SessionRetryCount: 2, } browsersConfig, _ := config.NewBrowsersConfig("config/browsers.yaml") diff --git a/platform/kubernetes.go b/platform/kubernetes.go index 2775252..6e8d02c 100644 --- a/platform/kubernetes.go +++ b/platform/kubernetes.go @@ -54,7 +54,7 @@ type ClientConfig struct { ImagePullSecretName string ProxyImage string ReadinessTimeout time.Duration - IddleTimeout time.Duration + IdleTimeout time.Duration } //Client ... @@ -65,7 +65,7 @@ type Client struct { imagePullSecretName string proxyImage string readinessTimeout time.Duration - iddleTimeout time.Duration + idleTimeout time.Duration clientset *kubernetes.Clientset } @@ -90,7 +90,7 @@ func NewClient(c ClientConfig) (Platform, error) { imagePullSecretName: c.ImagePullSecretName, proxyImage: c.ProxyImage, readinessTimeout: c.ReadinessTimeout, - iddleTimeout: c.IddleTimeout, + idleTimeout: c.IdleTimeout, }, nil } @@ -217,7 +217,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { }, Ports: getSidecarPorts(cl.svcPort), Command: []string{ - "/seleniferous", "--listhen-port", cl.svcPort.StrVal, "--proxy-default-path", path.Join(layout.Template.Path, "session"), "--iddle-timeout", cl.iddleTimeout.String(), "--namespace", cl.ns, + "/seleniferous", "--listhen-port", cl.svcPort.StrVal, "--proxy-default-path", path.Join(layout.Template.Path, "session"), "--idle-timeout", cl.idleTimeout.String(), "--namespace", cl.ns, }, ImagePullPolicy: apiv1.PullIfNotPresent, }, diff --git a/selenosis.go b/selenosis.go index a7b421e..624700f 100644 --- a/selenosis.go +++ b/selenosis.go @@ -11,28 +11,28 @@ import ( //Configuration .... type Configuration struct { - SelenosisHost string - ServiceName string - SidecarPort string - SessionLimit int - SessionRetryCount int - BrowserWaitTimeout time.Duration - SessionIddleTimeout time.Duration + SelenosisHost string + ServiceName string + SidecarPort string + SessionLimit int + SessionRetryCount int + BrowserWaitTimeout time.Duration + SessionIdleTimeout time.Duration } //App ... type App struct { - logger *log.Logger - client platform.Platform - browsers *config.BrowsersConfig - selenosisHost string - serviceName string - sidecarPort string - sessionLimit int - sessionRetryCount int - sessionIddleTimeout time.Duration - browserWaitTimeout time.Duration - stats *storage.Storage + logger *log.Logger + client platform.Platform + browsers *config.BrowsersConfig + selenosisHost string + serviceName string + sidecarPort string + sessionLimit int + sessionRetryCount int + sessionIdleTimeout time.Duration + browserWaitTimeout time.Duration + stats *storage.Storage } //New ... @@ -69,16 +69,16 @@ func New(logger *log.Logger, client platform.Platform, browsers *config.Browsers }() return &App{ - logger: logger, - client: client, - browsers: browsers, - selenosisHost: cfg.SelenosisHost, - serviceName: cfg.ServiceName, - sidecarPort: cfg.SidecarPort, - sessionLimit: cfg.SessionLimit, - sessionRetryCount: cfg.SessionRetryCount, - browserWaitTimeout: cfg.BrowserWaitTimeout, - sessionIddleTimeout: cfg.SessionIddleTimeout, - stats: storage, + logger: logger, + client: client, + browsers: browsers, + selenosisHost: cfg.SelenosisHost, + serviceName: cfg.ServiceName, + sidecarPort: cfg.SidecarPort, + sessionLimit: cfg.SessionLimit, + sessionRetryCount: cfg.SessionRetryCount, + browserWaitTimeout: cfg.BrowserWaitTimeout, + sessionIdleTimeout: cfg.SessionIdleTimeout, + stats: storage, } } From dcba899f334abdb517d0304e09abbcde81dded19 Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Thu, 11 Feb 2021 10:35:42 +0200 Subject: [PATCH 16/22] Fix typos, small improvements --- README.md | 2 +- cmd/selenosis/main.go | 22 ++++++++-------- handlers.go | 33 +++++++++++++++++++----- handlers_test.go | 12 ++++----- platform/kubernetes.go | 8 +++--- selenosis.go | 58 +++++++++++++++++++++--------------------- 6 files changed, 77 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 4fb71ee..793dd9f 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Flags: --service-name string kubernetes service name for browsers (default "seleniferous") --browser-wait-timeout duration time in seconds that a browser will be ready (default 30s) --session-wait-timeout duration time in seconds that a session will be ready (default 1m0s) - --session-iddle-timeout duration time in seconds that a session will iddle (default 5m0s) + --session-idle-timeout duration time in seconds that a session will idle (default 5m0s) --session-retry-count int session retry count (default 3) --graceful-shutdown-timeout duration time in seconds gracefull shutdown timeout (default 30s) --image-pull-secret-name string secret name to private registry diff --git a/cmd/selenosis/main.go b/cmd/selenosis/main.go index 2ead281..bead920 100644 --- a/cmd/selenosis/main.go +++ b/cmd/selenosis/main.go @@ -38,7 +38,7 @@ func command() *cobra.Command { limit int browserWaitTimeout time.Duration sessionWaitTimeout time.Duration - sessionIddleTimeout time.Duration + sessionIdleTimeout time.Duration shutdownTimeout time.Duration ) @@ -65,7 +65,7 @@ func command() *cobra.Command { Namespace: namespace, Service: service, ReadinessTimeout: browserWaitTimeout, - IddleTimeout: sessionIddleTimeout, + IdleTimeout: sessionIdleTimeout, ServicePort: proxyPort, ImagePullSecretName: imagePullSecretName, ProxyImage: proxyImage, @@ -80,19 +80,19 @@ func command() *cobra.Command { hostname, _ := os.Hostname() app := selenosis.New(logger, client, browsers, selenosis.Configuration{ - SelenosisHost: hostname, - ServiceName: service, - SidecarPort: proxyPort, - SessionLimit: limit, - SessionRetryCount: sessionRetryCount, - BrowserWaitTimeout: browserWaitTimeout, - SessionIddleTimeout: sessionIddleTimeout, + SelenosisHost: hostname, + ServiceName: service, + SidecarPort: proxyPort, + SessionLimit: limit, + SessionRetryCount: sessionRetryCount, + BrowserWaitTimeout: browserWaitTimeout, + SessionIdleTimeout: sessionIdleTimeout, }) router := mux.NewRouter() router.HandleFunc("/wd/hub/session", app.CheckLimit(app.HandleSession)).Methods(http.MethodPost) router.PathPrefix("/wd/hub/session/{sessionId}").HandlerFunc(app.HandleProxy) - router.HandleFunc("/wd/hub/status", app.HadleHubStatus).Methods(http.MethodGet) + router.HandleFunc("/wd/hub/status", app.HandleHubStatus).Methods(http.MethodGet) router.PathPrefix("/vnc/{sessionId}").Handler(websocket.Handler(app.HandleVNC())) router.PathPrefix("/logs/{sessionId}").Handler(websocket.Handler(app.HandleLogs())) router.PathPrefix("/devtools/{sessionId}").HandlerFunc(app.HandleReverseProxy) @@ -140,7 +140,7 @@ func command() *cobra.Command { cmd.Flags().StringVar(&service, "service-name", "seleniferous", "kubernetes service name for browsers") cmd.Flags().DurationVar(&browserWaitTimeout, "browser-wait-timeout", 30*time.Second, "time in seconds that a browser will be ready") cmd.Flags().DurationVar(&sessionWaitTimeout, "session-wait-timeout", 60*time.Second, "time in seconds that a session will be ready") - cmd.Flags().DurationVar(&sessionIddleTimeout, "session-iddle-timeout", 5*time.Minute, "time in seconds that a session will iddle") + cmd.Flags().DurationVar(&sessionIdleTimeout, "session-idle-timeout", 5*time.Minute, "time in seconds that a session will idle") cmd.Flags().IntVar(&sessionRetryCount, "session-retry-count", 3, "session retry count") cmd.Flags().DurationVar(&shutdownTimeout, "graceful-shutdown-timeout", 30*time.Second, "time in seconds gracefull shutdown timeout") cmd.Flags().StringVar(&imagePullSecretName, "image-pull-secret-name", "", "secret name to private registry") diff --git a/handlers.go b/handlers.go index 7bb29b5..e122351 100644 --- a/handlers.go +++ b/handlers.go @@ -204,9 +204,14 @@ func (app *App) HandleSession(w http.ResponseWriter, r *http.Request) { //HandleProxy ... func (app *App) HandleProxy(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - sessionID := vars["sessionId"] - host := tools.BuildHostPort(sessionID, app.serviceName, app.sidecarPort) + sessionID, ok := vars["sessionId"] + if !ok { + app.logger.Error("session id not found") + tools.JSONError(w, "session id not found", http.StatusBadRequest) + return + } + host := tools.BuildHostPort(sessionID, app.serviceName, app.sidecarPort) logger := app.logger.WithFields(logrus.Fields{ "request_id": uuid.New(), "session_id": sessionID, @@ -229,8 +234,8 @@ func (app *App) HandleProxy(w http.ResponseWriter, r *http.Request) { } -//HadleHubStatus ... -func (app *App) HadleHubStatus(w http.ResponseWriter, r *http.Request) { +//HandleHubStatus ... +func (app *App) HandleHubStatus(w http.ResponseWriter, r *http.Request) { logger := app.logger.WithFields(logrus.Fields{ "request_id": uuid.New(), "request": fmt.Sprintf("%s %s", r.Method, r.URL.Path), @@ -255,7 +260,13 @@ func (app *App) HadleHubStatus(w http.ResponseWriter, r *http.Request) { //HandleReverseProxy ... func (app *App) HandleReverseProxy(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - sessionID := vars["sessionId"] + sessionID, ok := vars["sessionId"] + if !ok { + app.logger.Error("session id not found") + tools.JSONError(w, "session id not found", http.StatusBadRequest) + return + } + fragments := strings.Split(r.URL.Path, "/") logger := app.logger.WithFields(logrus.Fields{ "request_id": uuid.New(), @@ -283,7 +294,11 @@ func (app *App) HandleVNC() websocket.Handler { defer wsconn.Close() vars := mux.Vars(wsconn.Request()) - sessionID := vars["sessionId"] + sessionID, ok := vars["sessionId"] + if !ok { + app.logger.Error("session id not found") + return + } logger := app.logger.WithFields(logrus.Fields{ "request_id": uuid.New(), @@ -323,7 +338,11 @@ func (app *App) HandleLogs() websocket.Handler { defer wsconn.Close() vars := mux.Vars(wsconn.Request()) - sessionID := vars["sessionId"] + sessionID, ok := vars["sessionId"] + if !ok { + app.logger.Error("session id not found") + return + } logger := app.logger.WithFields(logrus.Fields{ "request_id": uuid.New(), diff --git a/handlers_test.go b/handlers_test.go index 5a07891..6d090c2 100644 --- a/handlers_test.go +++ b/handlers_test.go @@ -530,12 +530,12 @@ func initApp(p *PlatformMock) *App { logger := &logrus.Logger{} client := NewPlatformMock(p) conf := Configuration{ - SelenosisHost: "hostname", - ServiceName: "selenosis", - SidecarPort: "4445", - BrowserWaitTimeout: 300 * time.Millisecond, - SessionIddleTimeout: 600 * time.Millisecond, - SessionRetryCount: 2, + SelenosisHost: "hostname", + ServiceName: "selenosis", + SidecarPort: "4445", + BrowserWaitTimeout: 300 * time.Millisecond, + SessionIdleTimeout: 600 * time.Millisecond, + SessionRetryCount: 2, } browsersConfig, _ := config.NewBrowsersConfig("config/browsers.yaml") diff --git a/platform/kubernetes.go b/platform/kubernetes.go index 2775252..6e8d02c 100644 --- a/platform/kubernetes.go +++ b/platform/kubernetes.go @@ -54,7 +54,7 @@ type ClientConfig struct { ImagePullSecretName string ProxyImage string ReadinessTimeout time.Duration - IddleTimeout time.Duration + IdleTimeout time.Duration } //Client ... @@ -65,7 +65,7 @@ type Client struct { imagePullSecretName string proxyImage string readinessTimeout time.Duration - iddleTimeout time.Duration + idleTimeout time.Duration clientset *kubernetes.Clientset } @@ -90,7 +90,7 @@ func NewClient(c ClientConfig) (Platform, error) { imagePullSecretName: c.ImagePullSecretName, proxyImage: c.ProxyImage, readinessTimeout: c.ReadinessTimeout, - iddleTimeout: c.IddleTimeout, + idleTimeout: c.IdleTimeout, }, nil } @@ -217,7 +217,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { }, Ports: getSidecarPorts(cl.svcPort), Command: []string{ - "/seleniferous", "--listhen-port", cl.svcPort.StrVal, "--proxy-default-path", path.Join(layout.Template.Path, "session"), "--iddle-timeout", cl.iddleTimeout.String(), "--namespace", cl.ns, + "/seleniferous", "--listhen-port", cl.svcPort.StrVal, "--proxy-default-path", path.Join(layout.Template.Path, "session"), "--idle-timeout", cl.idleTimeout.String(), "--namespace", cl.ns, }, ImagePullPolicy: apiv1.PullIfNotPresent, }, diff --git a/selenosis.go b/selenosis.go index a7b421e..624700f 100644 --- a/selenosis.go +++ b/selenosis.go @@ -11,28 +11,28 @@ import ( //Configuration .... type Configuration struct { - SelenosisHost string - ServiceName string - SidecarPort string - SessionLimit int - SessionRetryCount int - BrowserWaitTimeout time.Duration - SessionIddleTimeout time.Duration + SelenosisHost string + ServiceName string + SidecarPort string + SessionLimit int + SessionRetryCount int + BrowserWaitTimeout time.Duration + SessionIdleTimeout time.Duration } //App ... type App struct { - logger *log.Logger - client platform.Platform - browsers *config.BrowsersConfig - selenosisHost string - serviceName string - sidecarPort string - sessionLimit int - sessionRetryCount int - sessionIddleTimeout time.Duration - browserWaitTimeout time.Duration - stats *storage.Storage + logger *log.Logger + client platform.Platform + browsers *config.BrowsersConfig + selenosisHost string + serviceName string + sidecarPort string + sessionLimit int + sessionRetryCount int + sessionIdleTimeout time.Duration + browserWaitTimeout time.Duration + stats *storage.Storage } //New ... @@ -69,16 +69,16 @@ func New(logger *log.Logger, client platform.Platform, browsers *config.Browsers }() return &App{ - logger: logger, - client: client, - browsers: browsers, - selenosisHost: cfg.SelenosisHost, - serviceName: cfg.ServiceName, - sidecarPort: cfg.SidecarPort, - sessionLimit: cfg.SessionLimit, - sessionRetryCount: cfg.SessionRetryCount, - browserWaitTimeout: cfg.BrowserWaitTimeout, - sessionIddleTimeout: cfg.SessionIddleTimeout, - stats: storage, + logger: logger, + client: client, + browsers: browsers, + selenosisHost: cfg.SelenosisHost, + serviceName: cfg.ServiceName, + sidecarPort: cfg.SidecarPort, + sessionLimit: cfg.SessionLimit, + sessionRetryCount: cfg.SessionRetryCount, + browserWaitTimeout: cfg.BrowserWaitTimeout, + sessionIdleTimeout: cfg.SessionIdleTimeout, + stats: storage, } } From f5e7922197c0bd47e44b8fcf5d572e01d86ad99c Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Fri, 12 Feb 2021 12:48:11 +0200 Subject: [PATCH 17/22] Fix Issue https://github.com/alcounit/selenosis/issues/12 --- platform/kubernetes.go | 18 ++++++++++++------ platform/platform.go | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/platform/kubernetes.go b/platform/kubernetes.go index 6e8d02c..d853163 100644 --- a/platform/kubernetes.go +++ b/platform/kubernetes.go @@ -126,18 +126,17 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { defaults.session: layout.SessionID, } - envVar := func(name, value string) (i int, b bool) { + envVar := func(name string) (i int, b bool) { for i, slice := range layout.Template.Spec.EnvVars { if slice.Name == name { - slice.Value = value return i, true } } return -1, false } + i, b := envVar(defaults.screenResolution) if layout.RequestedCapabilities.ScreenResolution != "" { - i, b := envVar(defaults.screenResolution, layout.RequestedCapabilities.ScreenResolution) if !b { layout.Template.Spec.EnvVars = append(layout.Template.Spec.EnvVars, apiv1.EnvVar{Name: defaults.screenResolution, @@ -146,27 +145,34 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { layout.Template.Spec.EnvVars[i] = apiv1.EnvVar{Name: defaults.screenResolution, Value: layout.RequestedCapabilities.ScreenResolution} } labels[defaults.screenResolution] = layout.RequestedCapabilities.ScreenResolution + } else { + if b { + labels[defaults.screenResolution] = layout.Template.Spec.EnvVars[i].Value + } } + i, b = envVar(defaults.enableVNC) if layout.RequestedCapabilities.VNC { vnc := fmt.Sprintf("%v", layout.RequestedCapabilities.VNC) - i, b := envVar(defaults.enableVNC, vnc) if !b { layout.Template.Spec.EnvVars = append(layout.Template.Spec.EnvVars, apiv1.EnvVar{Name: defaults.enableVNC, Value: vnc}) } else { layout.Template.Spec.EnvVars[i] = apiv1.EnvVar{Name: defaults.enableVNC, Value: vnc} } labels[defaults.enableVNC] = vnc + } else { + if b { + labels[defaults.enableVNC] = layout.Template.Spec.EnvVars[i].Value + } } if layout.RequestedCapabilities.TimeZone != "" { - i, b := envVar(defaults.timeZone, layout.RequestedCapabilities.TimeZone) + i, b := envVar(defaults.timeZone) if !b { layout.Template.Spec.EnvVars = append(layout.Template.Spec.EnvVars, apiv1.EnvVar{Name: defaults.timeZone, Value: layout.RequestedCapabilities.TimeZone}) } else { layout.Template.Spec.EnvVars[i] = apiv1.EnvVar{Name: defaults.timeZone, Value: layout.RequestedCapabilities.TimeZone} } - labels[defaults.timeZone] = layout.RequestedCapabilities.TimeZone } if layout.Template.Meta.Labels == nil { diff --git a/platform/platform.go b/platform/platform.go index 37adac1..99d812f 100644 --- a/platform/platform.go +++ b/platform/platform.go @@ -20,7 +20,7 @@ type Meta struct { type Spec struct { Resources apiv1.ResourceRequirements `yaml:"resources,omitempty" json:"resources,omitempty"` HostAliases []apiv1.HostAlias `yaml:"hostAliases,omitempty" json:"hostAliases,omitempty"` - EnvVars []apiv1.EnvVar `yaml:"envVars,omitempty" json:"envVars,omitempty"` + EnvVars []apiv1.EnvVar `yaml:"env,omitempty" json:"env,omitempty"` NodeSelector map[string]string `yaml:"nodeSelector,omitempty" json:"nodeSelector,omitempty"` Affinity apiv1.Affinity `yaml:"affinity,omitempty" json:"affinity,omitempty"` DNSConfig apiv1.PodDNSConfig `yaml:"dnsConfig,omitempty" json:"dnsConfig,omitempty"` From 558f939cb048b6dca41eb5daad060d8de54ad0cc Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Fri, 12 Feb 2021 12:53:50 +0200 Subject: [PATCH 18/22] Fix issue #12 (#13) * Added support for session local storage * Fix typos, small improvements * Fix Issue https://github.com/alcounit/selenosis/issues/12 --- platform/kubernetes.go | 18 ++++++++++++------ platform/platform.go | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/platform/kubernetes.go b/platform/kubernetes.go index 6e8d02c..d853163 100644 --- a/platform/kubernetes.go +++ b/platform/kubernetes.go @@ -126,18 +126,17 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { defaults.session: layout.SessionID, } - envVar := func(name, value string) (i int, b bool) { + envVar := func(name string) (i int, b bool) { for i, slice := range layout.Template.Spec.EnvVars { if slice.Name == name { - slice.Value = value return i, true } } return -1, false } + i, b := envVar(defaults.screenResolution) if layout.RequestedCapabilities.ScreenResolution != "" { - i, b := envVar(defaults.screenResolution, layout.RequestedCapabilities.ScreenResolution) if !b { layout.Template.Spec.EnvVars = append(layout.Template.Spec.EnvVars, apiv1.EnvVar{Name: defaults.screenResolution, @@ -146,27 +145,34 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { layout.Template.Spec.EnvVars[i] = apiv1.EnvVar{Name: defaults.screenResolution, Value: layout.RequestedCapabilities.ScreenResolution} } labels[defaults.screenResolution] = layout.RequestedCapabilities.ScreenResolution + } else { + if b { + labels[defaults.screenResolution] = layout.Template.Spec.EnvVars[i].Value + } } + i, b = envVar(defaults.enableVNC) if layout.RequestedCapabilities.VNC { vnc := fmt.Sprintf("%v", layout.RequestedCapabilities.VNC) - i, b := envVar(defaults.enableVNC, vnc) if !b { layout.Template.Spec.EnvVars = append(layout.Template.Spec.EnvVars, apiv1.EnvVar{Name: defaults.enableVNC, Value: vnc}) } else { layout.Template.Spec.EnvVars[i] = apiv1.EnvVar{Name: defaults.enableVNC, Value: vnc} } labels[defaults.enableVNC] = vnc + } else { + if b { + labels[defaults.enableVNC] = layout.Template.Spec.EnvVars[i].Value + } } if layout.RequestedCapabilities.TimeZone != "" { - i, b := envVar(defaults.timeZone, layout.RequestedCapabilities.TimeZone) + i, b := envVar(defaults.timeZone) if !b { layout.Template.Spec.EnvVars = append(layout.Template.Spec.EnvVars, apiv1.EnvVar{Name: defaults.timeZone, Value: layout.RequestedCapabilities.TimeZone}) } else { layout.Template.Spec.EnvVars[i] = apiv1.EnvVar{Name: defaults.timeZone, Value: layout.RequestedCapabilities.TimeZone} } - labels[defaults.timeZone] = layout.RequestedCapabilities.TimeZone } if layout.Template.Meta.Labels == nil { diff --git a/platform/platform.go b/platform/platform.go index 37adac1..99d812f 100644 --- a/platform/platform.go +++ b/platform/platform.go @@ -20,7 +20,7 @@ type Meta struct { type Spec struct { Resources apiv1.ResourceRequirements `yaml:"resources,omitempty" json:"resources,omitempty"` HostAliases []apiv1.HostAlias `yaml:"hostAliases,omitempty" json:"hostAliases,omitempty"` - EnvVars []apiv1.EnvVar `yaml:"envVars,omitempty" json:"envVars,omitempty"` + EnvVars []apiv1.EnvVar `yaml:"env,omitempty" json:"env,omitempty"` NodeSelector map[string]string `yaml:"nodeSelector,omitempty" json:"nodeSelector,omitempty"` Affinity apiv1.Affinity `yaml:"affinity,omitempty" json:"affinity,omitempty"` DNSConfig apiv1.PodDNSConfig `yaml:"dnsConfig,omitempty" json:"dnsConfig,omitempty"` From 674e1f1f3c78a51dd8326d1564f2330f99da7d5f Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Thu, 18 Feb 2021 18:04:17 +0200 Subject: [PATCH 19/22] Fix https://github.com/alcounit/selenosis/issues/14 --- cmd/selenosis/main.go | 1 + handlers.go | 4 +++- platform/kubernetes.go | 8 ++------ platform/platform.go | 1 + selenosis.go | 3 +++ 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cmd/selenosis/main.go b/cmd/selenosis/main.go index bead920..7f84dc5 100644 --- a/cmd/selenosis/main.go +++ b/cmd/selenosis/main.go @@ -87,6 +87,7 @@ func command() *cobra.Command { SessionRetryCount: sessionRetryCount, BrowserWaitTimeout: browserWaitTimeout, SessionIdleTimeout: sessionIdleTimeout, + BuildVersion: buildVersion, }) router := mux.NewRouter() diff --git a/handlers.go b/handlers.go index e122351..6c68b7c 100644 --- a/handlers.go +++ b/handlers.go @@ -389,6 +389,7 @@ func (app *App) HandleStatus(w http.ResponseWriter, r *http.Request) { type Response struct { Status int `json:"status"` + Version string `json:"version"` Error string `json:"err,omitempty"` Selenosis Status `json:"selenosis,omitempty"` } @@ -397,7 +398,8 @@ func (app *App) HandleStatus(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode( Response{ - Status: http.StatusOK, + Status: http.StatusOK, + Version: app.buildVersion, Selenosis: Status{ Total: app.sessionLimit, Active: len(active), diff --git a/platform/kubernetes.go b/platform/kubernetes.go index d853163..c21a6f7 100644 --- a/platform/kubernetes.go +++ b/platform/kubernetes.go @@ -197,7 +197,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { Name: "browser", Image: layout.Template.Image, SecurityContext: &apiv1.SecurityContext{ - Privileged: pointer.BoolPtr(false), + Privileged: &layout.Template.Privileged, Capabilities: &apiv1.Capabilities{ Add: []apiv1.Capability{ "SYS_ADMIN", @@ -218,9 +218,6 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { { Name: "seleniferous", Image: cl.proxyImage, - SecurityContext: &apiv1.SecurityContext{ - Privileged: pointer.BoolPtr(true), - }, Ports: getSidecarPorts(cl.svcPort), Command: []string{ "/seleniferous", "--listhen-port", cl.svcPort.StrVal, "--proxy-default-path", path.Join(layout.Template.Path, "session"), "--idle-timeout", cl.idleTimeout.String(), "--namespace", cl.ns, @@ -504,7 +501,6 @@ func getImagePullSecretList(secret string) []apiv1.LocalObjectReference { func waitForService(u url.URL, t time.Duration) error { up := make(chan struct{}) done := make(chan struct{}) - u.Path = "/status" go func() { for { select { @@ -513,7 +509,7 @@ func waitForService(u url.URL, t time.Duration) error { default: } - req, _ := http.NewRequest(http.MethodGet, u.String(), nil) + req, _ := http.NewRequest(http.MethodHead, u.String(), nil) req.Close = true resp, err := http.DefaultClient.Do(req) if resp != nil { diff --git a/platform/platform.go b/platform/platform.go index 99d812f..48e22b0 100644 --- a/platform/platform.go +++ b/platform/platform.go @@ -32,6 +32,7 @@ type BrowserSpec struct { BrowserVersion string `yaml:"-" json:"-"` Image string `yaml:"image" json:"image"` Path string `yaml:"path" json:"path"` + Privileged bool `yaml:"privileged" json:"privileged"` Meta Meta `yaml:"meta" json:"meta"` Spec Spec `yaml:"spec" json:"spec"` } diff --git a/selenosis.go b/selenosis.go index 624700f..04ec468 100644 --- a/selenosis.go +++ b/selenosis.go @@ -18,6 +18,7 @@ type Configuration struct { SessionRetryCount int BrowserWaitTimeout time.Duration SessionIdleTimeout time.Duration + BuildVersion string } //App ... @@ -32,6 +33,7 @@ type App struct { sessionRetryCount int sessionIdleTimeout time.Duration browserWaitTimeout time.Duration + buildVersion string stats *storage.Storage } @@ -79,6 +81,7 @@ func New(logger *log.Logger, client platform.Platform, browsers *config.Browsers sessionRetryCount: cfg.SessionRetryCount, browserWaitTimeout: cfg.BrowserWaitTimeout, sessionIdleTimeout: cfg.SessionIdleTimeout, + buildVersion: cfg.BuildVersion, stats: storage, } } From c8e2f358d7ddcbb878143edf463f90fb9d5dcd2a Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Thu, 18 Feb 2021 19:49:27 +0200 Subject: [PATCH 20/22] Fix issue https://github.com/alcounit/selenosis/issues/14 (#15) --- cmd/selenosis/main.go | 1 + handlers.go | 4 +++- platform/kubernetes.go | 8 ++------ platform/platform.go | 1 + selenosis.go | 3 +++ 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cmd/selenosis/main.go b/cmd/selenosis/main.go index bead920..7f84dc5 100644 --- a/cmd/selenosis/main.go +++ b/cmd/selenosis/main.go @@ -87,6 +87,7 @@ func command() *cobra.Command { SessionRetryCount: sessionRetryCount, BrowserWaitTimeout: browserWaitTimeout, SessionIdleTimeout: sessionIdleTimeout, + BuildVersion: buildVersion, }) router := mux.NewRouter() diff --git a/handlers.go b/handlers.go index e122351..6c68b7c 100644 --- a/handlers.go +++ b/handlers.go @@ -389,6 +389,7 @@ func (app *App) HandleStatus(w http.ResponseWriter, r *http.Request) { type Response struct { Status int `json:"status"` + Version string `json:"version"` Error string `json:"err,omitempty"` Selenosis Status `json:"selenosis,omitempty"` } @@ -397,7 +398,8 @@ func (app *App) HandleStatus(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode( Response{ - Status: http.StatusOK, + Status: http.StatusOK, + Version: app.buildVersion, Selenosis: Status{ Total: app.sessionLimit, Active: len(active), diff --git a/platform/kubernetes.go b/platform/kubernetes.go index d853163..c21a6f7 100644 --- a/platform/kubernetes.go +++ b/platform/kubernetes.go @@ -197,7 +197,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { Name: "browser", Image: layout.Template.Image, SecurityContext: &apiv1.SecurityContext{ - Privileged: pointer.BoolPtr(false), + Privileged: &layout.Template.Privileged, Capabilities: &apiv1.Capabilities{ Add: []apiv1.Capability{ "SYS_ADMIN", @@ -218,9 +218,6 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { { Name: "seleniferous", Image: cl.proxyImage, - SecurityContext: &apiv1.SecurityContext{ - Privileged: pointer.BoolPtr(true), - }, Ports: getSidecarPorts(cl.svcPort), Command: []string{ "/seleniferous", "--listhen-port", cl.svcPort.StrVal, "--proxy-default-path", path.Join(layout.Template.Path, "session"), "--idle-timeout", cl.idleTimeout.String(), "--namespace", cl.ns, @@ -504,7 +501,6 @@ func getImagePullSecretList(secret string) []apiv1.LocalObjectReference { func waitForService(u url.URL, t time.Duration) error { up := make(chan struct{}) done := make(chan struct{}) - u.Path = "/status" go func() { for { select { @@ -513,7 +509,7 @@ func waitForService(u url.URL, t time.Duration) error { default: } - req, _ := http.NewRequest(http.MethodGet, u.String(), nil) + req, _ := http.NewRequest(http.MethodHead, u.String(), nil) req.Close = true resp, err := http.DefaultClient.Do(req) if resp != nil { diff --git a/platform/platform.go b/platform/platform.go index 99d812f..48e22b0 100644 --- a/platform/platform.go +++ b/platform/platform.go @@ -32,6 +32,7 @@ type BrowserSpec struct { BrowserVersion string `yaml:"-" json:"-"` Image string `yaml:"image" json:"image"` Path string `yaml:"path" json:"path"` + Privileged bool `yaml:"privileged" json:"privileged"` Meta Meta `yaml:"meta" json:"meta"` Spec Spec `yaml:"spec" json:"spec"` } diff --git a/selenosis.go b/selenosis.go index 624700f..04ec468 100644 --- a/selenosis.go +++ b/selenosis.go @@ -18,6 +18,7 @@ type Configuration struct { SessionRetryCount int BrowserWaitTimeout time.Duration SessionIdleTimeout time.Duration + BuildVersion string } //App ... @@ -32,6 +33,7 @@ type App struct { sessionRetryCount int sessionIdleTimeout time.Duration browserWaitTimeout time.Duration + buildVersion string stats *storage.Storage } @@ -79,6 +81,7 @@ func New(logger *log.Logger, client platform.Platform, browsers *config.Browsers sessionRetryCount: cfg.SessionRetryCount, browserWaitTimeout: cfg.BrowserWaitTimeout, sessionIdleTimeout: cfg.SessionIdleTimeout, + buildVersion: cfg.BuildVersion, stats: storage, } } From 756cf246a5f4b493a4cc6db62f4edc455ec25313 Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Tue, 23 Feb 2021 11:47:12 +0200 Subject: [PATCH 21/22] Create release.yml --- .github/workflows/release.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..16b0cb4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,35 @@ +name: Release +on: + create: + tags: + - v* + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - + name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + push: true + build-args: ${{ steps.get_version.outputs.GIT_TAG }} + tags: | + alcounit/selenosis:latest + alcounit/selenosis:${{ steps.get_version.outputs.GIT_TAG }} From 2d7fc7b38167b774613093ade14df5b0fa92afe0 Mon Sep 17 00:00:00 2001 From: Danylo Kuvshynov Date: Tue, 23 Feb 2021 11:49:42 +0200 Subject: [PATCH 22/22] Implement https://github.com/alcounit/selenosis/issues/16 --- platform/kubernetes.go | 5 +---- platform/platform.go | 1 + storage/storage.go | 2 ++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/kubernetes.go b/platform/kubernetes.go index c21a6f7..9417d19 100644 --- a/platform/kubernetes.go +++ b/platform/kubernetes.go @@ -11,7 +11,6 @@ import ( "path" "time" - "github.com/alcounit/selenosis/tools" apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" @@ -240,6 +239,7 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { RestartPolicy: apiv1.RestartPolicyNever, Affinity: &layout.Template.Spec.Affinity, DNSConfig: &layout.Template.Spec.DNSConfig, + Tolerations: layout.Template.Spec.Tolerations, ImagePullSecrets: getImagePullSecretList(cl.imagePullSecretName), }, } @@ -323,7 +323,6 @@ func (cl *Client) Create(layout *ServiceSpec) (*Service, error) { cancel() }, Started: pod.CreationTimestamp.Time, - Uptime: tools.TimeElapsed(pod.CreationTimestamp.Time), } return svc, nil @@ -377,7 +376,6 @@ func (cl *Client) List() ([]*Service, error) { }, Status: status, Started: pod.CreationTimestamp.Time, - Uptime: tools.TimeElapsed(pod.CreationTimestamp.Time), } services = append(services, service) } @@ -417,7 +415,6 @@ func (cl Client) Watch() <-chan Event { }, Status: status, Started: pod.CreationTimestamp.Time, - Uptime: tools.TimeElapsed(pod.CreationTimestamp.Time), } } diff --git a/platform/platform.go b/platform/platform.go index 48e22b0..1571731 100644 --- a/platform/platform.go +++ b/platform/platform.go @@ -24,6 +24,7 @@ type Spec struct { NodeSelector map[string]string `yaml:"nodeSelector,omitempty" json:"nodeSelector,omitempty"` Affinity apiv1.Affinity `yaml:"affinity,omitempty" json:"affinity,omitempty"` DNSConfig apiv1.PodDNSConfig `yaml:"dnsConfig,omitempty" json:"dnsConfig,omitempty"` + Tolerations []apiv1.Toleration `yaml:"tolerations,omitempty" json:"tolerations,omitempty"` } //BrowserSpec describes settings for Service diff --git a/storage/storage.go b/storage/storage.go index 4a409fd..db0cae1 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -4,6 +4,7 @@ import ( "sync" "github.com/alcounit/selenosis/platform" + "github.com/alcounit/selenosis/tools" ) //Storage ... @@ -41,6 +42,7 @@ func (s *Storage) List() []platform.Service { for _, p := range s.sessions { c := *p + c.Uptime = tools.TimeElapsed(c.Started) l = append(l, c) } return l