/ components / execd / pkg / runtime / ctrl.go
ctrl.go
  1  // Copyright 2025 Alibaba Group Holding Ltd.
  2  //
  3  // Licensed under the Apache License, Version 2.0 (the "License");
  4  // you may not use this file except in compliance with the License.
  5  // You may obtain a copy of the License at
  6  //
  7  //     http://www.apache.org/licenses/LICENSE-2.0
  8  //
  9  // Unless required by applicable law or agreed to in writing, software
 10  // distributed under the License is distributed on an "AS IS" BASIS,
 11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  // See the License for the specific language governing permissions and
 13  // limitations under the License.
 14  
 15  package runtime
 16  
 17  import (
 18  	"context"
 19  	"database/sql"
 20  	"fmt"
 21  	"sync"
 22  	"time"
 23  
 24  	"k8s.io/apimachinery/pkg/util/wait"
 25  
 26  	"github.com/alibaba/opensandbox/execd/pkg/jupyter"
 27  )
 28  
 29  var kernelWaitingBackoff = wait.Backoff{
 30  	Steps:    60,
 31  	Duration: 500 * time.Millisecond,
 32  	Factor:   1.5,
 33  	Jitter:   0.1,
 34  }
 35  
 36  // Controller manages code execution across runtimes.
 37  type Controller struct {
 38  	baseURL                 string
 39  	token                   string
 40  	mu                      sync.RWMutex
 41  	jupyterClientMap        sync.Map // map[sessionID]*jupyterKernel
 42  	defaultLanguageSessions sync.Map // map[Language]string
 43  	commandClientMap        sync.Map // map[sessionID]*commandKernel
 44  	bashSessionClientMap    sync.Map // map[sessionID]*bashSession
 45  	ptySessionMap           sync.Map // map[sessionID]*ptySession
 46  	db                      *sql.DB
 47  	dbOnce                  sync.Once
 48  }
 49  
 50  type jupyterKernel struct {
 51  	mu       sync.Mutex
 52  	kernelID string
 53  	client   *jupyter.Client
 54  	language Language
 55  }
 56  
 57  type commandKernel struct {
 58  	pid          int
 59  	stdoutPath   string
 60  	stderrPath   string
 61  	startedAt    time.Time
 62  	finishedAt   *time.Time
 63  	exitCode     *int
 64  	errMsg       string
 65  	running      bool
 66  	isBackground bool
 67  	content      string
 68  }
 69  
 70  // NewController creates a runtime controller.
 71  func NewController(baseURL, token string) *Controller {
 72  	return &Controller{
 73  		baseURL: baseURL,
 74  		token:   token,
 75  	}
 76  }
 77  
 78  // Execute dispatches a request to the correct backend.
 79  func (c *Controller) Execute(request *ExecuteCodeRequest) error {
 80  	var cancel context.CancelFunc
 81  	var ctx context.Context
 82  	if request.Timeout > 0 {
 83  		ctx, cancel = context.WithTimeout(context.Background(), request.Timeout)
 84  	} else {
 85  		ctx, cancel = context.WithCancel(context.Background())
 86  	}
 87  
 88  	switch request.Language {
 89  	case Command:
 90  		defer cancel()
 91  		return c.runCommand(ctx, request)
 92  	case BackgroundCommand:
 93  		return c.runBackgroundCommand(ctx, cancel, request)
 94  	case Bash, Python, Java, JavaScript, TypeScript, Go:
 95  		defer cancel()
 96  		return c.runJupyter(ctx, request)
 97  	case SQL:
 98  		defer cancel()
 99  		return c.runSQL(ctx, request)
100  	default:
101  		defer cancel()
102  		return fmt.Errorf("unknown language: %s", request.Language)
103  	}
104  }