Skip to content

do not merge: WIP provisionerd #84

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

Closed
wants to merge 21 commits into from
Closed

do not merge: WIP provisionerd #84

wants to merge 21 commits into from

Conversation

kylecarbs
Copy link
Member

@kylecarbs kylecarbs commented Jan 29, 2022

Creates the provisionerd service that interfaces with
coderd to process provision jobs!

This modifies a prior migration which is typically forbidden,
but because we're pre-production deployment I felt grouping
would be helpful to future contributors.

This adds database functions that are required for the provisioner
daemon and job queue logic.
Adds a projectparameter package to compute build-time project
values for a provided scope.

This package will be used to return which variables are being
used for a build, and can visually indicate the hierarchy to
a user.
Provisionerd communicates with coderd over a multiplexed
WebSocket serving dRPC. This adds a roughly accurate protocol
definition.

It shares definitions with "provisioner.proto" for simple
interop with provisioners!
Creates the provisionerd service that interfaces with
coderd to process provision jobs!
@kylecarbs kylecarbs self-assigned this Jan 29, 2022
@kylecarbs kylecarbs changed the base branch from main to provisionerdproto January 29, 2022 16:04
@codecov
Copy link

codecov bot commented Jan 29, 2022

Codecov Report

Merging #84 (82a5750) into main (ac617e1) will increase coverage by 2.66%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##             main      #84      +/-   ##
==========================================
+ Coverage   72.06%   74.72%   +2.66%     
==========================================
  Files          91       53      -38     
  Lines        3751      550    -3201     
  Branches       59       59              
==========================================
- Hits         2703      411    -2292     
+ Misses        829      127     -702     
+ Partials      219       12     -207     
Flag Coverage Δ
unittest-go-macos-latest ?
unittest-go-ubuntu-latest ?
unittest-go-windows-latest ?
unittest-js 74.72% <ø> (ø)
Impacted Files Coverage Δ
coderd/coderd.go
coderd/coderdtest/coderdtest.go
coderd/projects.go
coderd/workspaces.go
codersdk/projects.go
codersdk/workspaces.go
peerbroker/listen.go
provisionersdk/serve.go
provisionersdk/transport.go
peer/channel.go
... and 28 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ac617e1...82a5750. Read the comment docs.

}
return nil, readBodyAsError(res)
}
session, err := yamux.Client(websocket.NetConn(context.Background(), conn, websocket.MessageBinary), nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, why is this using context.Background() instead of ctx?

coderd/coderd.go Outdated
@@ -39,6 +42,7 @@ func New(options *Options) http.Handler {
})
r.Post("/login", users.loginWithPassword)
r.Post("/logout", users.logout)
r.Get("/provisionerd", provisionerd.listen)
Copy link
Contributor

@bryphe-coder bryphe-coder Jan 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At very first glance, I thought this was an endpoint the browser would listen to for updates over a socket - but after reading the PR, I realized this is for the provisionerd process to establish a socket connection with coderd.

It might be helpful to have a comment to this effect, like:

// Endpoint that facilitates connection with running `provisionerd` processes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we should change the endpoint? I genuinely wasn't sure what to call this!

Comment on lines 99 to 101
if a.isClosed() {
return
}
Copy link
Contributor

@bryphe-coder bryphe-coder Jan 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, why is this needed if we have this right below:

		select {
		case <-a.closed:
			return

?

Is it for performance, or just to be explicit?

func (a *API) acquireJob() {
a.opts.Logger.Debug(context.Background(), "acquiring new job")
var err error
a.activeJobMutex.Lock()
Copy link
Contributor

@bryphe-coder bryphe-coder Jan 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be using the

	a.activeJobMutex.Lock()
	defer a.activeJobMutex.Unlock()

pattern?

I see some cases below where we potentially modify activeJob again, that isn't protected by the mutex - so I'd be worried about a potential race (like Line 147/148)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just we call into cancelActiveJob later, which also acquires the mutex - go probably behaves like C, where it's an error to grab the mutex twice.

In that case - maybe we need to wrap Lines 147-150 in a mutex too?

return nil, xerrors.Errorf("unmarshal job data: %w", err)
}

// Validate that all parameters send from the provisioner daemon
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Validate that all parameters send from the provisioner daemon
// Validate that all parameters sent from the provisioner daemon

Comment on lines 40 to 42
// dRPC is a single-stream protocol by design. It's intended to operate
// a single HTTP-request per invocation. This multiplexes the WebSocket
// using yamux to enable multiple streams to function on a single connection.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the detailed comment; I wasn't sure why we were using yamux and this helps answer it 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we're going to add this kind of multiplexing, why not use gRPC, which supports this kind of multiplexing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gRPC multiplexes with HTTP/2. So although it kinda supports multiplexing, we'd have to proxy an HTTP/2 server over this WebSocket. I also had difficulty finding examples of gRPC over a WebSocket.

A benefit of the dRPC library is the simple abstraction. We could easily split the requests out into separate connections if we needed.

Happy to elaborate further!


switch jobType := a.activeJob.Type.(type) {
case *proto.AcquiredJob_ProjectImport_:
a.opts.Logger.Debug(context.Background(), "acquired job is project import",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the logging in this path

Copy link
Contributor

@bryphe-coder bryphe-coder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exciting to see this all come together! Looks good to me, just some questions / thoughts

Comment on lines 53 to 56
projectHistory, err := client.CreateProjectHistory(context.Background(), user.Organization, project.Name, coderd.CreateProjectVersionRequest{
StorageMethod: database.ProjectStorageMethodInlineArchive,
StorageSource: buffer.Bytes(),
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering how this flow will work on in the UI, to create new projects w/ the collateral necessary for the provisioner

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test cases are helpful in understanding the API!

Comment on lines 90 to 95
err := terraform.Serve(ctx, &terraform.ServeOptions{
ServeOptions: &provisionersdk.ServeOptions{
Transport: serverPipe,
},
})
require.NoError(t, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this test actually running against terraform?

Comment on lines 45 to 49
content := `resource "null_resource" "hi" {}`
err := writer.WriteHeader(&tar.Header{
Name: "main.tf",
Size: int64(len(content)),
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 😄

@kylecarbs
Copy link
Member Author

@bryphe-coder I shoulda made this a draft!

I'm refactoring a ton of this code actively, so I'll have to tag you again. Most of the testing and logic was scaffold a few hours ago, but the entire system is pretty much hooked up.

I'll need to add a few HTTP APIs, the workspace agent, and then I think we have the triangle!

Base automatically changed from provisionerdproto to main January 29, 2022 23:52
@kylecarbs kylecarbs changed the title feat: Add provisionerd service do not merge: WIP provisionerd Feb 1, 2022
@kylecarbs kylecarbs closed this Feb 4, 2022
@kylecarbs kylecarbs deleted the provisionerservice branch February 4, 2022 18:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants