{"action":"created","comment":{"url":"https://api.github.com/repos/hashicorp/nomad/pulls/comments/225274242","pull_request_review_id":164842033,"id":225274242,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIyNTI3NDI0Mg==","diff_hunk":"@@ -0,0 +1,1007 @@\n+// +build linux\n+\n+package rkt\n+\n+import (\n+\t\"bytes\"\n+\t\"fmt\"\n+\t\"net\"\n+\t\"os\"\n+\t\"os/exec\"\n+\t\"path/filepath\"\n+\t\"regexp\"\n+\t\"runtime\"\n+\t\"strings\"\n+\t\"syscall\"\n+\t\"time\"\n+\n+\tappcschema \"github.com/appc/spec/schema\"\n+\t\"github.com/hashicorp/go-hclog\"\n+\t\"github.com/hashicorp/go-plugin\"\n+\t\"github.com/hashicorp/go-version\"\n+\t\"github.com/hashicorp/nomad/client/config\"\n+\t\"github.com/hashicorp/nomad/client/driver/env\"\n+\tdstructs \"github.com/hashicorp/nomad/client/driver/structs\"\n+\tcstructs \"github.com/hashicorp/nomad/client/structs\"\n+\t\"github.com/hashicorp/nomad/drivers/shared/eventer\"\n+\t\"github.com/hashicorp/nomad/plugins/base\"\n+\t\"github.com/hashicorp/nomad/plugins/drivers\"\n+\t\"github.com/hashicorp/nomad/plugins/drivers/utils\"\n+\t\"github.com/hashicorp/nomad/plugins/shared/hclspec\"\n+\trktv1 \"github.com/rkt/rkt/api/v1\"\n+\t\"golang.org/x/net/context\"\n+\n+\t\"encoding/json\"\n+\t\"io/ioutil\"\n+\t\"math/rand\"\n+\t\"strconv\"\n+\n+\t\"github.com/hashicorp/consul-template/signals\"\n+\t\"github.com/hashicorp/nomad/client/driver/executor\"\n+)\n+\n+const (\n+\t// pluginName is the name of the plugin\n+\tpluginName = \"rkt\"\n+\n+\t// fingerprintPeriod is the interval at which the driver will send fingerprint responses\n+\tfingerprintPeriod = 30 * time.Second\n+\n+\t// minRktVersion is the earliest supported version of rkt. rkt added support\n+\t// for CPU and memory isolators in 0.14.0. We cannot support an earlier\n+\t// version to maintain an uniform interface across all drivers\n+\tminRktVersion = \"1.27.0\"\n+\n+\t// rktCmd is the command rkt is installed as.\n+\trktCmd = \"rkt\"\n+\n+\t// rktNetworkDeadline is how long to wait for container network to start\n+\trktNetworkDeadline = 1 * time.Minute\n+)\n+\n+var (\n+\t// pluginInfo is the response returned for the PluginInfo RPC\n+\tpluginInfo = &base.PluginInfoResponse{\n+\t\tType:             base.PluginTypeDriver,\n+\t\tPluginApiVersion: \"0.0.1\",\n+\t\tPluginVersion:    \"0.1.0\",\n+\t\tName:             pluginName,\n+\t}\n+\n+\t// configSpec is the hcl specification returned by the ConfigSchema RPC\n+\tconfigSpec = hclspec.NewObject(map[string]*hclspec.Spec{\n+\t\t\"volumes_enabled\": hclspec.NewDefault(\n+\t\t\thclspec.NewAttr(\"volumes_enabled\", \"bool\", false),\n+\t\t\thclspec.NewLiteral(\"true\"),\n+\t\t),\n+\t})\n+\n+\t// taskConfigSpec is the hcl specification for the driver config section of\n+\t// a taskConfig within a job. It is returned in the TaskConfigSchema RPC\n+\ttaskConfigSpec = hclspec.NewObject(map[string]*hclspec.Spec{\n+\t\t\"image\":              hclspec.NewAttr(\"image\", \"string\", true),\n+\t\t\"command\":            hclspec.NewAttr(\"command\", \"string\", false),\n+\t\t\"args\":               hclspec.NewAttr(\"args\", \"list(string)\", false),\n+\t\t\"trust_prefix\":       hclspec.NewAttr(\"trust_prefix\", \"string\", false),\n+\t\t\"dns_servers\":        hclspec.NewAttr(\"dns_servers\", \"list(string)\", false),\n+\t\t\"dns_search_domains\": hclspec.NewAttr(\"dns_search_domains\", \"list(string)\", false),\n+\t\t\"net\":                hclspec.NewAttr(\"net\", \"list(string)\", false),\n+\t\t\"port_map\":           hclspec.NewAttr(\"port_map\", \"list(map(string))\", false),\n+\t\t\"volumes\":            hclspec.NewAttr(\"volumes\", \"list(string)\", false),\n+\t\t\"insecure_options\":   hclspec.NewAttr(\"insecure_options\", \"list(string)\", false),\n+\t\t\"no_overlay\":         hclspec.NewAttr(\"no_overlay\", \"bool\", false),\n+\t\t\"debug\":              hclspec.NewAttr(\"debug\", \"bool\", false),\n+\t\t\"group\":              hclspec.NewAttr(\"group\", \"string\", false),\n+\t})\n+\n+\t// capabilities is returned by the Capabilities RPC and indicates what\n+\t// optional features this driver supports\n+\tcapabilities = &drivers.Capabilities{\n+\t\tSendSignals: true,\n+\t\tExec:        true,\n+\t\tFSIsolation: cstructs.FSIsolationChroot,\n+\t}\n+\n+\treRktVersion  = regexp.MustCompile(`rkt [vV]ersion[:]? (\\d[.\\d]+)`)\n+\treAppcVersion = regexp.MustCompile(`appc [vV]ersion[:]? (\\d[.\\d]+)`)\n+\n+\t_ drivers.DriverPlugin = (*RktDriver)(nil)\n+)\n+\n+// Config is the client configuration for the driver\n+type Config struct {\n+\t// VolumesEnabled allows tasks to bind host paths (volumes) inside their\n+\t// container. Binding relative paths is always allowed and will be resolved\n+\t// relative to the allocation's directory.\n+\tVolumesEnabled bool `codec:\"volumes_enabled\"`\n+}\n+\n+// TaskConfig is the driver configuration of a taskConfig within a job\n+type TaskConfig struct {\n+\tImageName        string              `codec:\"image\" cty:\"image\"`\n+\tCommand          string              `codec:\"command\" cty:\"command\"`\n+\tArgs             []string            `codec:\"args\" cty:\"args\"`\n+\tTrustPrefix      string              `codec:\"trust_prefix\" cty:\"trust_prefix\"`\n+\tDNSServers       []string            `codec:\"dns_servers\" cty:\"dns_servers\"`               // DNS Server for containers\n+\tDNSSearchDomains []string            `codec:\"dns_search_domains\" cty:\"dns_search_domains\"` // DNS Search domains for containers\n+\tNet              []string            `codec:\"net\" cty:\"net\"`                               // Networks for the containers\n+\tPortMap          []map[string]string `codec:\"port_map\" cty:\"port_map\"`                     // A map of host port and the port name defined in the image manifest file\n+\tVolumes          []string            `codec:\"volumes\" cty:\"volumes\"`                       // Host-Volumes to mount in, syntax: /path/to/host/directory:/destination/path/in/container[:readOnly]\n+\tInsecureOptions  []string            `codec:\"insecure_options\" cty:\"insecure_options\"`     // list of args for --insecure-options\n+\n+\tNoOverlay bool   `codec:\"no_overlay\" cty:\"no_overlay\"` // disable overlayfs for rkt run\n+\tDebug     bool   `codec:\"debug\" cty:\"debug\"`           // Enable debug option for rkt command\n+\tGroup     string `codec:\"group\" cty:\"group\"`           // Group override for the container\n+}\n+\n+// RktTaskState is the state which is encoded in the handle returned in\n+// StartTask. This information is needed to rebuild the taskConfig state and handler\n+// during recovery.\n+type RktTaskState struct {\n+\tReattachConfig *utils.ReattachConfig\n+\tTaskConfig     *drivers.TaskConfig\n+\tPid            int\n+\tStartedAt      time.Time\n+\tUUID           string\n+}\n+\n+// RktDriver is a driver for running images via Rkt\n+// We attempt to chose sane defaults for now, with more configuration available\n+// planned in the future\n+type RktDriver struct {\n+\t// eventer is used to handle multiplexing of TaskEvents calls such that an\n+\t// event can be broadcast to all callers\n+\teventer *eventer.Eventer\n+\n+\t// config is the driver configuration set by the SetConfig RPC\n+\tconfig *Config\n+\n+\t// tasks is the in memory datastore mapping taskIDs to execDriverHandles\n+\ttasks *taskStore\n+\n+\t// ctx is the context for the driver. It is passed to other subsystems to\n+\t// coordinate shutdown\n+\tctx context.Context\n+\n+\t// signalShutdown is called when the driver is shutting down and cancels the\n+\t// ctx passed to any subsystems\n+\tsignalShutdown context.CancelFunc\n+\n+\t// logger will log to the plugin output which is usually an 'executor.out'\n+\t// file located in the root of the TaskDir\n+\tlogger hclog.Logger\n+}\n+\n+func NewRktDriver(logger hclog.Logger) drivers.DriverPlugin {\n+\tctx, cancel := context.WithCancel(context.Background())\n+\tlogger = logger.Named(pluginName)\n+\treturn &RktDriver{\n+\t\teventer:        eventer.NewEventer(ctx, logger),\n+\t\tconfig:         &Config{},\n+\t\ttasks:          newTaskStore(),\n+\t\tctx:            ctx,\n+\t\tsignalShutdown: cancel,\n+\t\tlogger:         logger,\n+\t}\n+}\n+\n+func (d *RktDriver) PluginInfo() (*base.PluginInfoResponse, error) {\n+\treturn pluginInfo, nil\n+}\n+\n+func (d *RktDriver) ConfigSchema() (*hclspec.Spec, error) {\n+\treturn configSpec, nil\n+}\n+\n+func (d *RktDriver) SetConfig(data []byte) error {\n+\tvar config Config\n+\tif err := base.MsgPackDecode(data, &config); err != nil {\n+\t\treturn err\n+\t}\n+\n+\td.config = &config\n+\treturn nil\n+}\n+\n+func (d *RktDriver) TaskConfigSchema() (*hclspec.Spec, error) {\n+\treturn taskConfigSpec, nil\n+}\n+\n+func (d *RktDriver) Capabilities() (*drivers.Capabilities, error) {\n+\treturn capabilities, nil\n+}\n+\n+func (r *RktDriver) Fingerprint(ctx context.Context) (<-chan *drivers.Fingerprint, error) {\n+\tch := make(chan *drivers.Fingerprint)\n+\tgo r.handleFingerprint(ctx, ch)\n+\treturn ch, nil\n+}\n+\n+func (d *RktDriver) handleFingerprint(ctx context.Context, ch chan *drivers.Fingerprint) {\n+\tdefer close(ch)\n+\tticker := time.NewTimer(0)\n+\tfor {\n+\t\tselect {\n+\t\tcase <-ctx.Done():\n+\t\t\treturn\n+\t\tcase <-d.ctx.Done():\n+\t\t\treturn\n+\t\tcase <-ticker.C:\n+\t\t\tticker.Reset(fingerprintPeriod)\n+\t\t\tch <- d.buildFingerprint()\n+\t\t}\n+\t}\n+}\n+\n+func (d *RktDriver) buildFingerprint() *drivers.Fingerprint {\n+\tfingerprint := &drivers.Fingerprint{\n+\t\tAttributes:        map[string]string{},\n+\t\tHealth:            drivers.HealthStateHealthy,\n+\t\tHealthDescription: \"healthy\",\n+\t}\n+\n+\t// Only enable if we are root when running on non-windows systems.\n+\tif runtime.GOOS != \"windows\" && syscall.Geteuid() != 0 {\n+\t\td.logger.Debug(\"must run as root user, disabling\")\n+\t\tfingerprint.Health = drivers.HealthStateUndetected\n+\t\tfingerprint.HealthDescription = \"driver must run as root user\"\n+\t\treturn fingerprint\n+\t}\n+\n+\toutBytes, err := exec.Command(rktCmd, \"version\").Output()\n+\tif err != nil {\n+\t\tfingerprint.Health = drivers.HealthStateUndetected\n+\t\tfingerprint.HealthDescription = fmt.Sprintf(\"failed to executor %s version: %v\", rktCmd, err)\n+\t\treturn fingerprint\n+\t}\n+\tout := strings.TrimSpace(string(outBytes))\n+\n+\trktMatches := reRktVersion.FindStringSubmatch(out)\n+\tappcMatches := reAppcVersion.FindStringSubmatch(out)\n+\tif len(rktMatches) != 2 || len(appcMatches) != 2 {\n+\t\tfingerprint.Health = drivers.HealthStateUndetected\n+\t\tfingerprint.HealthDescription = \"unable to parse rkt version string\"\n+\t\treturn fingerprint\n+\t}\n+\n+\tminVersion, _ := version.NewVersion(minRktVersion)\n+\tcurrentVersion, _ := version.NewVersion(rktMatches[1])\n+\tif currentVersion.LessThan(minVersion) {\n+\t\t// Do not allow ancient rkt versions\n+\t\tfingerprint.Health = drivers.HealthStateUndetected\n+\t\tfingerprint.HealthDescription = fmt.Sprintf(\"unsuported rkt version %s\", currentVersion)\n+\t\td.logger.Warn(\"unsupported rkt version please upgrade to >= \"+minVersion.String(),\n+\t\t\t\"rkt_version\", currentVersion)\n+\t\treturn fingerprint\n+\t}\n+\n+\tfingerprint.Attributes[\"driver.rkt\"] = \"1\"\n+\tfingerprint.Attributes[\"driver.rkt.version\"] = rktMatches[1]\n+\tfingerprint.Attributes[\"driver.rkt.appc.version\"] = appcMatches[1]\n+\tif d.config.VolumesEnabled {\n+\t\tfingerprint.Attributes[\"driver.rkt.volumes.enabled\"] = \"1\"\n+\t}\n+\n+\treturn fingerprint\n+\n+}\n+\n+func (d *RktDriver) RecoverTask(handle *drivers.TaskHandle) error {\n+\tif handle == nil {\n+\t\treturn fmt.Errorf(\"error: handle cannot be nil\")\n+\t}\n+\n+\tvar taskState RktTaskState\n+\tif err := handle.GetDriverState(&taskState); err != nil {\n+\t\td.logger.Error(\"failed to decode taskConfig state from handle\", \"error\", err, \"task_id\", handle.Config.ID)\n+\t\treturn fmt.Errorf(\"failed to decode taskConfig state from handle: %v\", err)\n+\t}\n+\n+\tplugRC, err := utils.ReattachConfigToGoPlugin(taskState.ReattachConfig)\n+\tif err != nil {\n+\t\td.logger.Error(\"failed to build ReattachConfig from taskConfig state\", \"error\", err, \"task_id\", handle.Config.ID)\n+\t\treturn fmt.Errorf(\"failed to build ReattachConfig from taskConfig state: %v\", err)\n+\t}\n+\n+\tpluginConfig := &plugin.ClientConfig{\n+\t\tReattach: plugRC,\n+\t}\n+\n+\texecImpl, pluginClient, err := utils.CreateExecutorWithConfig(pluginConfig, os.Stderr)\n+\tif err != nil {\n+\t\td.logger.Error(\"failed to reattach to executor\", \"error\", err, \"task_id\", handle.Config.ID)\n+\t\treturn fmt.Errorf(\"failed to reattach to executor: %v\", err)\n+\t}\n+\n+\t// The taskConfig's environment is set via --set-env flags in Start, but the rkt\n+\t// command itself needs an environment with PATH set to find iptables.\n+\t// TODO (preetha) need to figure out how to read env.blacklist\n+\teb := env.NewEmptyBuilder()\n+\tfilter := strings.Split(config.DefaultEnvBlacklist, \",\")\n+\trktEnv := eb.SetHostEnvvars(filter).Build()\n+\n+\th := &rktTaskHandle{\n+\t\texec:         execImpl,\n+\t\tenv:          rktEnv,\n+\t\tpid:          taskState.Pid,\n+\t\tuuid:         taskState.UUID,\n+\t\tpluginClient: pluginClient,\n+\t\ttaskConfig:   taskState.TaskConfig,\n+\t\tprocState:    drivers.TaskStateRunning,\n+\t\tstartedAt:    taskState.StartedAt,\n+\t\texitResult:   &drivers.ExitResult{},\n+\t}\n+\n+\td.tasks.Set(taskState.TaskConfig.ID, h)\n+\n+\tgo h.run()\n+\treturn nil\n+}\n+\n+func (d *RktDriver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *cstructs.DriverNetwork, error) {\n+\tif _, ok := d.tasks.Get(cfg.ID); ok {\n+\t\treturn nil, nil, fmt.Errorf(\"taskConfig with ID '%s' already started\", cfg.ID)\n+\t}\n+\n+\tvar driverConfig TaskConfig\n+\tif err := cfg.DecodeDriverConfig(&driverConfig); err != nil {\n+\t\treturn nil, nil, fmt.Errorf(\"failed to decode driver config: %v\", err)\n+\t}\n+\n+\thandle := drivers.NewTaskHandle(pluginName)\n+\thandle.Config = cfg\n+\n+\t// todo(preetha) - port map in client v1 is a slice of maps that get merged. figure out if the caller will do this\n+\t//driverConfig.PortMap\n+\n+\t// ACI image\n+\timg := driverConfig.ImageName\n+\n+\t// Global arguments given to both prepare and run-prepared\n+\tglobalArgs := make([]string, 0, 50)\n+\n+\t// Add debug option to rkt command.\n+\tdebug := driverConfig.Debug\n+\n+\t// Add the given trust prefix\n+\ttrustPrefix := driverConfig.TrustPrefix\n+\tinsecure := false\n+\tif trustPrefix != \"\" {\n+\t\tvar outBuf, errBuf bytes.Buffer\n+\t\tcmd := exec.Command(rktCmd, \"trust\", \"--skip-fingerprint-review=true\", fmt.Sprintf(\"--prefix=%s\", trustPrefix), fmt.Sprintf(\"--debug=%t\", debug))\n+\t\tcmd.Stdout = &outBuf\n+\t\tcmd.Stderr = &errBuf\n+\t\tif err := cmd.Run(); err != nil {\n+\t\t\treturn nil, nil, fmt.Errorf(\"Error running rkt trust: %s\\n\\nOutput: %s\\n\\nError: %s\",\n+\t\t\t\terr, outBuf.String(), errBuf.String())\n+\t\t}\n+\t\td.logger.Debug(\"added trust prefix\", \"trust_prefix\", trustPrefix)","path":"drivers/rkt/driver.go","position":378,"original_position":378,"commit_id":"60c40322e66a38ad784d8f1f4d0446bf9e6a5859","original_commit_id":"60c40322e66a38ad784d8f1f4d0446bf9e6a5859","user":{"login":"schmichael","id":113362,"node_id":"MDQ6VXNlcjExMzM2Mg==","avatar_url":"https://avatars1.githubusercontent.com/u/113362?v=4","gravatar_id":"","url":"https://api.github.com/users/schmichael","html_url":"https://github.com/schmichael","followers_url":"https://api.github.com/users/schmichael/followers","following_url":"https://api.github.com/users/schmichael/following{/other_user}","gists_url":"https://api.github.com/users/schmichael/gists{/gist_id}","starred_url":"https://api.github.com/users/schmichael/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/schmichael/subscriptions","organizations_url":"https://api.github.com/users/schmichael/orgs","repos_url":"https://api.github.com/users/schmichael/repos","events_url":"https://api.github.com/users/schmichael/events{/privacy}","received_events_url":"https://api.github.com/users/schmichael/received_events","type":"User","site_admin":false},"body":"use the handle's logger or include `\"task_name\", cfg.Task.Name`","created_at":"2018-10-15T18:38:54Z","updated_at":"2018-10-15T18:38:54Z","html_url":"https://github.com/hashicorp/nomad/pull/4778#discussion_r225274242","pull_request_url":"https://api.github.com/repos/hashicorp/nomad/pulls/4778","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/hashicorp/nomad/pulls/comments/225274242"},"html":{"href":"https://github.com/hashicorp/nomad/pull/4778#discussion_r225274242"},"pull_request":{"href":"https://api.github.com/repos/hashicorp/nomad/pulls/4778"}}},"pull_request":{"url":"https://api.github.com/repos/hashicorp/nomad/pulls/4778","id":222563944,"node_id":"MDExOlB1bGxSZXF1ZXN0MjIyNTYzOTQ0","html_url":"https://github.com/hashicorp/nomad/pull/4778","diff_url":"https://github.com/hashicorp/nomad/pull/4778.diff","patch_url":"https://github.com/hashicorp/nomad/pull/4778.patch","issue_url":"https://api.github.com/repos/hashicorp/nomad/issues/4778","number":4778,"state":"open","locked":false,"title":"rkt driver plugin","user":{"login":"preetapan","id":460133,"node_id":"MDQ6VXNlcjQ2MDEzMw==","avatar_url":"https://avatars1.githubusercontent.com/u/460133?v=4","gravatar_id":"","url":"https://api.github.com/users/preetapan","html_url":"https://github.com/preetapan","followers_url":"https://api.github.com/users/preetapan/followers","following_url":"https://api.github.com/users/preetapan/following{/other_user}","gists_url":"https://api.github.com/users/preetapan/gists{/gist_id}","starred_url":"https://api.github.com/users/preetapan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/preetapan/subscriptions","organizations_url":"https://api.github.com/users/preetapan/orgs","repos_url":"https://api.github.com/users/preetapan/repos","events_url":"https://api.github.com/users/preetapan/events{/privacy}","received_events_url":"https://api.github.com/users/preetapan/received_events","type":"User","site_admin":false},"body":"Implements Rkt driver plugin, and ported over unit tests from client/driver/rkt_test.go","created_at":"2018-10-12T19:27:53Z","updated_at":"2018-10-15T18:38:54Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ff16f965d71fef5126f9694d025a67a22571cdd3","assignee":null,"assignees":[],"requested_reviewers":[{"login":"dadgar","id":1047914,"node_id":"MDQ6VXNlcjEwNDc5MTQ=","avatar_url":"https://avatars0.githubusercontent.com/u/1047914?v=4","gravatar_id":"","url":"https://api.github.com/users/dadgar","html_url":"https://github.com/dadgar","followers_url":"https://api.github.com/users/dadgar/followers","following_url":"https://api.github.com/users/dadgar/following{/other_user}","gists_url":"https://api.github.com/users/dadgar/gists{/gist_id}","starred_url":"https://api.github.com/users/dadgar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dadgar/subscriptions","organizations_url":"https://api.github.com/users/dadgar/orgs","repos_url":"https://api.github.com/users/dadgar/repos","events_url":"https://api.github.com/users/dadgar/events{/privacy}","received_events_url":"https://api.github.com/users/dadgar/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/hashicorp/nomad/pulls/4778/commits","review_comments_url":"https://api.github.com/repos/hashicorp/nomad/pulls/4778/comments","review_comment_url":"https://api.github.com/repos/hashicorp/nomad/pulls/comments{/number}","comments_url":"https://api.github.com/repos/hashicorp/nomad/issues/4778/comments","statuses_url":"https://api.github.com/repos/hashicorp/nomad/statuses/60c40322e66a38ad784d8f1f4d0446bf9e6a5859","head":{"label":"hashicorp:f-driver-plugin-rkt","ref":"f-driver-plugin-rkt","sha":"60c40322e66a38ad784d8f1f4d0446bf9e6a5859","user":{"login":"hashicorp","id":761456,"node_id":"MDEyOk9yZ2FuaXphdGlvbjc2MTQ1Ng==","avatar_url":"https://avatars2.githubusercontent.com/u/761456?v=4","gravatar_id":"","url":"https://api.github.com/users/hashicorp","html_url":"https://github.com/hashicorp","followers_url":"https://api.github.com/users/hashicorp/followers","following_url":"https://api.github.com/users/hashicorp/following{/other_user}","gists_url":"https://api.github.com/users/hashicorp/gists{/gist_id}","starred_url":"https://api.github.com/users/hashicorp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hashicorp/subscriptions","organizations_url":"https://api.github.com/users/hashicorp/orgs","repos_url":"https://api.github.com/users/hashicorp/repos","events_url":"https://api.github.com/users/hashicorp/events{/privacy}","received_events_url":"https://api.github.com/users/hashicorp/received_events","type":"Organization","site_admin":false},"repo":{"id":36653430,"node_id":"MDEwOlJlcG9zaXRvcnkzNjY1MzQzMA==","name":"nomad","full_name":"hashicorp/nomad","private":false,"owner":{"login":"hashicorp","id":761456,"node_id":"MDEyOk9yZ2FuaXphdGlvbjc2MTQ1Ng==","avatar_url":"https://avatars2.githubusercontent.com/u/761456?v=4","gravatar_id":"","url":"https://api.github.com/users/hashicorp","html_url":"https://github.com/hashicorp","followers_url":"https://api.github.com/users/hashicorp/followers","following_url":"https://api.github.com/users/hashicorp/following{/other_user}","gists_url":"https://api.github.com/users/hashicorp/gists{/gist_id}","starred_url":"https://api.github.com/users/hashicorp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hashicorp/subscriptions","organizations_url":"https://api.github.com/users/hashicorp/orgs","repos_url":"https://api.github.com/users/hashicorp/repos","events_url":"https://api.github.com/users/hashicorp/events{/privacy}","received_events_url":"https://api.github.com/users/hashicorp/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/hashicorp/nomad","description":"Nomad is a flexible, enterprise-grade cluster scheduler designed to easily integrate into existing workflows. Nomad can run a diverse workload of micro-service, batch, containerized and non-containerized applications. Nomad is easy to operate and scale and integrates seamlessly with Consul and Vault.","fork":false,"url":"https://api.github.com/repos/hashicorp/nomad","forks_url":"https://api.github.com/repos/hashicorp/nomad/forks","keys_url":"https://api.github.com/repos/hashicorp/nomad/keys{/key_id}","collaborators_url":"https://api.github.com/repos/hashicorp/nomad/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/hashicorp/nomad/teams","hooks_url":"https://api.github.com/repos/hashicorp/nomad/hooks","issue_events_url":"https://api.github.com/repos/hashicorp/nomad/issues/events{/number}","events_url":"https://api.github.com/repos/hashicorp/nomad/events","assignees_url":"https://api.github.com/repos/hashicorp/nomad/assignees{/user}","branches_url":"https://api.github.com/repos/hashicorp/nomad/branches{/branch}","tags_url":"https://api.github.com/repos/hashicorp/nomad/tags","blobs_url":"https://api.github.com/repos/hashicorp/nomad/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/hashicorp/nomad/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/hashicorp/nomad/git/refs{/sha}","trees_url":"https://api.github.com/repos/hashicorp/nomad/git/trees{/sha}","statuses_url":"https://api.github.com/repos/hashicorp/nomad/statuses/{sha}","languages_url":"https://api.github.com/repos/hashicorp/nomad/languages","stargazers_url":"https://api.github.com/repos/hashicorp/nomad/stargazers","contributors_url":"https://api.github.com/repos/hashicorp/nomad/contributors","subscribers_url":"https://api.github.com/repos/hashicorp/nomad/subscribers","subscription_url":"https://api.github.com/repos/hashicorp/nomad/subscription","commits_url":"https://api.github.com/repos/hashicorp/nomad/commits{/sha}","git_commits_url":"https://api.github.com/repos/hashicorp/nomad/git/commits{/sha}","comments_url":"https://api.github.com/repos/hashicorp/nomad/comments{/number}","issue_comment_url":"https://api.github.com/repos/hashicorp/nomad/issues/comments{/number}","contents_url":"https://api.github.com/repos/hashicorp/nomad/contents/{+path}","compare_url":"https://api.github.com/repos/hashicorp/nomad/compare/{base}...{head}","merges_url":"https://api.github.com/repos/hashicorp/nomad/merges","archive_url":"https://api.github.com/repos/hashicorp/nomad/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/hashicorp/nomad/downloads","issues_url":"https://api.github.com/repos/hashicorp/nomad/issues{/number}","pulls_url":"https://api.github.com/repos/hashicorp/nomad/pulls{/number}","milestones_url":"https://api.github.com/repos/hashicorp/nomad/milestones{/number}","notifications_url":"https://api.github.com/repos/hashicorp/nomad/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/hashicorp/nomad/labels{/name}","releases_url":"https://api.github.com/repos/hashicorp/nomad/releases{/id}","deployments_url":"https://api.github.com/repos/hashicorp/nomad/deployments","created_at":"2015-06-01T10:21:00Z","updated_at":"2018-10-15T15:06:27Z","pushed_at":"2018-10-15T18:09:52Z","git_url":"git://github.com/hashicorp/nomad.git","ssh_url":"git@github.com:hashicorp/nomad.git","clone_url":"https://github.com/hashicorp/nomad.git","svn_url":"https://github.com/hashicorp/nomad","homepage":"https://www.nomadproject.io/","size":112299,"stargazers_count":3924,"watchers_count":3924,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":768,"mirror_url":null,"archived":false,"open_issues_count":454,"license":{"key":"mpl-2.0","name":"Mozilla Public License 2.0","spdx_id":"MPL-2.0","url":"https://api.github.com/licenses/mpl-2.0","node_id":"MDc6TGljZW5zZTE0"},"forks":768,"open_issues":454,"watchers":3924,"default_branch":"master"}},"base":{"label":"hashicorp:r-clientv2","ref":"r-clientv2","sha":"a69deebec9fdd75c431694140d3086cb06800524","user":{"login":"hashicorp","id":761456,"node_id":"MDEyOk9yZ2FuaXphdGlvbjc2MTQ1Ng==","avatar_url":"https://avatars2.githubusercontent.com/u/761456?v=4","gravatar_id":"","url":"https://api.github.com/users/hashicorp","html_url":"https://github.com/hashicorp","followers_url":"https://api.github.com/users/hashicorp/followers","following_url":"https://api.github.com/users/hashicorp/following{/other_user}","gists_url":"https://api.github.com/users/hashicorp/gists{/gist_id}","starred_url":"https://api.github.com/users/hashicorp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hashicorp/subscriptions","organizations_url":"https://api.github.com/users/hashicorp/orgs","repos_url":"https://api.github.com/users/hashicorp/repos","events_url":"https://api.github.com/users/hashicorp/events{/privacy}","received_events_url":"https://api.github.com/users/hashicorp/received_events","type":"Organization","site_admin":false},"repo":{"id":36653430,"node_id":"MDEwOlJlcG9zaXRvcnkzNjY1MzQzMA==","name":"nomad","full_name":"hashicorp/nomad","private":false,"owner":{"login":"hashicorp","id":761456,"node_id":"MDEyOk9yZ2FuaXphdGlvbjc2MTQ1Ng==","avatar_url":"https://avatars2.githubusercontent.com/u/761456?v=4","gravatar_id":"","url":"https://api.github.com/users/hashicorp","html_url":"https://github.com/hashicorp","followers_url":"https://api.github.com/users/hashicorp/followers","following_url":"https://api.github.com/users/hashicorp/following{/other_user}","gists_url":"https://api.github.com/users/hashicorp/gists{/gist_id}","starred_url":"https://api.github.com/users/hashicorp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hashicorp/subscriptions","organizations_url":"https://api.github.com/users/hashicorp/orgs","repos_url":"https://api.github.com/users/hashicorp/repos","events_url":"https://api.github.com/users/hashicorp/events{/privacy}","received_events_url":"https://api.github.com/users/hashicorp/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/hashicorp/nomad","description":"Nomad is a flexible, enterprise-grade cluster scheduler designed to easily integrate into existing workflows. Nomad can run a diverse workload of micro-service, batch, containerized and non-containerized applications. Nomad is easy to operate and scale and integrates seamlessly with Consul and Vault.","fork":false,"url":"https://api.github.com/repos/hashicorp/nomad","forks_url":"https://api.github.com/repos/hashicorp/nomad/forks","keys_url":"https://api.github.com/repos/hashicorp/nomad/keys{/key_id}","collaborators_url":"https://api.github.com/repos/hashicorp/nomad/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/hashicorp/nomad/teams","hooks_url":"https://api.github.com/repos/hashicorp/nomad/hooks","issue_events_url":"https://api.github.com/repos/hashicorp/nomad/issues/events{/number}","events_url":"https://api.github.com/repos/hashicorp/nomad/events","assignees_url":"https://api.github.com/repos/hashicorp/nomad/assignees{/user}","branches_url":"https://api.github.com/repos/hashicorp/nomad/branches{/branch}","tags_url":"https://api.github.com/repos/hashicorp/nomad/tags","blobs_url":"https://api.github.com/repos/hashicorp/nomad/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/hashicorp/nomad/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/hashicorp/nomad/git/refs{/sha}","trees_url":"https://api.github.com/repos/hashicorp/nomad/git/trees{/sha}","statuses_url":"https://api.github.com/repos/hashicorp/nomad/statuses/{sha}","languages_url":"https://api.github.com/repos/hashicorp/nomad/languages","stargazers_url":"https://api.github.com/repos/hashicorp/nomad/stargazers","contributors_url":"https://api.github.com/repos/hashicorp/nomad/contributors","subscribers_url":"https://api.github.com/repos/hashicorp/nomad/subscribers","subscription_url":"https://api.github.com/repos/hashicorp/nomad/subscription","commits_url":"https://api.github.com/repos/hashicorp/nomad/commits{/sha}","git_commits_url":"https://api.github.com/repos/hashicorp/nomad/git/commits{/sha}","comments_url":"https://api.github.com/repos/hashicorp/nomad/comments{/number}","issue_comment_url":"https://api.github.com/repos/hashicorp/nomad/issues/comments{/number}","contents_url":"https://api.github.com/repos/hashicorp/nomad/contents/{+path}","compare_url":"https://api.github.com/repos/hashicorp/nomad/compare/{base}...{head}","merges_url":"https://api.github.com/repos/hashicorp/nomad/merges","archive_url":"https://api.github.com/repos/hashicorp/nomad/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/hashicorp/nomad/downloads","issues_url":"https://api.github.com/repos/hashicorp/nomad/issues{/number}","pulls_url":"https://api.github.com/repos/hashicorp/nomad/pulls{/number}","milestones_url":"https://api.github.com/repos/hashicorp/nomad/milestones{/number}","notifications_url":"https://api.github.com/repos/hashicorp/nomad/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/hashicorp/nomad/labels{/name}","releases_url":"https://api.github.com/repos/hashicorp/nomad/releases{/id}","deployments_url":"https://api.github.com/repos/hashicorp/nomad/deployments","created_at":"2015-06-01T10:21:00Z","updated_at":"2018-10-15T15:06:27Z","pushed_at":"2018-10-15T18:09:52Z","git_url":"git://github.com/hashicorp/nomad.git","ssh_url":"git@github.com:hashicorp/nomad.git","clone_url":"https://github.com/hashicorp/nomad.git","svn_url":"https://github.com/hashicorp/nomad","homepage":"https://www.nomadproject.io/","size":112299,"stargazers_count":3924,"watchers_count":3924,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":768,"mirror_url":null,"archived":false,"open_issues_count":454,"license":{"key":"mpl-2.0","name":"Mozilla Public License 2.0","spdx_id":"MPL-2.0","url":"https://api.github.com/licenses/mpl-2.0","node_id":"MDc6TGljZW5zZTE0"},"forks":768,"open_issues":454,"watchers":3924,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/hashicorp/nomad/pulls/4778"},"html":{"href":"https://github.com/hashicorp/nomad/pull/4778"},"issue":{"href":"https://api.github.com/repos/hashicorp/nomad/issues/4778"},"comments":{"href":"https://api.github.com/repos/hashicorp/nomad/issues/4778/comments"},"review_comments":{"href":"https://api.github.com/repos/hashicorp/nomad/pulls/4778/comments"},"review_comment":{"href":"https://api.github.com/repos/hashicorp/nomad/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/hashicorp/nomad/pulls/4778/commits"},"statuses":{"href":"https://api.github.com/repos/hashicorp/nomad/statuses/60c40322e66a38ad784d8f1f4d0446bf9e6a5859"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"schmichael"}}