Skip to content

Commit

Permalink
feat: serve verb, daemon default (#483)
Browse files Browse the repository at this point in the history
Signed-off-by: Ivan Dagelic <[email protected]>
  • Loading branch information
idagelic authored May 6, 2024
1 parent 6404df7 commit b6a43c3
Show file tree
Hide file tree
Showing 7 changed files with 334 additions and 258 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Set up a development environment on any infrastructure, with a single command.
## Quick Start
### Mac / Linux
```bash
(curl -sf -L https://download.daytona.io/daytona/install.sh | sudo bash) && daytona server -d && daytona
(curl -sf -L https://download.daytona.io/daytona/install.sh | sudo bash) && daytona server -y && daytona
```
### Windows
<details>
Expand All @@ -71,7 +71,7 @@ $architecture = if ($env:PROCESSOR_ARCHITECTURE -eq "AMD64") { "amd64" } else {
md -Force "$Env:APPDATA\bin\daytona"; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]'Tls,Tls11,Tls12';
Invoke-WebRequest -URI "https://download.daytona.io/daytona/latest/daytona-windows-$architecture.exe" -OutFile "$Env:APPDATA\bin\daytona\daytona.exe";
$env:Path += ";" + $Env:APPDATA + "\bin\daytona"; [Environment]::SetEnvironmentVariable("Path", $env:Path, [System.EnvironmentVariableTarget]::User);
daytona server;
daytona serve;
```

</details>
Expand Down Expand Up @@ -156,7 +156,7 @@ To initialize Daytona, follow these steps:
__1. Start the Daytona Server:__
This initiates the Daytona Server in daemon mode. Use the command:
```bash
daytona server -d
daytona server
```
__2. Add Your Git Provider of Choice:__
Daytona supports GitHub, GitLab, Bitbucket and Gitea. To add them to your profile, use the command:
Expand Down
2 changes: 1 addition & 1 deletion cmd/daytona/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type GitProvider struct {
Name string
}

const configDoesntExistError = "config does not exist. Run `daytona server` to create a default profile or `daytona profile add` to connect to a remote server."
const configDoesntExistError = "config does not exist. Run `daytona serve` to create a default profile or `daytona profile add` to connect to a remote server."

func IsNotExist(err error) bool {
return err.Error() == configDoesntExistError
Expand Down
1 change: 1 addition & 0 deletions pkg/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func Execute() {
rootCmd.AddCommand(SshProxyCmd)
rootCmd.AddCommand(CreateCmd)
rootCmd.AddCommand(DeleteCmd)
rootCmd.AddCommand(ServeCmd)
rootCmd.AddCommand(ServerCmd)
rootCmd.AddCommand(ApiKeyCmd)
rootCmd.AddCommand(ContainerRegistryCmd)
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/server/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func getServiceConfig() (*service.Config, error) {
Name: "DaytonaServerDaemon",
DisplayName: "Daytona Server",
Description: "This is the Daytona Server daemon.",
Arguments: []string{"server"},
Arguments: []string{"serve"},
}

user := os.Getenv("USER")
Expand Down
279 changes: 279 additions & 0 deletions pkg/cmd/server/serve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
// Copyright 2024 Daytona Platforms Inc.
// SPDX-License-Identifier: Apache-2.0

package server

import (
"net/url"
"os"
"path/filepath"
"time"

"github.com/daytonaio/daytona/cmd/daytona/config"
"github.com/daytonaio/daytona/internal/util"
"github.com/daytonaio/daytona/pkg/api"
"github.com/daytonaio/daytona/pkg/apikey"
"github.com/daytonaio/daytona/pkg/db"
"github.com/daytonaio/daytona/pkg/logger"
"github.com/daytonaio/daytona/pkg/provider/manager"
"github.com/daytonaio/daytona/pkg/provisioner"
"github.com/daytonaio/daytona/pkg/server"
"github.com/daytonaio/daytona/pkg/server/apikeys"
"github.com/daytonaio/daytona/pkg/server/containerregistries"
"github.com/daytonaio/daytona/pkg/server/gitproviders"
"github.com/daytonaio/daytona/pkg/server/headscale"
"github.com/daytonaio/daytona/pkg/server/providertargets"
"github.com/daytonaio/daytona/pkg/server/workspaces"
started_view "github.com/daytonaio/daytona/pkg/views/server/started"

log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

var ServeCmd = &cobra.Command{
Use: "serve",
Short: "Run the server process in the current terminal session",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
if log.GetLevel() < log.InfoLevel {
// for now, force the log level to info when running the server
log.SetLevel(log.InfoLevel)
}

c, err := server.GetConfig()
if err != nil {
log.Fatal(err)
}

apiServer := api.NewApiServer(api.ApiServerConfig{
ApiPort: int(c.ApiPort),
})

logsDir, err := server.GetWorkspaceLogsDir()
if err != nil {
log.Fatal(err)
}
loggerFactory := logger.NewLoggerFactory(logsDir)

dbPath, err := getDbPath()
if err != nil {
log.Fatal(err)
}

dbConnection := db.GetSQLiteConnection(dbPath)
apiKeyStore, err := db.NewApiKeyStore(dbConnection)
if err != nil {
log.Fatal(err)
}
containerRegistryStore, err := db.NewContainerRegistryStore(dbConnection)
if err != nil {
log.Fatal(err)
}
gitProviderConfigStore, err := db.NewGitProviderConfigStore(dbConnection)
if err != nil {
log.Fatal(err)
}
providerTargetStore, err := db.NewProviderTargetStore(dbConnection)
if err != nil {
log.Fatal(err)
}
workspaceStore, err := db.NewWorkspaceStore(dbConnection)
if err != nil {
log.Fatal(err)
}

headscaleServer := headscale.NewHeadscaleServer(&headscale.HeadscaleServerConfig{
ServerId: c.Id,
FrpsDomain: c.Frps.Domain,
FrpsProtocol: c.Frps.Protocol,
HeadscalePort: c.HeadscalePort,
})
err = headscaleServer.Init()
if err != nil {
log.Fatal(err)
}

containerRegistryService := containerregistries.NewContainerRegistryService(containerregistries.ContainerRegistryServiceConfig{
Store: containerRegistryStore,
})

providerTargetService := providertargets.NewProviderTargetService(providertargets.ProviderTargetServiceConfig{
TargetStore: providerTargetStore,
})
apiKeyService := apikeys.NewApiKeyService(apikeys.ApiKeyServiceConfig{
ApiKeyStore: apiKeyStore,
})
providerManager := manager.NewProviderManager(manager.ProviderManagerConfig{
LogsDir: logsDir,
ProviderTargetService: providerTargetService,
ServerApiUrl: util.GetFrpcApiUrl(c.Frps.Protocol, c.Id, c.Frps.Domain),
ServerDownloadUrl: getDaytonaScriptUrl(c),
ServerUrl: util.GetFrpcServerUrl(c.Frps.Protocol, c.Id, c.Frps.Domain),
RegistryUrl: c.RegistryUrl,
BaseDir: c.ProvidersDir,
})
provisioner := provisioner.NewProvisioner(provisioner.ProvisionerConfig{
ProviderManager: providerManager,
})

workspaceService := workspaces.NewWorkspaceService(workspaces.WorkspaceServiceConfig{
WorkspaceStore: workspaceStore,
TargetStore: providerTargetStore,
ApiKeyService: apiKeyService,
ContainerRegistryStore: containerRegistryStore,
ServerApiUrl: util.GetFrpcApiUrl(c.Frps.Protocol, c.Id, c.Frps.Domain),
ServerUrl: util.GetFrpcServerUrl(c.Frps.Protocol, c.Id, c.Frps.Domain),
DefaultProjectImage: c.DefaultProjectImage,
DefaultProjectUser: c.DefaultProjectUser,
DefaultProjectPostStartCommands: c.DefaultProjectPostStartCommands,
Provisioner: provisioner,
LoggerFactory: loggerFactory,
})
gitProviderService := gitproviders.NewGitProviderService(gitproviders.GitProviderServiceConfig{
ConfigStore: gitProviderConfigStore,
})

server := server.GetInstance(&server.ServerInstanceConfig{
Config: *c,
TailscaleServer: headscaleServer,
ProviderTargetService: providerTargetService,
ContainerRegistryService: containerRegistryService,
ApiKeyService: apiKeyService,
WorkspaceService: workspaceService,
GitProviderService: gitProviderService,
ProviderManager: providerManager,
})

errCh := make(chan error)

err = server.Start(errCh)
if err != nil {
log.Fatal(err)
}

go func() {
err := apiServer.Start()
if err != nil {
log.Fatal(err)
}
}()

go func() {
err := <-errCh
if err != nil {
log.Fatal(err)
}
}()

err = waitForServerToStart(apiServer)

if err != nil {
log.Fatal(err)
}

printServerStartedMessage(c, false)

err = setDefaultConfig(server)
if err != nil {
log.Fatal(err)
}

err = <-errCh
if err != nil {
log.Fatal(err)
}
},
}

func waitForServerToStart(apiServer *api.ApiServer) error {
var err error
for i := 0; i < 3; i++ {
err = apiServer.HealthCheck()
if err != nil {
time.Sleep(3 * time.Second)
continue
}

return nil
}

return err
}

func getDaytonaScriptUrl(config *server.Config) string {
url, _ := url.JoinPath(util.GetFrpcApiUrl(config.Frps.Protocol, config.Id, config.Frps.Domain), "binary", "script")
return url
}

func printServerStartedMessage(c *server.Config, runAsDaemon bool) {
started_view.Render(c.ApiPort, util.GetFrpcApiUrl(c.Frps.Protocol, c.Id, c.Frps.Domain), runAsDaemon)
}

func getDbPath() (string, error) {
userConfigDir, err := os.UserConfigDir()
if err != nil {
return "", err
}

dir := filepath.Join(userConfigDir, "daytona")

err = os.MkdirAll(dir, 0755)
if err != nil {
return "", err
}

return filepath.Join(dir, "db"), nil
}

func setDefaultConfig(server *server.Server) error {
existingConfig, err := config.GetConfig()
if err != nil && !config.IsNotExist(err) {
return err
}

if existingConfig != nil {
for _, profile := range existingConfig.Profiles {
if profile.Id == "default" {
return nil
}
}
}

apiKey, err := server.ApiKeyService.Generate(apikey.ApiKeyTypeClient, "default")
if err != nil {
return err
}

if existingConfig != nil {
err := existingConfig.AddProfile(config.Profile{
Id: "default",
Name: "default",
Api: config.ServerApi{
Url: "http://localhost:3000",
Key: apiKey,
},
})
if err != nil {
return err
}

return existingConfig.Save()
}

config := &config.Config{
ActiveProfileId: "default",
DefaultIdeId: config.DefaultIdeId,
Profiles: []config.Profile{
{
Id: "default",
Name: "default",
Api: config.ServerApi{
Url: "http://localhost:3000",
Key: apiKey,
},
},
},
}

return config.Save()
}
Loading

0 comments on commit b6a43c3

Please sign in to comment.