Skip to content
This repository has been archived by the owner on Sep 4, 2024. It is now read-only.

Add a Web view #17

Merged
merged 11 commits into from
Apr 19, 2018
Merged

Add a Web view #17

merged 11 commits into from
Apr 19, 2018

Conversation

apostergiou
Copy link
Contributor

With this we add a web view for the service for monitoring the available jobs.

The feature introduces:

  • an index page to view all available jobs and relevant information such as start time and state
  • a show page for each where we can view the job result information alongside the job build logs. The show page provides a web tailf like feature for monitoring the build log as it unfolds.

@apostergiou apostergiou force-pushed the web-view branch 2 times, most recently from ff75cdc to 9d080f0 Compare April 6, 2018 09:28
@apostergiou apostergiou requested a review from agis April 6, 2018 09:32
@apostergiou apostergiou self-assigned this Apr 6, 2018
job.go Outdated
@@ -192,3 +192,16 @@ func (j *Job) String() string {
"{project=%s params=%s group=%s id=%s}",
j.Project, j.Params, j.Group, j.ID[:7])
}

func jobPath(buildPath, project, id string) (string, string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be an exported method on Job and have proper documentation. It should also be given a more accurate name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.

job.go Outdated
func jobPath(buildPath, project, id string) (string, string, error) {
pPath := filepath.Join(buildPath, project, "pending", id)
rPath := filepath.Join(buildPath, project, "ready", id)
if _, err := os.Stat(pPath); err == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

We do not use this style, break the error check to a new line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

local_assets.go Outdated
import "net/http"

func staticFs() (http.FileSystem, error) {
return http.Dir("public"), nil
Copy link
Contributor

Choose a reason for hiding this comment

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

I see no need for this to be a method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

True, deleted.


<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should also embed these? I mean, we do embed foundation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, moved inside the head tag.

}
}

ReactDOM.render(<Jobs every={5000} />, JobsRoot)
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe we can reduce this to 3sec.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, let's try with 3sec.

sleep 30
touch artifacts/out.txt

exit 0
Copy link
Contributor

Choose a reason for hiding this comment

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

This directory should be here only if it's actually used in mistry's tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, removed.

@@ -23,6 +23,9 @@ type BuildResult struct {

// The method by which the build artifacts can be fetched.
TransportMethod TransportMethod

// The job's start time
StartTime string
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be a time.Time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Output string
Log string
State string
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I see many duplicated fields between this and Job. Can we merge this into Job? It seems redundant.

Copy link
Contributor

Choose a reason for hiding this comment

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

Bump.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

server.go Outdated
clients map[*C]bool

// A connection queue used to track all open connections by their id.
cq map[string]bool
Copy link
Contributor

Choose a reason for hiding this comment

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

All these fields and methods could go into a separate struct, ideally extracted into its own package and exporting its http handlers that one can use selectively.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Extracted into a broker package.

tail_reader.go Outdated
} else if err != io.EOF {
return n, err
}
time.Sleep(5 * time.Second)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you elaborate on why is this sleep needed?

Copy link
Contributor Author

@apostergiou apostergiou Apr 6, 2018

Choose a reason for hiding this comment

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

Thanks for commenting on this. I added sleep here as a way to give up control to the scheduler while developing the feature. Nevertheless, using it here feels like an anti-pattern and doesn't add anything really so I removed it.

@apostergiou
Copy link
Contributor Author

apostergiou commented Apr 6, 2018

We should decide when to kill the goroutine that utilizes Tailer. As it stands, the goroutine behaves like tail, reading forever that is, which in our case can and should be avoided. When the job is ready the build log is considered closed, so we should stop tailing it.

On a similar note, as far as the front end is concerned, we could improve the view by updating it when the job is ready. Short polling is a good choice for this.

@agis agis added this to the 1.0 milestone Apr 12, 2018
@agis
Copy link
Contributor

agis commented Apr 12, 2018

This PR should also update the relevant README section.

@apostergiou apostergiou force-pushed the web-view branch 2 times, most recently from 0d60114 to 9569b0f Compare April 17, 2018 08:20
Copy link
Contributor

@agis agis left a comment

Choose a reason for hiding this comment

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

As we discussed, this should be revamped soon. We should open another issue for it with all the ideas we've discussed.

README.md Outdated
@@ -16,7 +16,7 @@ Features include:
- caching and reusing build results
- efficient use of disk space due to copy-on-write semantics
- a simple JSON API for interacting with the service
- ([wip](https://github.com/skroutz/mistry/pull/17)) a web view for inspecting the progress and result of builds
- a web view for inspecting the progress and result of builds
Copy link
Contributor

Choose a reason for hiding this comment

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

This needs a rebase. I slightly updated this point.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

True, fixed.

job.go Outdated
pPath := filepath.Join(path, project, "pending", id)
rPath := filepath.Join(path, project, "ready", id)
_, err := os.Stat(pPath)
if err == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

What if error is non nil but also not a "not exist" error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It will return the error.

Copy link
Contributor

Choose a reason for hiding this comment

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

You're right, nvm.

job.go Outdated
if err == nil {
return "ready", nil
}
return "", err
Copy link
Contributor

@agis agis Apr 17, 2018

Choose a reason for hiding this comment

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

I don't like the fact that we may return an empty string and no error. What would such a case indicate?

Copy link
Contributor

Choose a reason for hiding this comment

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

Correction: we do return a non-nil error here, but still, what does an empty string and a "ready path doesn't exist" error means?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for noticing, maybe we could return a custom error then in which we have concatenated any errors with a meaningful message, e.g. "state not found for job %s error %v"

Copy link
Contributor

Choose a reason for hiding this comment

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

A simple "job %s not found error", j I think is sufficient.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

job.go Outdated
// State determines the job's current state by using it's path in the filesystem.
func (j *Job) State(path, project, id string) (string, error) {
pPath := filepath.Join(path, project, "pending", id)
rPath := filepath.Join(path, project, "ready", id)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can't we use j.PendingBuildPath and j.ReadyBuildPath?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think we can as the job has been initialized with an empty struct

j := &Job{}
state, err := j.GetState(s.cfg.BuildPath, project, id)

Probably we can introduce a GetJob function that would find a job, WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

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

Didn't notice that. Jobs should not be initialized this way, we should only use NewJob().


<div class="top-bar">
<div class="top-bar-left">
<h1><a href="/">Mistry</a></h1>
Copy link
Contributor

Choose a reason for hiding this comment

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

lowercase

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

server_test.go Outdated
@@ -65,3 +72,87 @@ func TestLoad(t *testing.T) {
<-results
}
}

func TestIndex(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Tests functions are named according to the functions they test. This should be named TestHandleIndex for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

server_test.go Outdated
}
}

func TestShow(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

server_test.go Outdated
}

func TestShow(t *testing.T) {
// Build a job.
Copy link
Contributor

Choose a reason for hiding this comment

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

Redundant.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

@@ -23,6 +24,9 @@ type BuildResult struct {

// The method by which the build artifacts can be fetched.
TransportMethod TransportMethod

// The job's start time
StartTime time.Time
Copy link
Contributor

Choose a reason for hiding this comment

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

This belongs in Job.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved to Job.

Output string
Log string
State string
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Bump.

@agis
Copy link
Contributor

agis commented Apr 17, 2018

Please also add a small section in the README on where the web view can be found.

job.go Outdated
func (j *Job) State(path, project, id string) (string, error) {
pPath := filepath.Join(path, project, "pending", id)
rPath := filepath.Join(path, project, "ready", id)
_, err := os.Stat(pPath)
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead for checking through the filesystem, which is racy if other methods of mistry manipulate the same paths, you can use the JobQueue struct which the server already contains.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, but while I agree that this is racy, what would happen when we restart the server?The JobQueue would be empty then and the index page wouldn't display any jobs even if there are some. That's why I believe we should scan the filesystem.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's revisit this at the next iteration of the web view. It's fine for now.

@agis
Copy link
Contributor

agis commented Apr 17, 2018

We should add a new issue tracking what we need to revamp in the implementation.

@apostergiou
Copy link
Contributor Author

We should add a new issue tracking what we need to revamp in the implementation.

Tracked on #38.

apostergiou and others added 9 commits April 19, 2018 14:11
Also extend job struct and add JSON encodings
Tailer provides a tailf functionality for reading from continuously
updated files.
The broker package implements a registry for open client connections
and functions that enable listening and broadcasting events for the
registered clients.
This generally makes sense but also assists us in web view.
@apostergiou apostergiou merged commit 5595bf9 into master Apr 19, 2018
@apostergiou apostergiou deleted the web-view branch April 19, 2018 13:35
agis added a commit that referenced this pull request Apr 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants