{"action":"created","comment":{"url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/pulls/comments/165845319","pull_request_review_id":93846140,"id":165845319,"diff_hunk":"@@ -0,0 +1,219 @@\n+---\n+title: \"Scheduling in Kubernetes\"\n+date: 2018-02-01T22:08:22Z\n+draft: false\n+---\n+\n+The importance of understanding the implementation of the tools we use in\n+production every day cannot be underestimated.\n+\n+This process informs about the tradeoffs engineers made in the implementations.\n+Knowing a tool's strengths and weaknesses helps better design systems on top\n+of it; it exposes potential failure modes and helps debug critical errors when\n+they occur. It also reveals brilliant ideas, tricks, patterns and conventions used in\n+production systems.\n+\n+In this post, I will go through the implementation of the default scheduler in\n+Kubernetes (k8s).\n+Although, k8s has a large set of features, tending towards a full-blown PaaS,\n+at it’s core, k8s is a cluster scheduler, that is to say, it schedules work\n+(pods) onto computing resources (nodes).\n+\n+## The genericScheduler\n+\n+K8s has different mechanisms to control pod scheduling: node and pod affinity and\n+anti-affinity taints and tolerations and it also has support for custom schedulers. I will look at how all these options factor into the default\n+scheduling process.\n+\n+The core abstraction for the scheduler in k8s is called `genericScheduler`.\n+If you clone [github.com/kubernetes/kubernetes](github.com/kubernetes/kubernetes)\n+master, you can find it in [pkg/scheduler/core/generic_scheduler.go](https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/core/generic_scheduler.go).\n+The main method of this structure is called `Schedule`, which\n+functions as a [template method pattern](https://en.wikipedia.org/wiki/Template_method_pattern),\n+ie. different implementations can modify specific steps, but the order in which\n+these steps are executed remains fixed.\n+\n+Here is the `Schedule` method without logging and instrumentation:\n+\n+```golang\n+// Schedule tries to schedule the given pod to one of node in the node list.\n+// If it succeeds, it will return the name of the node.\n+// If it fails, it will return a Fiterror error with reasons.\n+func (g *genericScheduler) Schedule(pod *v1.Pod, nodeLister algorithm.NodeLister) (string, error) {\n+    if err := podPassesBasicChecks(pod, g.pvcLister); err != nil {\n+        return \"\", err\n+    }\n+\n+    nodes, err := nodeLister.List()\n+    if err != nil {\n+        return \"\", err\n+    }\n+    if len(nodes) == 0 {\n+        return \"\", ErrNoNodesAvailable\n+    }\n+\n+    // Used for all fit and priority funcs.\n+    err = g.cache.UpdateNodeNameToInfoMap(g.cachedNodeInfoMap)\n+    if err != nil {\n+        return \"\", err\n+    }\n+\n+    filteredNodes, failedPredicateMap, err := findNodesThatFit(pod, g.cachedNodeInfoMap, nodes, g.predicates, g.extenders, g.predicateMetaProducer, g.equivalenceCache, g.schedulingQueue)\n+    if err != nil {\n+        return \"\", err\n+    }\n+\n+    if len(filteredNodes) == 0 {\n+        return \"\", &FitError{\n+            Pod:              pod,\n+            NumAllNodes:      len(nodes),\n+            FailedPredicates: failedPredicateMap,\n+        }\n+    }\n+\n+    // When only one node after predicate, just use it.\n+    if len(filteredNodes) == 1 {\n+        return filteredNodes[0].Name, nil\n+    }\n+\n+    metaPrioritiesInterface := g.priorityMetaProducer(pod, g.cachedNodeInfoMap)\n+    priorityList, err := PrioritizeNodes(pod, g.cachedNodeInfoMap, metaPrioritiesInterface, g.prioritizers, filteredNodes, g.extenders)\n+    if err != nil {\n+        return \"\", err\n+    }\n+\n+    trace.Step(\"Selecting host\")\n+    return g.selectHost(priorityList)\n+}\n+```\n+\n+K8s schedules pods on a first come first served order. It formes a queue of all\n+the requests to schedule pods and processes them one by one. At this point, there\n+is no concurrency which makes the code simpler. I’ll show later how k8s makes\n+the process fast.\n+\n+## Volumes\n+\n+The first thing that happens is a call to `podPassesBasicChecks()`. Despite the\n+complicated sounding name, at the time of writing, this method makes sure that\n+all the PersistentVolumeClaims (PVCs) defined by the pod are ready to be used,\n+that is they have each been bound to a PersistentVolume (PV) and they are not\n+being deleted.\n+\n+In k8s, nodes represent compute resources and pods consume those compute resources.\n+PVs are similar to nodes, but they represent disk resources available to the cluster.\n+Conceptually they map to physical disks, same way nodes maps to machine, of course,\n+in reality, there are layers of virtualization in between.\n+PVs are defined by the administrators of the cluster, while PVCs are\n+defined by the users of the cluster, the service developers.\n+Each PVC needs to bind to a PV in order for the containers to use the storage.\n+\n+## Algorithm\n+\n+Next up, the algorithm makes sure the list of available nodes is not empty!\n+\n+I mentioned before that although pod scheduling requests are processed one by one,\n+k8s makes great effort to make this efficient, including caching and concurrent\n+execution. One of the pieces of data to be cached is the nodes' specs. It is reasonable\n+to assume that node information will not change often between successive runs of Schedule,\n+so it is cached and updated in `UpdateNodeNameToInfo()`.\n+\n+K8s picks the most appropriate node out of the list of candidates in two steps: predicates and filters.\n+\n+### Predicates\n+\n+Predicates are pure functions which take a node and a pod and return a boolean\n+whether that pod fits onto the node or not. Predicates are meant to be fast and\n+should eliminate all the nodes that can’t fit the pod. Predicates are of type\n+[algorithm.FitPredicate](https://godoc.org/k8s.io/kubernetes/pkg/scheduler/algorithm#FitPredicate)\n+and model concepts like available resources, taints and tolerations, volume\n+availability, memory and disk pressure on the node, etc.\n+\n+This is done in the call to `findNodesThatFit()`. This method executes all\n+predicates on all the available nodes. Because this step is CPU intensive,\n+but also, because the predicates are independent of each other, it runs\n+concurrently on 16 goroutines using the [workqueue.Parallelize](https://godoc.org/k8s.io/client-go/util/workqueue#Parallelize)\n+framework. This is the order in which they are executed by default ([source](https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/algorithm/predicates/predicates.go#L102-L115)]):\n+\n+``` golang\n+[]string{\n+  CheckNodeConditionPred,\n+  GeneralPred,\n+  HostNamePred,\n+  PodFitsHostPortsPred,\n+  MatchNodeSelectorPred,\n+  PodFitsResourcesPred,\n+  NoDiskConflictPred,\n+  PodToleratesNodeTaintsPred,\n+  PodToleratesNodeNoExecuteTaintsPred,\n+  CheckNodeLabelPresencePred,\n+  checkServiceAffinityPred,\n+  MaxEBSVolumeCountPred,\n+  MaxGCEPDVolumeCountPred,\n+  MaxAzureDiskVolumeCountPred,\n+  CheckVolumeBindingPred,\n+  NoVolumeZoneConflictPred,\n+  CheckNodeMemoryPressurePred,\n+  CheckNodeDiskPressurePred,\n+  MatchInterPodAffinityPred,\n+}\n+```\n+\n+One question that comes to mind: Why is the PVCs check also done at the beginning\n+if it’s done here as a predicate? It might be for efficiency reasons.\n+\n+### Priorities\n+\n+Priorities, much like predicates, take a node and a pod but instead of a binary\n+value, return a _“score”_, an integer between 1 and 10. This step is called\n+filtering and is where the algorithm ranks all the nodes to find the best one\n+suited for the pod.\n+This is done in the `PrioritizeNodes()` method and is also runs concurrently on\n+16 goroutines and uses cached data. The scores are also weighted by the importance\n+of each priority function.\n+\n+Finally, all the scores for a node are added to a final score per node. Then, nodes are\n+sorted by this final score in a priority queue (heap) which is returned by\n+`PrioritizeNodes()`.\n+\n+The user of concurrency to compute the predicates and the final scores makes sense,","path":"content/post/scheduling-in-kubernetes.md","position":179,"original_position":179,"commit_id":"767c14d5c1cf5c671d221d14d8e287fdd336997b","original_commit_id":"767c14d5c1cf5c671d221d14d8e287fdd336997b","user":{"login":"simonasorlescu","id":2654917,"avatar_url":"https://avatars2.githubusercontent.com/u/2654917?v=4","gravatar_id":"","url":"https://api.github.com/users/simonasorlescu","html_url":"https://github.com/simonasorlescu","followers_url":"https://api.github.com/users/simonasorlescu/followers","following_url":"https://api.github.com/users/simonasorlescu/following{/other_user}","gists_url":"https://api.github.com/users/simonasorlescu/gists{/gist_id}","starred_url":"https://api.github.com/users/simonasorlescu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/simonasorlescu/subscriptions","organizations_url":"https://api.github.com/users/simonasorlescu/orgs","repos_url":"https://api.github.com/users/simonasorlescu/repos","events_url":"https://api.github.com/users/simonasorlescu/events{/privacy}","received_events_url":"https://api.github.com/users/simonasorlescu/received_events","type":"User","site_admin":false},"body":"concurrency to compute - is this correct?","created_at":"2018-02-04T14:00:18Z","updated_at":"2018-02-04T14:00:18Z","html_url":"https://github.com/topliceanu/alexandrutopliceanu.ro/pull/5#discussion_r165845319","pull_request_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/pulls/5","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/pulls/comments/165845319"},"html":{"href":"https://github.com/topliceanu/alexandrutopliceanu.ro/pull/5#discussion_r165845319"},"pull_request":{"href":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/pulls/5"}}},"pull_request":{"url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/pulls/5","id":166973114,"html_url":"https://github.com/topliceanu/alexandrutopliceanu.ro/pull/5","diff_url":"https://github.com/topliceanu/alexandrutopliceanu.ro/pull/5.diff","patch_url":"https://github.com/topliceanu/alexandrutopliceanu.ro/pull/5.patch","issue_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/issues/5","number":5,"state":"open","locked":false,"title":"add the draft of a new blog post","user":{"login":"topliceanu","id":462588,"avatar_url":"https://avatars1.githubusercontent.com/u/462588?v=4","gravatar_id":"","url":"https://api.github.com/users/topliceanu","html_url":"https://github.com/topliceanu","followers_url":"https://api.github.com/users/topliceanu/followers","following_url":"https://api.github.com/users/topliceanu/following{/other_user}","gists_url":"https://api.github.com/users/topliceanu/gists{/gist_id}","starred_url":"https://api.github.com/users/topliceanu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/topliceanu/subscriptions","organizations_url":"https://api.github.com/users/topliceanu/orgs","repos_url":"https://api.github.com/users/topliceanu/repos","events_url":"https://api.github.com/users/topliceanu/events{/privacy}","received_events_url":"https://api.github.com/users/topliceanu/received_events","type":"User","site_admin":false},"body":"","created_at":"2018-02-03T18:06:20Z","updated_at":"2018-02-04T14:00:18Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d428952a8ad98201e4e076fa02391ba07edcb820","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"milestone":null,"commits_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/pulls/5/commits","review_comments_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/pulls/5/comments","review_comment_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/pulls/comments{/number}","comments_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/issues/5/comments","statuses_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/statuses/767c14d5c1cf5c671d221d14d8e287fdd336997b","head":{"label":"topliceanu:scheduling-in-kubernetes","ref":"scheduling-in-kubernetes","sha":"767c14d5c1cf5c671d221d14d8e287fdd336997b","user":{"login":"topliceanu","id":462588,"avatar_url":"https://avatars1.githubusercontent.com/u/462588?v=4","gravatar_id":"","url":"https://api.github.com/users/topliceanu","html_url":"https://github.com/topliceanu","followers_url":"https://api.github.com/users/topliceanu/followers","following_url":"https://api.github.com/users/topliceanu/following{/other_user}","gists_url":"https://api.github.com/users/topliceanu/gists{/gist_id}","starred_url":"https://api.github.com/users/topliceanu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/topliceanu/subscriptions","organizations_url":"https://api.github.com/users/topliceanu/orgs","repos_url":"https://api.github.com/users/topliceanu/repos","events_url":"https://api.github.com/users/topliceanu/events{/privacy}","received_events_url":"https://api.github.com/users/topliceanu/received_events","type":"User","site_admin":false},"repo":{"id":1664406,"name":"alexandrutopliceanu.ro","full_name":"topliceanu/alexandrutopliceanu.ro","owner":{"login":"topliceanu","id":462588,"avatar_url":"https://avatars1.githubusercontent.com/u/462588?v=4","gravatar_id":"","url":"https://api.github.com/users/topliceanu","html_url":"https://github.com/topliceanu","followers_url":"https://api.github.com/users/topliceanu/followers","following_url":"https://api.github.com/users/topliceanu/following{/other_user}","gists_url":"https://api.github.com/users/topliceanu/gists{/gist_id}","starred_url":"https://api.github.com/users/topliceanu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/topliceanu/subscriptions","organizations_url":"https://api.github.com/users/topliceanu/orgs","repos_url":"https://api.github.com/users/topliceanu/repos","events_url":"https://api.github.com/users/topliceanu/events{/privacy}","received_events_url":"https://api.github.com/users/topliceanu/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/topliceanu/alexandrutopliceanu.ro","description":"blog on software development","fork":false,"url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro","forks_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/forks","keys_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/keys{/key_id}","collaborators_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/teams","hooks_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/hooks","issue_events_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/issues/events{/number}","events_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/events","assignees_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/assignees{/user}","branches_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/branches{/branch}","tags_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/tags","blobs_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/git/refs{/sha}","trees_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/git/trees{/sha}","statuses_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/statuses/{sha}","languages_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/languages","stargazers_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/stargazers","contributors_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/contributors","subscribers_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/subscribers","subscription_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/subscription","commits_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/commits{/sha}","git_commits_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/git/commits{/sha}","comments_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/comments{/number}","issue_comment_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/issues/comments{/number}","contents_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/contents/{+path}","compare_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/compare/{base}...{head}","merges_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/merges","archive_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/downloads","issues_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/issues{/number}","pulls_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/pulls{/number}","milestones_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/milestones{/number}","notifications_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/labels{/name}","releases_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/releases{/id}","deployments_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/deployments","created_at":"2011-04-26T11:23:27Z","updated_at":"2017-01-22T08:40:03Z","pushed_at":"2018-02-03T18:56:56Z","git_url":"git://github.com/topliceanu/alexandrutopliceanu.ro.git","ssh_url":"git@github.com:topliceanu/alexandrutopliceanu.ro.git","clone_url":"https://github.com/topliceanu/alexandrutopliceanu.ro.git","svn_url":"https://github.com/topliceanu/alexandrutopliceanu.ro","homepage":"http://alexandrutopliceanu.ro","size":1565,"stargazers_count":2,"watchers_count":2,"language":"Shell","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1,"mirror_url":null,"archived":false,"open_issues_count":1,"license":null,"forks":1,"open_issues":1,"watchers":2,"default_branch":"master"}},"base":{"label":"topliceanu:master","ref":"master","sha":"1f65d4a328c1d012a24e7a45ec5597a2f208ab40","user":{"login":"topliceanu","id":462588,"avatar_url":"https://avatars1.githubusercontent.com/u/462588?v=4","gravatar_id":"","url":"https://api.github.com/users/topliceanu","html_url":"https://github.com/topliceanu","followers_url":"https://api.github.com/users/topliceanu/followers","following_url":"https://api.github.com/users/topliceanu/following{/other_user}","gists_url":"https://api.github.com/users/topliceanu/gists{/gist_id}","starred_url":"https://api.github.com/users/topliceanu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/topliceanu/subscriptions","organizations_url":"https://api.github.com/users/topliceanu/orgs","repos_url":"https://api.github.com/users/topliceanu/repos","events_url":"https://api.github.com/users/topliceanu/events{/privacy}","received_events_url":"https://api.github.com/users/topliceanu/received_events","type":"User","site_admin":false},"repo":{"id":1664406,"name":"alexandrutopliceanu.ro","full_name":"topliceanu/alexandrutopliceanu.ro","owner":{"login":"topliceanu","id":462588,"avatar_url":"https://avatars1.githubusercontent.com/u/462588?v=4","gravatar_id":"","url":"https://api.github.com/users/topliceanu","html_url":"https://github.com/topliceanu","followers_url":"https://api.github.com/users/topliceanu/followers","following_url":"https://api.github.com/users/topliceanu/following{/other_user}","gists_url":"https://api.github.com/users/topliceanu/gists{/gist_id}","starred_url":"https://api.github.com/users/topliceanu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/topliceanu/subscriptions","organizations_url":"https://api.github.com/users/topliceanu/orgs","repos_url":"https://api.github.com/users/topliceanu/repos","events_url":"https://api.github.com/users/topliceanu/events{/privacy}","received_events_url":"https://api.github.com/users/topliceanu/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/topliceanu/alexandrutopliceanu.ro","description":"blog on software development","fork":false,"url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro","forks_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/forks","keys_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/keys{/key_id}","collaborators_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/teams","hooks_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/hooks","issue_events_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/issues/events{/number}","events_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/events","assignees_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/assignees{/user}","branches_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/branches{/branch}","tags_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/tags","blobs_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/git/refs{/sha}","trees_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/git/trees{/sha}","statuses_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/statuses/{sha}","languages_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/languages","stargazers_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/stargazers","contributors_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/contributors","subscribers_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/subscribers","subscription_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/subscription","commits_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/commits{/sha}","git_commits_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/git/commits{/sha}","comments_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/comments{/number}","issue_comment_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/issues/comments{/number}","contents_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/contents/{+path}","compare_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/compare/{base}...{head}","merges_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/merges","archive_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/downloads","issues_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/issues{/number}","pulls_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/pulls{/number}","milestones_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/milestones{/number}","notifications_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/labels{/name}","releases_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/releases{/id}","deployments_url":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/deployments","created_at":"2011-04-26T11:23:27Z","updated_at":"2017-01-22T08:40:03Z","pushed_at":"2018-02-03T18:56:56Z","git_url":"git://github.com/topliceanu/alexandrutopliceanu.ro.git","ssh_url":"git@github.com:topliceanu/alexandrutopliceanu.ro.git","clone_url":"https://github.com/topliceanu/alexandrutopliceanu.ro.git","svn_url":"https://github.com/topliceanu/alexandrutopliceanu.ro","homepage":"http://alexandrutopliceanu.ro","size":1565,"stargazers_count":2,"watchers_count":2,"language":"Shell","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1,"mirror_url":null,"archived":false,"open_issues_count":1,"license":null,"forks":1,"open_issues":1,"watchers":2,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/pulls/5"},"html":{"href":"https://github.com/topliceanu/alexandrutopliceanu.ro/pull/5"},"issue":{"href":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/issues/5"},"comments":{"href":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/issues/5/comments"},"review_comments":{"href":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/pulls/5/comments"},"review_comment":{"href":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/pulls/5/commits"},"statuses":{"href":"https://api.github.com/repos/topliceanu/alexandrutopliceanu.ro/statuses/767c14d5c1cf5c671d221d14d8e287fdd336997b"}},"author_association":"OWNER"}}
{"actor":{"display_login":"simonasorlescu"}}