Skip to content

Commit 9bbf6ed

Browse files
ci: [sync] Update from eclipse-che/che-machine-exec @ 9a4e
Signed-off-by: devstudio-release <[email protected]>
1 parent 65020a0 commit 9bbf6ed

File tree

6 files changed

+242
-91
lines changed

6 files changed

+242
-91
lines changed

devspaces-machineexec/api/rest/activity.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ package rest
1515
import (
1616
"net/http"
1717

18-
"github.com/eclipse-che/che-machine-exec/activity"
1918
"github.com/eclipse-che/che-machine-exec/auth"
2019
restUtil "github.com/eclipse-che/che-machine-exec/common/rest"
20+
"github.com/eclipse-che/che-machine-exec/timeout"
2121
"github.com/gin-gonic/gin"
2222
)
2323

24-
func HandleActivityTick(c *gin.Context, manager activity.Manager) {
24+
func HandleActivityTick(c *gin.Context, manager timeout.InactivityIdleManager) {
2525
if auth.IsEnabled() {
2626
_, err := auth.Authenticate(c)
2727
if err != nil {

devspaces-machineexec/cfg/cfg.go

+36-3
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,14 @@ var (
3434
AuthenticatedUserID string
3535

3636
// IdleTimeout is a inactivity period after which workspace should be stopped
37-
// Default -1, which mean - does not stop
37+
// Default value is 30 minutes
3838
IdleTimeout time.Duration
3939
// StopRetryPeriod is a period after which workspace should be tried to stop if the previous try failed
40-
// Defaults 10 second
40+
// Default value is 10 seconds
4141
StopRetryPeriod time.Duration
42+
// RunTimeout is the maximum duration a workspace can be running before it is stopped
43+
// Default value is -1, which means - no maximium duration
44+
RunTimeout time.Duration
4245

4346
// UseTLS flag to enable/disable serving TLS
4447
UseTLS bool
@@ -82,7 +85,32 @@ func init() {
8285
}
8386
flag.StringVar(&AuthenticatedUserID, "authenticated-user-id", defaultAuthenticatedUserID, "OpenShift user's ID that should has access to API. Is used only if useBearerToken is configured")
8487

85-
flag.DurationVar(&IdleTimeout, "idle-timeout", -1*time.Nanosecond, "IdleTimeout is a inactivity period after which workspace should be stopped. Examples: -1, 30s, 15m, 1h")
88+
const defaultIdleTimeout = -1 * time.Nanosecond
89+
idleTimeout := defaultIdleTimeout
90+
idleTimeoutEnv := "SECONDS_OF_DW_INACTIVITY_BEFORE_IDLING"
91+
idleTimeoutEnvValue, isFound := os.LookupEnv(idleTimeoutEnv)
92+
if isFound && len(idleTimeoutEnvValue) > 0 {
93+
if v, err := strconv.ParseInt(idleTimeoutEnvValue, 10, 32); err == nil {
94+
idleTimeout = time.Duration(v) * time.Second
95+
} else {
96+
logrus.Errorf("Invalid value '%s' for env variable key '%s'. Value should be an integer", idleTimeoutEnvValue, idleTimeoutEnv)
97+
}
98+
}
99+
flag.DurationVar(&IdleTimeout, "idle-timeout", idleTimeout, "IdleTimeout is a inactivity period after which workspace should be stopped. By default, IdleTimeout is set to 30m. To disable IdleTimeout, set to -1. Examples: -1, 30s, 15m, 1h")
100+
101+
const defaultRunTimeout = 1800 * time.Nanosecond
102+
runtimeout := defaultRunTimeout
103+
runTimeoutEnv := "SECONDS_OF_DW_RUN_BEFORE_IDLING"
104+
runTimeoutEnvValue, isFound := os.LookupEnv(runTimeoutEnv)
105+
if isFound && len(runTimeoutEnvValue) > 0 {
106+
if v, err := strconv.ParseInt(runTimeoutEnvValue, 10, 32); err == nil {
107+
runtimeout = time.Duration(v) * time.Second
108+
} else {
109+
logrus.Errorf("Invalid value '%s' for env variable key '%s'. Value should be an integer", runTimeoutEnvValue, runTimeoutEnv)
110+
}
111+
}
112+
flag.DurationVar(&RunTimeout, "run-timeout", runtimeout, "RunTimeout is the maximum duration a workspace can run. After this period, the workspace will be stopped. Examples: -1, 30s, 15m, 1h")
113+
86114
flag.DurationVar(&StopRetryPeriod, "stop-retry-period", 10*time.Second, "StopRetryPeriod is a period after which workspace should be tried to stop if the previous try failed. Examples: 30s")
87115

88116
flag.BoolVar(&UseTLS, "use-tls", false, "Serve content via TLS")
@@ -148,6 +176,11 @@ func Print() {
148176
}
149177
if IdleTimeout > 0 {
150178
logrus.Infof("==> Idle timeout: %s", IdleTimeout)
179+
}
180+
if RunTimeout > 0 {
181+
logrus.Infof("==> Run timeout: %s", RunTimeout)
182+
}
183+
if IdleTimeout > 0 || RunTimeout > 0 {
151184
logrus.Infof("==> Stop retry period: %s", StopRetryPeriod)
152185
}
153186
}

devspaces-machineexec/main.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ package main
1515
import (
1616
"net/http"
1717

18-
"github.com/eclipse-che/che-machine-exec/activity"
18+
"github.com/eclipse-che/che-machine-exec/timeout"
1919

2020
jsonRpcApi "github.com/eclipse-che/che-machine-exec/api/jsonrpc"
2121
"github.com/eclipse-che/che-machine-exec/api/rest"
@@ -30,12 +30,18 @@ func main() {
3030
cfg.Parse()
3131
cfg.Print()
3232

33-
activityManager, err := activity.New(cfg.IdleTimeout, cfg.StopRetryPeriod)
33+
activityManager, err := timeout.NewInactivityIdleManager(cfg.IdleTimeout, cfg.StopRetryPeriod)
3434
if err != nil {
3535
logrus.Fatal("Unable to create activity manager. Cause: ", err.Error())
3636
return
3737
}
3838

39+
runtimeManager, err := timeout.NewRunIdleManager(cfg.RunTimeout, cfg.StopRetryPeriod)
40+
if err != nil {
41+
logrus.Fatal("Unable to create runtime manager. Cause: ", err.Error())
42+
return
43+
}
44+
3945
r := gin.Default()
4046

4147
if cfg.StaticPath != "" {
@@ -84,6 +90,10 @@ func main() {
8490
activityManager.Start()
8591
}
8692

93+
if runtimeManager != nil {
94+
runtimeManager.Start()
95+
}
96+
8797
if cfg.UseTLS {
8898
if err := r.RunTLS(cfg.URL, "/var/serving-cert/tls.crt", "/var/serving-cert/tls.key"); err != nil {
8999
logrus.Fatal("Unable to start server with TLS enabled. Cause: ", err.Error())
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright (c) 2019-2021 Red Hat, Inc.
2+
// Copyright (c) 2019-2022 Red Hat, Inc.
33
// This program and the accompanying materials are made
44
// available under the terms of the Eclipse Public License 2.0
55
// which is available at https://www.eclipse.org/legal/epl-2.0/
@@ -10,10 +10,9 @@
1010
// Red Hat, Inc. - initial API and implementation
1111
//
1212

13-
package activity
13+
package timeout
1414

1515
import (
16-
"context"
1716
"errors"
1817
"os"
1918
"os/signal"
@@ -22,30 +21,9 @@ import (
2221

2322
"github.com/eclipse-che/che-machine-exec/exec"
2423
"github.com/sirupsen/logrus"
25-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26-
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27-
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
28-
"k8s.io/apimachinery/pkg/runtime/schema"
29-
"k8s.io/apimachinery/pkg/types"
30-
"k8s.io/client-go/dynamic"
31-
"k8s.io/client-go/rest"
3224
)
3325

34-
var (
35-
DevWorkspaceAPIResource = &metav1.APIResource{
36-
Name: "devworkspaces",
37-
Group: "workspace.devfile.io",
38-
Version: "v1alpha1",
39-
Namespaced: true,
40-
}
41-
42-
DevWorkspaceGroupVersion = &schema.GroupVersion{
43-
Group: "workspace.devfile.io",
44-
Version: "v1alpha1",
45-
}
46-
)
47-
48-
type Manager interface {
26+
type InactivityIdleManager interface {
4927
// Start starts tracking users activity and scheduling workspace stopping if there is no activity for idle timeout
5028
// Should be called once
5129
Start()
@@ -54,9 +32,9 @@ type Manager interface {
5432
Tick()
5533
}
5634

57-
func New(idleTimeout, stopRetryPeriod time.Duration) (Manager, error) {
58-
if idleTimeout < 0 {
59-
return &noOpManager{}, nil
35+
func NewInactivityIdleManager(idleTimeout, stopRetryPeriod time.Duration) (InactivityIdleManager, error) {
36+
if idleTimeout <= 0 {
37+
return &noOpInactivityIdleManager{}, nil
6038
}
6139

6240
if stopRetryPeriod <= 0 {
@@ -65,7 +43,7 @@ func New(idleTimeout, stopRetryPeriod time.Duration) (Manager, error) {
6543

6644
namespace := exec.GetNamespace()
6745
if namespace == "" {
68-
return nil, errors.New("unable to evaluate the current namespace that is needed for activity manager works correctly")
46+
return nil, errors.New("unable to evaluate the current namespace required for activity manager to work correctly")
6947
}
7048

7149
workspaceName, isFound := os.LookupEnv("CHE_WORKSPACE_NAME")
@@ -76,7 +54,7 @@ func New(idleTimeout, stopRetryPeriod time.Duration) (Manager, error) {
7654
}
7755
}
7856

79-
return managerImpl{
57+
return inactivityIdleManagerImpl{
8058
namespace: namespace,
8159
workspaceName: workspaceName,
8260
idleTimeout: idleTimeout,
@@ -85,14 +63,14 @@ func New(idleTimeout, stopRetryPeriod time.Duration) (Manager, error) {
8563
}, nil
8664
}
8765

88-
// noOpManager should be used if idle timeout is configured less 0
66+
// noOpInactivityIdleManager should be used if idle timeout is configured less 0
8967
// invocation its method does not have affect
90-
type noOpManager struct{}
68+
type noOpInactivityIdleManager struct{}
9169

92-
func (m noOpManager) Tick() {}
93-
func (m noOpManager) Start() {}
70+
func (m noOpInactivityIdleManager) Tick() {}
71+
func (m noOpInactivityIdleManager) Start() {}
9472

95-
type managerImpl struct {
73+
type inactivityIdleManagerImpl struct {
9674
namespace string
9775
workspaceName string
9876

@@ -102,7 +80,7 @@ type managerImpl struct {
10280
activityC chan bool
10381
}
10482

105-
func (m managerImpl) Tick() {
83+
func (m inactivityIdleManagerImpl) Tick() {
10684
select {
10785
case m.activityC <- true:
10886
default:
@@ -111,7 +89,7 @@ func (m managerImpl) Tick() {
11189
}
11290
}
11391

114-
func (m managerImpl) Start() {
92+
func (m inactivityIdleManagerImpl) Start() {
11593
logrus.Infof("Activity tracker is run and workspace will be stopped in %s if there is no activity", m.idleTimeout)
11694
timer := time.NewTimer(m.idleTimeout)
11795
var shutdownChan = make(chan os.Signal, 1)
@@ -121,7 +99,7 @@ func (m managerImpl) Start() {
12199
for {
122100
select {
123101
case <-timer.C:
124-
if err := m.stopWorkspace(); err != nil {
102+
if err := stopWorkspace(m.namespace, m.workspaceName); err != nil {
125103
timer.Reset(m.stopRetryPeriod)
126104
logrus.Errorf("Failed to stop workspace. Will retry in %s. Cause: %s", m.stopRetryPeriod, err)
127105
} else {
@@ -141,49 +119,3 @@ func (m managerImpl) Start() {
141119
}
142120
}()
143121
}
144-
145-
func (m managerImpl) stopWorkspace() error {
146-
c, err := newWorkspaceClientInCluster()
147-
if err != nil {
148-
return err
149-
}
150-
151-
stopWorkspacePath := &unstructured.Unstructured{
152-
Object: map[string]interface{}{
153-
"metadata": map[string]interface{}{
154-
"annotations": map[string]interface{}{
155-
"controller.devfile.io/stopped-by": "inactivity",
156-
},
157-
},
158-
"spec": map[string]interface{}{
159-
"started": false,
160-
},
161-
},
162-
}
163-
jsonPath, err := stopWorkspacePath.MarshalJSON()
164-
if err != nil {
165-
return err
166-
}
167-
168-
_, err = c.Resource(DevWorkspaceGroupVersion.WithResource("devworkspaces")).Namespace(m.namespace).Patch(context.TODO(), m.workspaceName, types.MergePatchType, jsonPath, v1.PatchOptions{}, "")
169-
if err != nil {
170-
return err
171-
}
172-
173-
return nil
174-
}
175-
176-
func newWorkspaceClientInCluster() (dynamic.Interface, error) {
177-
config, err := rest.InClusterConfig()
178-
if err != nil {
179-
return nil, err
180-
}
181-
config.APIPath = "/apis"
182-
config.GroupVersion = DevWorkspaceGroupVersion
183-
184-
c, err := dynamic.NewForConfig(config)
185-
if err != nil {
186-
return nil, err
187-
}
188-
return c, nil
189-
}

devspaces-machineexec/timeout/run.go

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//
2+
// Copyright (c) 2022 Red Hat, Inc.
3+
// This program and the accompanying materials are made
4+
// available under the terms of the Eclipse Public License 2.0
5+
// which is available at https://www.eclipse.org/legal/epl-2.0/
6+
//
7+
// SPDX-License-Identifier: EPL-2.0
8+
//
9+
// Contributors:
10+
// Red Hat, Inc. - initial API and implementation
11+
//
12+
13+
package timeout
14+
15+
import (
16+
"errors"
17+
"os"
18+
"os/signal"
19+
"syscall"
20+
"time"
21+
22+
"github.com/eclipse-che/che-machine-exec/exec"
23+
"github.com/sirupsen/logrus"
24+
)
25+
26+
type RunIdleManager interface {
27+
// Start schedules workspace to stop after run timeout
28+
// Should be called once
29+
Start()
30+
}
31+
32+
func NewRunIdleManager(runTimeout, stopRetryPeriod time.Duration) (RunIdleManager, error) {
33+
if runTimeout <= 0 {
34+
return &noOpRunIdleManager{}, nil
35+
}
36+
37+
if stopRetryPeriod <= 0 {
38+
return nil, errors.New("stop retry period must be greater than 0")
39+
}
40+
41+
namespace := exec.GetNamespace()
42+
if namespace == "" {
43+
return nil, errors.New("unable to evaluate the current namespace required for run idle manager to work correctly")
44+
}
45+
46+
workspaceName, isFound := os.LookupEnv("CHE_WORKSPACE_NAME")
47+
if !isFound {
48+
workspaceName, isFound = os.LookupEnv("DEVWORKSPACE_NAME")
49+
if !isFound {
50+
return nil, errors.New("CHE_WORKSPACE_NAME or DEVWORKSPACE_NAME environment variables must be set for activity manager to work correctly")
51+
}
52+
}
53+
54+
return runIdleManagerImpl{
55+
namespace: namespace,
56+
workspaceName: workspaceName,
57+
runTimeout: runTimeout,
58+
stopRetryPeriod: stopRetryPeriod,
59+
}, nil
60+
}
61+
62+
// noOpRunIdleManager should be used if run timeout is configured less 0
63+
// invocation its method does not have affect
64+
type noOpRunIdleManager struct{}
65+
66+
func (m noOpRunIdleManager) Start() {}
67+
68+
type runIdleManagerImpl struct {
69+
namespace string
70+
workspaceName string
71+
72+
runTimeout time.Duration
73+
stopRetryPeriod time.Duration
74+
}
75+
76+
func (m runIdleManagerImpl) Start() {
77+
logrus.Infof("Run idle manager is running. The workspace will be stopped in %s", m.runTimeout)
78+
timer := time.NewTimer(m.runTimeout)
79+
var shutdownChan = make(chan os.Signal, 1)
80+
signal.Notify(shutdownChan, syscall.SIGTERM)
81+
82+
go func() {
83+
for {
84+
select {
85+
case <-timer.C:
86+
if err := stopWorkspace(m.namespace, m.workspaceName); err != nil {
87+
timer.Reset(m.stopRetryPeriod)
88+
logrus.Errorf("Failed to stop workspace. Will retry in %s. Cause: %s", m.stopRetryPeriod, err)
89+
} else {
90+
logrus.Info("Workspace has reached its run timeout. Bye")
91+
return
92+
}
93+
case <-shutdownChan:
94+
logrus.Info("Received SIGTERM: shutting down run timeout manager")
95+
return
96+
}
97+
}
98+
}()
99+
}

0 commit comments

Comments
 (0)