feat: Add templates to create working release (#422)

* Add templates

* Move API structs to codersdk

* Back to green tests!

* It all works, but now with tea! 🧋

* It works!

* Add cancellation to provisionerd

* Tests pass!

* Add deletion of workspaces and projects

* Fix agent lock

* Add clog

* Fix linting errors

* Remove unused CLI tests

* Rename daemon to start

* Fix leaking command

* Fix promptui test

* Update agent connection frequency

* Skip login tests on Windows

* Increase tunnel connect timeout

* Fix templater

* Lower test requirements

* Fix embed

* Disable promptui tests for Windows

* Fix write newline

* Fix PTY write newline

* Fix CloseReader

* Fix compilation on Windows

* Fix linting error

* Remove bubbletea

* Cleanup readwriter

* Use embedded templates instead of serving over API

* Move templates to examples

* Improve workspace create flow

* Fix Windows build

* Fix tests

* Fix linting errors

* Fix untar with extracting max size

* Fix newline char
This commit is contained in:
Kyle Carberry
2022-03-22 13:17:50 -06:00
committed by GitHub
parent 2818b3ce6d
commit c451f4e685
138 changed files with 7314 additions and 2331 deletions
+29 -45
View File
@@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"net/http"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
@@ -13,45 +12,12 @@ import (
"github.com/moby/moby/pkg/namesgenerator"
"golang.org/x/xerrors"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/database"
"github.com/coder/coder/httpapi"
"github.com/coder/coder/httpmw"
)
// Organization is the JSON representation of a Coder organization.
type Organization struct {
ID string `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
CreatedAt time.Time `json:"created_at" validate:"required"`
UpdatedAt time.Time `json:"updated_at" validate:"required"`
}
// CreateProjectVersionRequest enables callers to create a new Project Version.
type CreateProjectVersionRequest struct {
// ProjectID optionally associates a version with a project.
ProjectID *uuid.UUID `json:"project_id"`
StorageMethod database.ProvisionerStorageMethod `json:"storage_method" validate:"oneof=file,required"`
StorageSource string `json:"storage_source" validate:"required"`
Provisioner database.ProvisionerType `json:"provisioner" validate:"oneof=terraform echo,required"`
// ParameterValues allows for additional parameters to be provided
// during the dry-run provision stage.
ParameterValues []CreateParameterRequest `json:"parameter_values"`
}
// CreateProjectRequest provides options when creating a project.
type CreateProjectRequest struct {
Name string `json:"name" validate:"username,required"`
// VersionID is an in-progress or completed job to use as
// an initial version of the project.
//
// This is required on creation to enable a user-flow of validating a
// project works. There is no reason the data-model cannot support
// empty projects, but it doesn't make sense for users.
VersionID uuid.UUID `json:"project_version_id" validate:"required"`
}
func (*api) organization(rw http.ResponseWriter, r *http.Request) {
organization := httpmw.OrganizationParam(r)
render.Status(r, http.StatusOK)
@@ -80,12 +46,12 @@ func (api *api) provisionerDaemonsByOrganization(rw http.ResponseWriter, r *http
func (api *api) postProjectVersionsByOrganization(rw http.ResponseWriter, r *http.Request) {
apiKey := httpmw.APIKey(r)
organization := httpmw.OrganizationParam(r)
var req CreateProjectVersionRequest
var req codersdk.CreateProjectVersionRequest
if !httpapi.Read(rw, r, &req) {
return
}
if req.ProjectID != nil {
_, err := api.Database.GetProjectByID(r.Context(), *req.ProjectID)
if req.ProjectID != uuid.Nil {
_, err := api.Database.GetProjectByID(r.Context(), req.ProjectID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: "project does not exist",
@@ -152,9 +118,9 @@ func (api *api) postProjectVersionsByOrganization(rw http.ResponseWriter, r *htt
}
var projectID uuid.NullUUID
if req.ProjectID != nil {
if req.ProjectID != uuid.Nil {
projectID = uuid.NullUUID{
UUID: *req.ProjectID,
UUID: req.ProjectID,
Valid: true,
}
}
@@ -187,7 +153,7 @@ func (api *api) postProjectVersionsByOrganization(rw http.ResponseWriter, r *htt
// Create a new project in an organization.
func (api *api) postProjectsByOrganization(rw http.ResponseWriter, r *http.Request) {
var createProject CreateProjectRequest
var createProject codersdk.CreateProjectRequest
if !httpapi.Read(rw, r, &createProject) {
return
}
@@ -232,7 +198,7 @@ func (api *api) postProjectsByOrganization(rw http.ResponseWriter, r *http.Reque
return
}
var project Project
var project codersdk.Project
err = api.Database.InTx(func(db database.Store) error {
dbProject, err := db.InsertProject(r.Context(), database.InsertProjectParams{
ID: uuid.New(),
@@ -256,6 +222,22 @@ func (api *api) postProjectsByOrganization(rw http.ResponseWriter, r *http.Reque
if err != nil {
return xerrors.Errorf("insert project version: %s", err)
}
for _, parameterValue := range createProject.ParameterValues {
_, err = db.InsertParameterValue(r.Context(), database.InsertParameterValueParams{
ID: uuid.New(),
Name: parameterValue.Name,
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
Scope: database.ParameterScopeProject,
ScopeID: dbProject.ID.String(),
SourceScheme: parameterValue.SourceScheme,
SourceValue: parameterValue.SourceValue,
DestinationScheme: parameterValue.DestinationScheme,
})
if err != nil {
return xerrors.Errorf("insert parameter value: %w", err)
}
}
project = convertProject(dbProject, 0)
return nil
})
@@ -272,7 +254,9 @@ func (api *api) postProjectsByOrganization(rw http.ResponseWriter, r *http.Reque
func (api *api) projectsByOrganization(rw http.ResponseWriter, r *http.Request) {
organization := httpmw.OrganizationParam(r)
projects, err := api.Database.GetProjectsByOrganization(r.Context(), organization.ID)
projects, err := api.Database.GetProjectsByOrganization(r.Context(), database.GetProjectsByOrganizationParams{
OrganizationID: organization.ID,
})
if errors.Is(err, sql.ErrNoRows) {
err = nil
}
@@ -338,8 +322,8 @@ func (api *api) projectByOrganizationAndName(rw http.ResponseWriter, r *http.Req
}
// convertOrganization consumes the database representation and outputs an API friendly representation.
func convertOrganization(organization database.Organization) Organization {
return Organization{
func convertOrganization(organization database.Organization) codersdk.Organization {
return codersdk.Organization{
ID: organization.ID,
Name: organization.Name,
CreatedAt: organization.CreatedAt,