{"action":"created","comment":{"url":"https://api.github.com/repos/sonm-io/core/pulls/comments/185707557","pull_request_review_id":117149567,"id":185707557,"diff_hunk":"@@ -0,0 +1,345 @@\n+package miner\n+\n+import (\n+\t\"context\"\n+\t\"crypto/ecdsa\"\n+\t\"errors\"\n+\t\"fmt\"\n+\t\"sync\"\n+\t\"time\"\n+\n+\t\"github.com/ethereum/go-ethereum/crypto\"\n+\t\"github.com/noxiouz/zapctx/ctxlog\"\n+\t\"github.com/pborman/uuid\"\n+\t\"github.com/sonm-io/core/blockchain\"\n+\t\"github.com/sonm-io/core/insonmnia/cgroups\"\n+\t\"github.com/sonm-io/core/insonmnia/hardware\"\n+\t\"github.com/sonm-io/core/insonmnia/matcher\"\n+\t\"github.com/sonm-io/core/insonmnia/resource\"\n+\t\"github.com/sonm-io/core/insonmnia/state\"\n+\t\"github.com/sonm-io/core/proto\"\n+\t\"github.com/sonm-io/core/util\"\n+\t\"go.uber.org/zap\"\n+)\n+\n+type Salesman struct {\n+\tstate         *state.Storage\n+\tresources     *resource.Scheduler\n+\thardware      *hardware.Hardware\n+\teth           blockchain.API\n+\tdwh           sonm.DWHClient\n+\tcontrolGroup  cgroups.CGroup\n+\tcGroupManager cgroups.CGroupManager\n+\tethkey        *ecdsa.PrivateKey\n+\n+\taskPlanCGroups map[string]cgroups.CGroup\n+\n+\tmatcher  matcher.Matcher\n+\tlog      *zap.SugaredLogger\n+\tdealChan chan *sonm.Deal\n+\tmu       sync.Mutex\n+}\n+\n+func NewSalesman(\n+\tctx context.Context,\n+\tstate *state.Storage,\n+\tresources *resource.Scheduler,\n+\thardware *hardware.Hardware,\n+\teth blockchain.API,\n+\tmatcher matcher.Matcher,\n+\tethkey *ecdsa.PrivateKey,\n+) (*Salesman, error) {\n+\tif state == nil {\n+\t\treturn nil, errors.New(\"storage is required for salesman\")\n+\t}\n+\tif resources == nil {\n+\t\treturn nil, errors.New(\"resource scheduler is required for salesman\")\n+\t}\n+\tif hardware == nil {\n+\t\treturn nil, errors.New(\"hardware is required for salesman\")\n+\t}\n+\tif eth == nil {\n+\t\treturn nil, errors.New(\"blockchain API is required for salesman\")\n+\t}\n+\tif ethkey == nil {\n+\t\treturn nil, errors.New(\"ethereum private key is required for salesman\")\n+\t}\n+\tif matcher == nil {\n+\t\treturn nil, errors.New(\"matcher is required for salesman\")\n+\t}\n+\n+\ts := &Salesman{\n+\t\tstate:     state,\n+\t\tresources: resources,\n+\t\thardware:  hardware,\n+\t\teth:       eth,\n+\t\tmatcher:   matcher,\n+\t\tethkey:    ethkey,\n+\t\tlog:       ctxlog.S(ctx).With(\"source\", \"salesman\"),\n+\t\tdealChan:  make(chan *sonm.Deal, 1),\n+\t}\n+\n+\tif err := s.restoreState(); err != nil {\n+\t\treturn nil, err\n+\t}\n+\treturn s, nil\n+}\n+\n+func (m *Salesman) Run(ctx context.Context) chan<- *sonm.Deal {\n+\tgo m.syncRoutine(ctx)\n+\treturn m.dealChan\n+}\n+\n+func (m *Salesman) AskPlan(planID string) (*sonm.AskPlan, error) {\n+\treturn m.state.AskPlan(planID)\n+}\n+\n+func (m *Salesman) AskPlans() map[string]*sonm.AskPlan {\n+\treturn m.state.AskPlans()\n+}\n+\n+func (m *Salesman) CreateAskPlan(askPlan *sonm.AskPlan) (string, error) {\n+\tid := uuid.New()\n+\taskPlan.ID = id\n+\tif err := askPlan.GetResources().GetGPU().Normalize(m.hardware); err != nil {\n+\t\treturn \"\", err\n+\t}\n+\n+\tm.mu.Lock()\n+\tdefer m.mu.Unlock()\n+\n+\tif err := m.createCGroup(askPlan); err != nil {\n+\t\treturn \"\", err\n+\t}\n+\n+\tif err := m.resources.Consume(askPlan); err != nil {\n+\t\tm.dropCGroup(askPlan.ID)\n+\t\treturn \"\", err\n+\t}\n+\n+\tif err := m.state.SaveAskPlan(askPlan); err != nil {\n+\t\tm.dropCGroup(askPlan.ID)\n+\t\tm.resources.Release(askPlan.ID)\n+\t\treturn \"\", err\n+\t}\n+\treturn id, nil\n+}\n+\n+func (m *Salesman) createCGroup(plan *sonm.AskPlan) error {\n+\tcgroupResources := plan.GetResources().ToCgroupResources()\n+\tcgroup, err := m.cGroupManager.Attach(plan.ID, cgroupResources)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\tm.askPlanCGroups[plan.ID] = cgroup\n+\treturn nil\n+}\n+\n+func (m *Salesman) dropCGroup(planID string) error {\n+\tcgroup, ok := m.askPlanCGroups[planID]\n+\tif !ok {\n+\t\treturn fmt.Errorf(\"cgroup for ask plan %s not found, probably no such plan\", planID)\n+\t}\n+\terr := cgroup.Delete()\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"could not drop cgroup %s for ask plan %s - %s\", cgroup.Suffix(), planID, err)\n+\t}\n+\tdelete(m.askPlanCGroups, planID)\n+\treturn nil\n+}\n+\n+func (m *Salesman) RemoveAskPlan(planID string) error {\n+\task, err := m.state.AskPlan(planID)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\tif !ask.GetDealID().IsZero() {\n+\t\treturn fmt.Errorf(\"ask plan %s is bound to deal %s\", ask.ID, ask.DealID.Unwrap().String())\n+\t}\n+\tif !ask.GetOrderID().IsZero() {\n+\t\t// background context is used  here because we need to get reply from blockchain\n+\t\terr = <-m.eth.Market().CancelOrder(context.Background(), m.ethkey, ask.OrderID.Unwrap())\n+\t\tif err != nil {\n+\t\t\treturn fmt.Errorf(\"could not cancel market order - %s\", err)\n+\t\t}\n+\t}\n+\t_, err = m.state.RemoveAskPlan(planID)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\tif err = m.resources.Release(planID); err != nil {\n+\t\treturn err\n+\t}\n+\tm.mu.Lock()\n+\tdefer m.mu.Unlock()\n+\treturn m.dropCGroup(planID)\n+}\n+\n+func (m *Salesman) AskPlanByDeal(dealID string) (*sonm.AskPlan, error) {\n+\tplans := m.state.AskPlans()\n+\tfor _, plan := range plans {\n+\t\tif plan.DealID.String() == dealID {\n+\t\t\treturn plan, nil\n+\t\t}\n+\t}\n+\treturn nil, fmt.Errorf(\"ask plan for deal id %s is not found\", dealID)\n+}\n+\n+func (m *Salesman) syncRoutine(ctx context.Context) {\n+\tm.log.Debugf(\"starting sync routine\")\n+\tticker := util.NewImmediateTicker(time.Second)\n+\tdefer ticker.Stop()\n+\tfor {\n+\t\tselect {\n+\t\tcase <-ticker.C:\n+\t\t\tm.syncWithBlockchain(ctx)\n+\t\tcase <-ctx.Done():\n+\t\t\treturn\n+\t\t}\n+\t}\n+}\n+\n+func (m *Salesman) syncWithBlockchain(ctx context.Context) {\n+\tm.log.Debugf(\"syncing salesman with blockchain\")\n+\tplans := m.state.AskPlans()\n+\tfor _, plan := range plans {\n+\t\torderId := plan.GetOrderID()\n+\t\tdealId := plan.GetDealID()\n+\t\tif !dealId.IsZero() {\n+\t\t\tm.checkDeal(ctx, plan)\n+\t\t} else if !orderId.IsZero() {\n+\t\t\tm.checkOrder(ctx, plan)\n+\t\t} else if dealId.IsZero() && orderId.IsZero() {\n+\t\t\tm.placeOrder(ctx, plan)\n+\t\t}\n+\t}\n+}\n+\n+func (m *Salesman) restoreState() error {\n+\taskPlans := m.state.AskPlans()\n+\tfor _, plan := range askPlans {\n+\t\tm.resources.Consume(plan)\n+\t}\n+\t//TODO: restore tasks\n+\treturn nil\n+}\n+\n+func (m *Salesman) checkDeal(ctx context.Context, plan *sonm.AskPlan) {\n+\tm.log.Debugf(\"checking deal %s for ask plan %s and order %s\",\n+\t\tplan.DealID.Unwrap().String(), plan.ID, plan.GetOrderID().Unwrap().String())\n+\tdeal, err := m.eth.Market().GetDealInfo(ctx, plan.GetDealID().Unwrap())\n+\tif err != nil {\n+\t\tm.log.Warnf(\"could not get deal info for order %s, ask %s - %s\",\n+\t\t\tplan.GetOrderID().Unwrap().String(), plan.ID, err)\n+\t\t// TODO: log, what else can we do?\n+\t\treturn\n+\t}\n+\n+\tif deal.Status == sonm.DealStatus_DEAL_CLOSED {\n+\t\terr = m.resources.Release(plan.ID)\n+\t\tif err != nil {\n+\t\t\tm.log.Warnf(\"could not release resources for deal %s, order %s, ask %s - %s\",\n+\t\t\t\tplan.DealID.Unwrap().String(), plan.OrderID.Unwrap().String(), plan.ID, err)\n+\t\t}\n+\t\tplan.DealID = nil\n+\t\tplan.OrderID = nil\n+\t\tm.state.SaveAskPlan(plan)\n+\t}\n+}\n+\n+func (m *Salesman) checkOrder(ctx context.Context, plan *sonm.AskPlan) {\n+\t//TODO: validate deal that it is ours\n+\tm.log.Infof(\"checking order %s for ask plan %s\", plan.GetOrderID().Unwrap().String(), plan.ID)\n+\torder, err := m.eth.Market().GetOrderInfo(ctx, plan.GetOrderID().Unwrap())\n+\tif err != nil {\n+\t\tm.log.Warnf(\"could not get order info for order %s - %s\", plan.GetOrderID().Unwrap().String(), err)\n+\t\t// TODO: log, what else can we do?\n+\t\treturn\n+\t}\n+\n+\tif !order.DealID.IsZero() {\n+\t\tdeal, err := m.eth.Market().GetDealInfo(ctx, order.DealID.Unwrap())\n+\t\tif err != nil {\n+\t\t\tm.log.Warnf(\"could not get deal info for ID %s from market - %s\", order.DealID.Unwrap().String(), err)\n+\t\t\t// TODO: log, what else can we do?\n+\t\t\treturn\n+\t\t}\n+\t\tm.dealChan <- deal\n+\n+\t\tplan.DealID = order.DealID\n+\n+\t\tif err := m.state.SaveAskPlan(plan); err != nil {\n+\t\t\tm.log.Warnf(\"could not get save ask plan with deal %s - %s\", order.DealID.Unwrap().String(), err)\n+\t\t\t// TODO: log, what else can we do?\n+\t\t\treturn\n+\t\t}\n+\t}\n+}\n+\n+func (m *Salesman) placeOrder(ctx context.Context, plan *sonm.AskPlan) {\n+\tbenchmarks, err := m.hardware.ResourcesToBenchmarks(plan.GetResources())\n+\tif err != nil {\n+\t\tm.log.Warnf(\"could not get benchmarks for ask plan %s - %s\", plan.ID, err)\n+\t\treturn\n+\t}\n+\tnet := m.hardware.Network\n+\torder := &sonm.Order{\n+\t\tOrderType:      sonm.OrderType_ASK,\n+\t\tOrderStatus:    sonm.OrderStatus_ORDER_ACTIVE,\n+\t\tAuthorID:       sonm.NewEthAddress(crypto.PubkeyToAddress(m.ethkey.PublicKey)),\n+\t\tCounterpartyID: plan.GetCounterparty(),\n+\t\tDuration:       uint64(plan.GetDuration().Unwrap().Seconds()),\n+\t\tPrice:          plan.GetPrice().GetPerSecond(),\n+\t\t//TODO:refactor NetFlags in separqate PR\n+\t\tNetflags:      sonm.NetflagsToUint([3]bool{net.GetOverlay(), true, net.Incoming}),","path":"insonmnia/miner/salesman.go","position":295,"original_position":294,"commit_id":"d0ef552f560134b301ead7c5cbac6c10f4107783","original_commit_id":"4c602a455fc3de23ddf022f225a5c1d90d74c895","user":{"login":"antmat","id":691770,"avatar_url":"https://avatars3.githubusercontent.com/u/691770?v=4","gravatar_id":"","url":"https://api.github.com/users/antmat","html_url":"https://github.com/antmat","followers_url":"https://api.github.com/users/antmat/followers","following_url":"https://api.github.com/users/antmat/following{/other_user}","gists_url":"https://api.github.com/users/antmat/gists{/gist_id}","starred_url":"https://api.github.com/users/antmat/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/antmat/subscriptions","organizations_url":"https://api.github.com/users/antmat/orgs","repos_url":"https://api.github.com/users/antmat/repos","events_url":"https://api.github.com/users/antmat/events{/privacy}","received_events_url":"https://api.github.com/users/antmat/received_events","type":"User","site_admin":false},"body":"yep, I will use finally available GetOutbound and GetIncoming() for consistency","created_at":"2018-05-03T06:47:25Z","updated_at":"2018-05-03T06:47:25Z","html_url":"https://github.com/sonm-io/core/pull/666#discussion_r185707557","pull_request_url":"https://api.github.com/repos/sonm-io/core/pulls/666","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/sonm-io/core/pulls/comments/185707557"},"html":{"href":"https://github.com/sonm-io/core/pull/666#discussion_r185707557"},"pull_request":{"href":"https://api.github.com/repos/sonm-io/core/pulls/666"}},"in_reply_to_id":185623768},"pull_request":{"url":"https://api.github.com/repos/sonm-io/core/pulls/666","id":184772544,"html_url":"https://github.com/sonm-io/core/pull/666","diff_url":"https://github.com/sonm-io/core/pull/666.diff","patch_url":"https://github.com/sonm-io/core/pull/666.patch","issue_url":"https://api.github.com/repos/sonm-io/core/issues/666","number":666,"state":"open","locked":false,"title":"feat: adapt market to worker","user":{"login":"antmat","id":691770,"avatar_url":"https://avatars3.githubusercontent.com/u/691770?v=4","gravatar_id":"","url":"https://api.github.com/users/antmat","html_url":"https://github.com/antmat","followers_url":"https://api.github.com/users/antmat/followers","following_url":"https://api.github.com/users/antmat/following{/other_user}","gists_url":"https://api.github.com/users/antmat/gists{/gist_id}","starred_url":"https://api.github.com/users/antmat/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/antmat/subscriptions","organizations_url":"https://api.github.com/users/antmat/orgs","repos_url":"https://api.github.com/users/antmat/repos","events_url":"https://api.github.com/users/antmat/events{/privacy}","received_events_url":"https://api.github.com/users/antmat/received_events","type":"User","site_admin":false},"body":"Ready for early review.\r\nSome cleanup is still required though","created_at":"2018-04-28T08:25:54Z","updated_at":"2018-05-03T06:47:25Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7d7a209985db6a1e630c52b287f607fb4411851b","assignee":null,"assignees":[],"requested_reviewers":[{"login":"oopcode","id":6124982,"avatar_url":"https://avatars0.githubusercontent.com/u/6124982?v=4","gravatar_id":"","url":"https://api.github.com/users/oopcode","html_url":"https://github.com/oopcode","followers_url":"https://api.github.com/users/oopcode/followers","following_url":"https://api.github.com/users/oopcode/following{/other_user}","gists_url":"https://api.github.com/users/oopcode/gists{/gist_id}","starred_url":"https://api.github.com/users/oopcode/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oopcode/subscriptions","organizations_url":"https://api.github.com/users/oopcode/orgs","repos_url":"https://api.github.com/users/oopcode/repos","events_url":"https://api.github.com/users/oopcode/events{/privacy}","received_events_url":"https://api.github.com/users/oopcode/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/sonm-io/core/pulls/666/commits","review_comments_url":"https://api.github.com/repos/sonm-io/core/pulls/666/comments","review_comment_url":"https://api.github.com/repos/sonm-io/core/pulls/comments{/number}","comments_url":"https://api.github.com/repos/sonm-io/core/issues/666/comments","statuses_url":"https://api.github.com/repos/sonm-io/core/statuses/d0ef552f560134b301ead7c5cbac6c10f4107783","head":{"label":"sonm-io:feat/adapt_market666","ref":"feat/adapt_market666","sha":"d0ef552f560134b301ead7c5cbac6c10f4107783","user":{"login":"sonm-io","id":25040195,"avatar_url":"https://avatars1.githubusercontent.com/u/25040195?v=4","gravatar_id":"","url":"https://api.github.com/users/sonm-io","html_url":"https://github.com/sonm-io","followers_url":"https://api.github.com/users/sonm-io/followers","following_url":"https://api.github.com/users/sonm-io/following{/other_user}","gists_url":"https://api.github.com/users/sonm-io/gists{/gist_id}","starred_url":"https://api.github.com/users/sonm-io/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sonm-io/subscriptions","organizations_url":"https://api.github.com/users/sonm-io/orgs","repos_url":"https://api.github.com/users/sonm-io/repos","events_url":"https://api.github.com/users/sonm-io/events{/privacy}","received_events_url":"https://api.github.com/users/sonm-io/received_events","type":"Organization","site_admin":false},"repo":{"id":98422971,"name":"core","full_name":"sonm-io/core","owner":{"login":"sonm-io","id":25040195,"avatar_url":"https://avatars1.githubusercontent.com/u/25040195?v=4","gravatar_id":"","url":"https://api.github.com/users/sonm-io","html_url":"https://github.com/sonm-io","followers_url":"https://api.github.com/users/sonm-io/followers","following_url":"https://api.github.com/users/sonm-io/following{/other_user}","gists_url":"https://api.github.com/users/sonm-io/gists{/gist_id}","starred_url":"https://api.github.com/users/sonm-io/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sonm-io/subscriptions","organizations_url":"https://api.github.com/users/sonm-io/orgs","repos_url":"https://api.github.com/users/sonm-io/repos","events_url":"https://api.github.com/users/sonm-io/events{/privacy}","received_events_url":"https://api.github.com/users/sonm-io/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/sonm-io/core","description":"Decentralized Fog Computing Platform","fork":false,"url":"https://api.github.com/repos/sonm-io/core","forks_url":"https://api.github.com/repos/sonm-io/core/forks","keys_url":"https://api.github.com/repos/sonm-io/core/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sonm-io/core/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sonm-io/core/teams","hooks_url":"https://api.github.com/repos/sonm-io/core/hooks","issue_events_url":"https://api.github.com/repos/sonm-io/core/issues/events{/number}","events_url":"https://api.github.com/repos/sonm-io/core/events","assignees_url":"https://api.github.com/repos/sonm-io/core/assignees{/user}","branches_url":"https://api.github.com/repos/sonm-io/core/branches{/branch}","tags_url":"https://api.github.com/repos/sonm-io/core/tags","blobs_url":"https://api.github.com/repos/sonm-io/core/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sonm-io/core/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sonm-io/core/git/refs{/sha}","trees_url":"https://api.github.com/repos/sonm-io/core/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sonm-io/core/statuses/{sha}","languages_url":"https://api.github.com/repos/sonm-io/core/languages","stargazers_url":"https://api.github.com/repos/sonm-io/core/stargazers","contributors_url":"https://api.github.com/repos/sonm-io/core/contributors","subscribers_url":"https://api.github.com/repos/sonm-io/core/subscribers","subscription_url":"https://api.github.com/repos/sonm-io/core/subscription","commits_url":"https://api.github.com/repos/sonm-io/core/commits{/sha}","git_commits_url":"https://api.github.com/repos/sonm-io/core/git/commits{/sha}","comments_url":"https://api.github.com/repos/sonm-io/core/comments{/number}","issue_comment_url":"https://api.github.com/repos/sonm-io/core/issues/comments{/number}","contents_url":"https://api.github.com/repos/sonm-io/core/contents/{+path}","compare_url":"https://api.github.com/repos/sonm-io/core/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sonm-io/core/merges","archive_url":"https://api.github.com/repos/sonm-io/core/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sonm-io/core/downloads","issues_url":"https://api.github.com/repos/sonm-io/core/issues{/number}","pulls_url":"https://api.github.com/repos/sonm-io/core/pulls{/number}","milestones_url":"https://api.github.com/repos/sonm-io/core/milestones{/number}","notifications_url":"https://api.github.com/repos/sonm-io/core/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sonm-io/core/labels{/name}","releases_url":"https://api.github.com/repos/sonm-io/core/releases{/id}","deployments_url":"https://api.github.com/repos/sonm-io/core/deployments","created_at":"2017-07-26T12:57:50Z","updated_at":"2018-05-02T15:32:40Z","pushed_at":"2018-05-02T20:17:50Z","git_url":"git://github.com/sonm-io/core.git","ssh_url":"git@github.com:sonm-io/core.git","clone_url":"https://github.com/sonm-io/core.git","svn_url":"https://github.com/sonm-io/core","homepage":"https://sonm.com","size":32264,"stargazers_count":253,"watchers_count":253,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":40,"mirror_url":null,"archived":false,"open_issues_count":12,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":40,"open_issues":12,"watchers":253,"default_branch":"master"}},"base":{"label":"sonm-io:master","ref":"master","sha":"b963d23032b2b573c9bd7344acbd40cd5a482912","user":{"login":"sonm-io","id":25040195,"avatar_url":"https://avatars1.githubusercontent.com/u/25040195?v=4","gravatar_id":"","url":"https://api.github.com/users/sonm-io","html_url":"https://github.com/sonm-io","followers_url":"https://api.github.com/users/sonm-io/followers","following_url":"https://api.github.com/users/sonm-io/following{/other_user}","gists_url":"https://api.github.com/users/sonm-io/gists{/gist_id}","starred_url":"https://api.github.com/users/sonm-io/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sonm-io/subscriptions","organizations_url":"https://api.github.com/users/sonm-io/orgs","repos_url":"https://api.github.com/users/sonm-io/repos","events_url":"https://api.github.com/users/sonm-io/events{/privacy}","received_events_url":"https://api.github.com/users/sonm-io/received_events","type":"Organization","site_admin":false},"repo":{"id":98422971,"name":"core","full_name":"sonm-io/core","owner":{"login":"sonm-io","id":25040195,"avatar_url":"https://avatars1.githubusercontent.com/u/25040195?v=4","gravatar_id":"","url":"https://api.github.com/users/sonm-io","html_url":"https://github.com/sonm-io","followers_url":"https://api.github.com/users/sonm-io/followers","following_url":"https://api.github.com/users/sonm-io/following{/other_user}","gists_url":"https://api.github.com/users/sonm-io/gists{/gist_id}","starred_url":"https://api.github.com/users/sonm-io/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sonm-io/subscriptions","organizations_url":"https://api.github.com/users/sonm-io/orgs","repos_url":"https://api.github.com/users/sonm-io/repos","events_url":"https://api.github.com/users/sonm-io/events{/privacy}","received_events_url":"https://api.github.com/users/sonm-io/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/sonm-io/core","description":"Decentralized Fog Computing Platform","fork":false,"url":"https://api.github.com/repos/sonm-io/core","forks_url":"https://api.github.com/repos/sonm-io/core/forks","keys_url":"https://api.github.com/repos/sonm-io/core/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sonm-io/core/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sonm-io/core/teams","hooks_url":"https://api.github.com/repos/sonm-io/core/hooks","issue_events_url":"https://api.github.com/repos/sonm-io/core/issues/events{/number}","events_url":"https://api.github.com/repos/sonm-io/core/events","assignees_url":"https://api.github.com/repos/sonm-io/core/assignees{/user}","branches_url":"https://api.github.com/repos/sonm-io/core/branches{/branch}","tags_url":"https://api.github.com/repos/sonm-io/core/tags","blobs_url":"https://api.github.com/repos/sonm-io/core/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sonm-io/core/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sonm-io/core/git/refs{/sha}","trees_url":"https://api.github.com/repos/sonm-io/core/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sonm-io/core/statuses/{sha}","languages_url":"https://api.github.com/repos/sonm-io/core/languages","stargazers_url":"https://api.github.com/repos/sonm-io/core/stargazers","contributors_url":"https://api.github.com/repos/sonm-io/core/contributors","subscribers_url":"https://api.github.com/repos/sonm-io/core/subscribers","subscription_url":"https://api.github.com/repos/sonm-io/core/subscription","commits_url":"https://api.github.com/repos/sonm-io/core/commits{/sha}","git_commits_url":"https://api.github.com/repos/sonm-io/core/git/commits{/sha}","comments_url":"https://api.github.com/repos/sonm-io/core/comments{/number}","issue_comment_url":"https://api.github.com/repos/sonm-io/core/issues/comments{/number}","contents_url":"https://api.github.com/repos/sonm-io/core/contents/{+path}","compare_url":"https://api.github.com/repos/sonm-io/core/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sonm-io/core/merges","archive_url":"https://api.github.com/repos/sonm-io/core/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sonm-io/core/downloads","issues_url":"https://api.github.com/repos/sonm-io/core/issues{/number}","pulls_url":"https://api.github.com/repos/sonm-io/core/pulls{/number}","milestones_url":"https://api.github.com/repos/sonm-io/core/milestones{/number}","notifications_url":"https://api.github.com/repos/sonm-io/core/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sonm-io/core/labels{/name}","releases_url":"https://api.github.com/repos/sonm-io/core/releases{/id}","deployments_url":"https://api.github.com/repos/sonm-io/core/deployments","created_at":"2017-07-26T12:57:50Z","updated_at":"2018-05-02T15:32:40Z","pushed_at":"2018-05-02T20:17:50Z","git_url":"git://github.com/sonm-io/core.git","ssh_url":"git@github.com:sonm-io/core.git","clone_url":"https://github.com/sonm-io/core.git","svn_url":"https://github.com/sonm-io/core","homepage":"https://sonm.com","size":32264,"stargazers_count":253,"watchers_count":253,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":40,"mirror_url":null,"archived":false,"open_issues_count":12,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":40,"open_issues":12,"watchers":253,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/sonm-io/core/pulls/666"},"html":{"href":"https://github.com/sonm-io/core/pull/666"},"issue":{"href":"https://api.github.com/repos/sonm-io/core/issues/666"},"comments":{"href":"https://api.github.com/repos/sonm-io/core/issues/666/comments"},"review_comments":{"href":"https://api.github.com/repos/sonm-io/core/pulls/666/comments"},"review_comment":{"href":"https://api.github.com/repos/sonm-io/core/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/sonm-io/core/pulls/666/commits"},"statuses":{"href":"https://api.github.com/repos/sonm-io/core/statuses/d0ef552f560134b301ead7c5cbac6c10f4107783"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"antmat"}}