Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restructure Code #21

Merged
merged 3 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ WORKDIR /build
COPY kubelab-backend/go.mod kubelab-backend/go.sum kubelab-backend/main.go ./
COPY kubelab-backend/hooks ./hooks
COPY kubelab-backend/pkg ./pkg
COPY kubelab-backend/vcluster-values.yaml ./vcluster-values.yaml
RUN apk --no-cache add upx make git gcc libtool musl-dev ca-certificates dumb-init \
&& go mod tidy \
&& CGO_ENABLED=0 go build \
Expand All @@ -20,6 +21,7 @@ RUN npm run build
FROM alpine as runtime
WORKDIR /app/kubelab
COPY --from=backend-builder /build/kubelab /app/kubelab/kubelab
COPY --from=backend-builder /build/vcluster-values.yaml /app/kubelab/vcluster-values.yaml
COPY ./kubelab-backend/pb_migrations ./pb_migrations
COPY --from=ui-builder /build/build /app/kubelab/pb_public
EXPOSE 8090
Expand Down
2 changes: 1 addition & 1 deletion kubelab-backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
/pb_data
/tmp
/bin
kubelab
./pocketbase
kubelab
4 changes: 2 additions & 2 deletions kubelab-backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ go 1.20
require (
github.com/caarlos0/env/v8 v8.0.0
github.com/pocketbase/dbx v1.10.0
github.com/pocketbase/pocketbase v0.17.4
gopkg.in/yaml.v2 v2.4.0
github.com/pocketbase/pocketbase v0.17.5
helm.sh/helm/v3 v3.11.2
k8s.io/api v0.27.2
k8s.io/apimachinery v0.27.2
Expand Down Expand Up @@ -101,6 +100,7 @@ require (
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.26.3 // indirect
k8s.io/apiserver v0.26.3 // indirect
Expand Down
4 changes: 2 additions & 2 deletions kubelab-backend/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -600,8 +600,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pocketbase/dbx v1.10.0 h1:58VIT7r6T+BnVbYVosvGBsPjQEic3/VFRYGT823vWSQ=
github.com/pocketbase/dbx v1.10.0/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
github.com/pocketbase/pocketbase v0.17.4 h1:PwT84MMoXPVdv/EC/dfanCsqfByY1xMNlaNWvlM5MfY=
github.com/pocketbase/pocketbase v0.17.4/go.mod h1:IqsgywiDX5ewaUdeG+0NNckBLWp5mFhJSca1PH9zSp4=
github.com/pocketbase/pocketbase v0.17.5 h1:3KwMrlSMt1rcwZK2Z1/wFa5p/rkDHpcyeCRvUDXMb+Y=
github.com/pocketbase/pocketbase v0.17.5/go.mod h1:IqsgywiDX5ewaUdeG+0NNckBLWp5mFhJSca1PH9zSp4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/poy/onpar v0.0.0-20200406201722-06f95a1c68e8/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU=
Expand Down
277 changes: 6 additions & 271 deletions kubelab-backend/main.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
package main

import (
"io"
"log"
"net/http"
"os"
"path/filepath"
"strings"
"time"

"github.com/natrontech/kubelab/hooks"
"github.com/natrontech/kubelab/pkg/controller"
"github.com/natrontech/kubelab/pkg/env"
"github.com/natrontech/kubelab/pkg/helm"
"github.com/natrontech/kubelab/pkg/k8s"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/core"
"github.com/pocketbase/pocketbase/models"
"github.com/pocketbase/pocketbase/plugins/jsvm"
"github.com/pocketbase/pocketbase/plugins/migratecmd"
"gopkg.in/yaml.v2"
v1 "k8s.io/api/core/v1"
)

func defaultPublicDir() string {
Expand Down Expand Up @@ -76,271 +70,12 @@ func main() {
})

app.OnRecordBeforeUpdateRequest().Add(func(e *core.RecordUpdateEvent) error {
// check collection name
if e.Collection.Name == "lab_sessions" {
if e.Record.GetBool("clusterRunning") {

// deploy a new vcluster
helmclient, err := helm.CreateHelmClient(e.Record.GetString("lab"), e.Record.GetString("user"))
if err != nil {
log.Println(err)
return err
}

err = helm.AddHelmRepositoryToClient(helmclient, "loft-sh", "https://charts.loft.sh")
if err != nil {
log.Println(err)
return err
}

// create yaml struct
yamlValues := struct {
Sync struct {
PersistentVolumes struct {
Enabled bool `yaml:"enabled"`
} `yaml:"persistentvolumes"`
StorageClasses struct {
Enabled bool `yaml:"enabled"`
} `yaml:"storageclasses"`
Ingresses struct {
Enabled bool `yaml:"enabled"`
} `yaml:"ingresses"`
HostStorageClasses struct {
Enabled bool `yaml:"enabled"`
} `yaml:"hoststorageclasses"`
} `yaml:"sync"`
Storage struct {
Persistence bool `yaml:"persistence"`
} `yaml:"storage"`
Isolation struct {
Enabled bool `yaml:"enabled"`
PodSecurityStandard string `yaml:"podSecurityStandard"`
NodeProxyPermission struct {
Enabled bool `yaml:"enabled"`
} `yaml:"nodeProxyPermission"`
ResourceQuota struct {
Enabled bool `yaml:"enabled"`
} `yaml:"resourceQuota"`
LimitRange struct {
Enabled bool `yaml:"enabled"`
Default map[string]string `yaml:"default"`
DefaultRequest map[string]string `yaml:"defaultRequest"`
} `yaml:"limitRange"`
NetworkPolicy struct {
Enabled bool `yaml:"enabled"`
OutgoingConnections struct {
IPBlock struct {
CIDR string `yaml:"cidr"`
Except []string `yaml:"except"`
} `yaml:"ipBlock"`
} `yaml:"outgoingConnections"`
} `yaml:"networkPolicy"`
} `yaml:"isolation"`
}{}

// set values
yamlValues.Sync.PersistentVolumes.Enabled = true
yamlValues.Sync.StorageClasses.Enabled = false
yamlValues.Sync.Ingresses.Enabled = true
yamlValues.Sync.HostStorageClasses.Enabled = true
yamlValues.Storage.Persistence = false
yamlValues.Isolation.Enabled = true
yamlValues.Isolation.PodSecurityStandard = "baseline"
yamlValues.Isolation.NodeProxyPermission.Enabled = false
yamlValues.Isolation.ResourceQuota.Enabled = false
yamlValues.Isolation.LimitRange.Enabled = true
yamlValues.Isolation.LimitRange.Default = map[string]string{
"ephemeral-storage": "8Gi",
"memory": "512Mi",
"cpu": "1",
}
yamlValues.Isolation.LimitRange.DefaultRequest = map[string]string{
"ephemeral-storage": "3Gi",
"memory": "128Mi",
"cpu": "100m",
}
yamlValues.Isolation.NetworkPolicy.Enabled = true
yamlValues.Isolation.NetworkPolicy.OutgoingConnections.IPBlock.CIDR = "8.8.8.8/32"

// convert to yaml
yamlValuesBytes, err := yaml.Marshal(yamlValues)
if err != nil {
log.Println(err)
return err
}

_, err = helm.CreateOrUpdateHelmRelease(
helmclient,
"loft-sh/vcluster",
"vcluster",
helm.GetNamespaceName(e.Record.GetString("lab"), e.Record.GetString("user")),
"0.15.2",
string(string(yamlValuesBytes)),
)
if err != nil {
log.Println(err)
return err
}

err = k8s.CreateResourceQuota(helm.GetNamespaceName(e.Record.GetString("lab"), e.Record.GetString("user")), env.Config.ResourceName, env.Config.PodsLimit, env.Config.StorageLimit)
if err != nil {
log.Println(err)
return err
}

time.Sleep(15 * time.Second)

} else {
// delete the namespace
err := k8s.DeleteNamespace(helm.GetNamespaceName(e.Record.GetString("lab"), e.Record.GetString("user")))
if err != nil {
log.Println(err)
}

time.Sleep(15 * time.Second)
}
}

if e.Collection.Name == "exercise_sessions" {
if e.Record.GetBool("agentRunning") {
var err error
var exercise *models.Record
// retrieve the exercise
exercise, err = app.Dao().FindRecordById("exercises", e.Record.GetString("exercise"))
if err != nil {
log.Println(err)
return err
}

// check if the namespace exists
err = k8s.CreateNamespace(helm.GetNamespaceName(exercise.GetString("lab"), e.Record.GetString("user")))
if err != nil {
log.Println(err)
} else {
return err
}

// check if vcluster pod exists 'vcluster-0'
_, err = k8s.GetPodByName(helm.GetNamespaceName(exercise.GetString("lab"), e.Record.GetString("user")), "vcluster-0")
if err != nil {
log.Println(err)
return err
}

// get kubeconfig secret called 'vc-vcluster'
var secret *v1.Secret
secret, err = k8s.GetSecretByName(helm.GetNamespaceName(exercise.GetString("lab"), e.Record.GetString("user")), "vc-vcluster")
if err != nil {
log.Println(err)
return err
}

// get exercise.GetString("bootstrap") this is a url to a bootstrap script over https github raw
bootstrap, err := http.Get(exercise.GetString("bootstrap"))
if err != nil {
log.Println(err)
return err
}

defer bootstrap.Body.Close()

// read the body
bootstrapBody, err := io.ReadAll(bootstrap.Body)
if err != nil {
log.Println(err)
return err
}

check, err := http.Get(exercise.GetString("check"))
if err != nil {
log.Println(err)
return err
}

defer check.Body.Close()

// read the body
checkBody, err := io.ReadAll(check.Body)
if err != nil {
log.Println(err)
return err
}

// create a new deployment
_, err = k8s.CreateDeployment(
"kubelab-agent-"+exercise.Id,
helm.GetNamespaceName(exercise.GetString("lab"), e.Record.GetString("user")),
env.Config.KubelabImage,
1,
string(secret.Data["config"]),
string(bootstrapBody),
string(checkBody),
env.Config.AllowedHosts,
)
if err != nil {
log.Println(err)
}

// create a new service
_, err = k8s.CreateService(
helm.GetNamespaceName(exercise.GetString("lab"), e.Record.GetString("user")),
"kubelab-agent-"+exercise.Id,
8376,
)
if err != nil {
log.Println(err)
}

// create a new ingress
_, err = k8s.CreateIngress(
helm.GetNamespaceName(exercise.GetString("lab"), e.Record.GetString("user")),
"kubelab-"+exercise.GetString("lab")+"-"+exercise.Id+"-"+e.Record.GetString("user"),
env.Config.AllowedHosts,
"kubelab-agent-"+exercise.Id,
"kubelab-"+exercise.GetString("lab")+"-"+exercise.Id+"-"+e.Record.GetString("user"),
)

// check if deployment is ready
err = k8s.WaitForDeployment(helm.GetNamespaceName(exercise.GetString("lab"), e.Record.GetString("user")), "kubelab-agent-"+exercise.Id)
if err != nil {
log.Println(err)
}

// sleep for 5 seconds
time.Sleep(5 * time.Second)

} else {
var err error
var exercise *models.Record
// retrieve the exercise
exercise, err = app.Dao().FindRecordById("exercises", e.Record.GetString("exercise"))
if err != nil {
log.Println(err)
return err
}
// delete the deployment
err = k8s.DeleteDeployment(helm.GetNamespaceName(exercise.GetString("lab"), e.Record.GetString("user")), "kubelab-agent-"+exercise.Id)
if err != nil {
log.Println(err)
// return err
}

// delete the service
err = k8s.DeleteService(helm.GetNamespaceName(exercise.GetString("lab"), e.Record.GetString("user")), "kubelab-agent-"+exercise.Id)
if err != nil {
log.Println(err)
// return err
}

// delete the ingress
err = k8s.DeleteIngress(helm.GetNamespaceName(exercise.GetString("lab"), e.Record.GetString("user")), "kubelab-"+exercise.GetString("lab")+"-"+exercise.Id+"-"+e.Record.GetString("user"))
if err != nil {
log.Println(err)
// return err
}
}
switch e.Collection.Name {
case "lab_sessions":
return controller.HandleLabSessions(e, app)
case "exercise_sessions":
return controller.HandleExerciseSessions(e, app)
}

return nil
})

Expand Down
Loading