text:
layout: post
title: Instalasi Dan Setup Kubernetes Cluster
modified:
categories:
description: Instalasi Dan Setup Kubernetes Cluster
tags: [kubernetes, cluster, docker, kubernetes dashboard]
image:
  background: abstract-2.png
comments: true
share: true
date: 2018-05-01T20:15:28+07:00
---

Pada artikel sebelumnya [Belajar Membuat Cluster Apache Cassandra Dengan Docker Swarm](https://rizkimufrizal.github.io/belajar-membuat-cluster-apache-cassandra-dengan-docker-swarm/), penulis telah membahas mengenai tentang cluster pada apache cassandra dengan menggunakan docker swarm. Untuk dapat membuat cluster pada apache cassandra, kita juga dapat menggunakan kubernetes sebagai penganti dari docker swarm. Jika docker swarm merupakan bawaan dari docker maka kubernetes perlu dilakukan instalasi dan setup. Pada artikel ini, penulis akan membahas bagaimana cara instalasi, setup kubernetes dan membuat cluster apache cassandra pada kubernetes.

## Apa Itu Kubernetes ?

>>Kubernetes adalah salah satu produk open source untuk sistem manajemen container

Sama seperti docker swarm, kubernetes dapat melakukan manajemen container, akan tetapi kubernetes merupakan produk yang akan terus berkembang dan akan lebih banyak digunakan dibandingkan dengan docker swarm. Salah satu kelebihan dari kubernetes adalah kita dapat menggunakan kubernetes dashboard untuk memonitoring container, kita dapat melakukan deployment melalui dashboard dan pastinya struktur yang diusulkan oleh kubernetes dan docker swarm sangatlah berbeda. Berikut adalah struktur yang biasanya digunakan di dalam kubernetes.

![Arsitektur Kubernetes.png](../images/Arsitektur Kubernetes.png)

Berikut adalah penjelasan nya

* Node : biasanya sebagai server, bisa sebagai server fisik atau virtual server sehingga node ini juga dapat berupa sebuah server yang berjalan diatas virtual machine seperti VMWare atau virtualbox.
* Deployment : biasanya berfungsi untuk mengontrol dari sebuah pods, misalnya adanya update dari sebuah pods, adanya penambahan replikasi pada pods dan lain sebagainya.
* Service : berfungsi untuk mengexpose pods, tujuan nya adalah agar client dapat mengakses service yang terdapat di dalam sebuah pods.
* Pods : di dalam sebuah deployment terdapat beberapa pods, biasanya pods ini terdapat 1 container atau lebih. Pods ini biasanya akan dilakukan replikasi sesuai dengan kebutuhan.
* Container : container ini mewakili dari 1 aplikasi, misalnya database postgresql atau web server apache tomcat.

## Instalasi Kubernetes

Pada artikel ini, kita akan menggunakan 2 buat vm yaitu :

1. VM Master : berfungsi sebagai node master, master disini berfungsi untuk mendeploy container ke dalam node worker kubernetes.
2. VM Worker : berfungsi sebagai node worker.

### Requirment Node Master

Adapun kebutuhan untuk node master adalah :

* Ram 3 GB
* Disk 10 GB
* Bridge Network
* IP 192.168.88.100
* Ubuntu 16.04 Server

### Requirment Node Worker

Adapun kebutuhan untuk node worker adalah :

* Ram 2 GB
* Disk 10 GB
* Bridge Network
* IP 192.168.88.101
* Ubuntu 16.04 Server

Dengan requirment diatas, silahkan lakukan instalasi ubuntu server pada 2 vm tersebut yaitu pada vm master dan vm worker. IP yang digunakan disini adalah IP static, sehingga anda perlu melakukan sedikit konfigurasi pada jaringan ubuntu server.

Untuk melakukan instalasi kubernetes, anda wajib melakukan instalasi docker terlebih dahulu, bagi yang belum mengerti docker, silahkan baca artikel [Belajar Docker](https://rizkimufrizal.github.io/belajar-docker/). Hal yang pertama dilakukan adalah lakukan update dan upgrade ubuntu server dengan perintah.

{% highlight bash %}
sudo -s
apt update && apt upgrade -y
{% endhighlight %}

Lalu update certificate dan kebutuhan untuk kubernetes dengan perintah

{% highlight bash %}
apt install apt-transport-https ca-certificates curl software-properties-common -y
{% endhighlight %}

Lalu tambahkan key untuk kubernetes dengan perintah

{% highlight bash %}
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
{% endhighlight %}

Lalu tambahkan repository kubernetes untuk ubuntu dengan perintah

{% highlight bash %}
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF
{% endhighlight %}

Lalu lakukan update kembali dengan perintah

{% highlight bash %}
apt update
{% endhighlight %}

Dan lakukan instalasi dengan perintah

{% highlight bash %}
apt install kubelet kubeadm kubernetes-cni -y
{% endhighlight %}

## Setup Kubernetes

Setelah selesai melakukan instalasi kubernetes, tahapan selanjutnya adalah kita akan melakukan setup kubernetes. Setup kubernetes akan dilakukan secara bertahap, yaitu dilakukan pada node master terlebih dahulu kemudian akan dilakukan pada node worker.

### Setup Kubernetes Master

Silahkan login ke node master, lalu aktifkan bridged IPv4 traffic dengan perintah

{% highlight bash %}
sysctl net.bridge.bridge-nf-call-iptables=1
{% endhighlight %}

Lalu jalankan perintah berikut untuk disable swap pada ubuntu

{% highlight bash %}
swapoff -a
{% endhighlight %}

Setelah selesai, jalankan perintah berikut untuk inisialisasi kubernetes pada node master.

{% highlight bash %}
kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.88.100 --kubernetes-version stable-1.9
{% endhighlight %}

Jika berhasil maka akan muncul output seperti berikut.

{% highlight bash %}
Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of machines by running the following on each node
as root:

  kubeadm join --token d36cc6.a9d32c295072f336 192.168.88.100:6443 --discovery-token-ca-cert-hash sha256:4a6626451ad1ad54e1c85e93dd26d7bfd66a2d1da4052f04b5bd1f424a4082d4

{% endhighlight %}

Perintah `kubeadm join` nantinya akan kita gunakan untuk melakukan registrasi dari node worker ke node master.

Lalu jalankan perintah berikut agar kubectl dapat diakses tanpa perlu menggunakan user root.

{% highlight bash %}
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
{% endhighlight %}

Setelah selesai, jika kita melakukan setup kubernetes cluster maka dibutuhkan network overlay seperti docker swarm yang dapat menyambung pods yang berbeda node. Jika kita menggunakan kubernetes, maka ada beberapa opsi network pod plugin yang dapat kita gunakan yaitu :

* [Flannel](https://github.com/coreos/flannel)
* [Calico](https://www.projectcalico.org)
* [Canal](https://github.com/projectcalico/canal)
* [Kube Router](https://github.com/cloudnativelabs/kube-router)
* [Romana](http://romana.io/)
* [Weave Net](https://www.weave.works/)

Pada artikel ini, penulis akan menggunakan flannel sebagai network plugin. Untuk melakukan setup flannel pada kubernetes, silahkan jalankan perintah berikut.

{% highlight bash %}
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
{% endhighlight %}

Jika telah selesai, silahkan lakukan pengecekan pods dengan perintah

{% highlight bash %}
kubectl get pods --all-namespaces
{% endhighlight %}

Jika beberapa pods masih dalam proses creating / prosess pull image maka akan muncul seperti berikut.

{% highlight bash %}
NAMESPACE     NAME                             READY     STATUS     RESTARTS   AGE
kube-system   etcd-master                      1/1       Running    0          4s
kube-system   kube-apiserver-master            1/1       Running    3          4s
kube-system   kube-controller-manager-master   1/1       Running    0          4s
kube-system   kube-dns-6f4fd4bdf-47lc6         0/3       Pending    0          7m
kube-system   kube-flannel-ds-j5d75            0/1       Init:0/1   0          19s
kube-system   kube-proxy-gmrfm                 1/1       Running    0          8m
kube-system   kube-scheduler-master            1/1       Running    0          4s
{% endhighlight %}

Jika semua pods telah berjalan maka akan muncul seperti berikut.

{% highlight bash %}
NAMESPACE     NAME                             READY     STATUS    RESTARTS   AGE
kube-system   etcd-master                      1/1       Running   0          7m
kube-system   kube-apiserver-master            1/1       Running   3          7m
kube-system   kube-controller-manager-master   1/1       Running   0          7m
kube-system   kube-dns-6f4fd4bdf-47lc6         3/3       Running   0          16m
kube-system   kube-flannel-ds-j5d75            1/1       Running   0          9m
kube-system   kube-proxy-gmrfm                 1/1       Running   0          16m
kube-system   kube-scheduler-master            1/1       Running   0          7m
{% endhighlight %}

### Setup Kubernetes Worker

Silahkan login ke node worker lalu aktifkan bridged IPv4 traffic dengan perintah

{% highlight bash %}
sysctl net.bridge.bridge-nf-call-iptables=1
{% endhighlight %}

Lalu jalankan perintah berikut untuk disable swap pada ubuntu

{% highlight bash %}
swapoff -a
{% endhighlight %}

Setelah selesai, jalankan perintah berikut untuk inisialisasi kubernetes pada node worker dengan melakukan register ke node master.

{% highlight bash %}
kubeadm join --token d36cc6.a9d32c295072f336 192.168.88.100:6443 --discovery-token-ca-cert-hash sha256:4a6626451ad1ad54e1c85e93dd26d7bfd66a2d1da4052f04b5bd1f424a4082d4
{% endhighlight %}

Setelah selesai, silahkan login kembali ke node master, lalu jalankan perintah berikut untuk melihat node yang berhasil melakukan register.

{% highlight bash %}
kubectl get nodes
{% endhighlight %}

Jika berhasil maka akan muncul seperti berikut.

{% highlight bash %}
NAME      STATUS     ROLES     AGE       VERSION
master    Ready      master    18m       v1.9.6
worker    NotReady   <none>    2s        v1.9.6
{% endhighlight %}

Dan berikut jika kedua node telah siap untuk digunakan.

{% highlight bash %}
NAME      STATUS    ROLES     AGE       VERSION
master    Ready     master    23m       v1.9.6
worker    Ready     <none>    5m        v1.9.6
{% endhighlight %}

### Setup Kubernetes Dashboard

Secara default, kubernetes tidak memiliki dashboard akan tetapi kita dapat melakukan setup untuk kubernetes dashboard. Yang pertama kali dilakukan adalah kita akan membuat sebuah file yaitu `admin.yaml` lalu isikan dengan source berikut.

{% highlight yaml %}
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: kubernetes-dashboard
  labels:
    k8s-app: kubernetes-dashboard
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: kubernetes-dashboard
  namespace: kube-system
{% endhighlight %}

Konfigurasi diatas berfungsi untuk user authentication pada kubernetes dashboard. Lalu jalankan perintah berikut untuk menambahkan user tersebut.

{% highlight bash %}
kubectl create -f admin.yaml
{% endhighlight %}

Setelah selesai, silahkan buat sebuah file `dashboard.yaml` lalu isikan source berikut.

{% highlight yaml %}
apiVersion: v1
kind: Secret
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard-certs
  namespace: kube-system
type: Opaque

---
# ------------------- Dashboard Service Account ------------------- #

apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kube-system

---
# ------------------- Dashboard Role & Role Binding ------------------- #

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: kubernetes-dashboard-minimal
  namespace: kube-system
rules:
  # Allow Dashboard to create 'kubernetes-dashboard-key-holder' secret.
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["create"]
  # Allow Dashboard to create 'kubernetes-dashboard-settings' config map.
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["create"]
  # Allow Dashboard to get, update and delete Dashboard exclusive secrets.
- apiGroups: [""]
  resources: ["secrets"]
  resourceNames: ["kubernetes-dashboard-key-holder", "kubernetes-dashboard-certs"]
  verbs: ["get", "update", "delete"]
  # Allow Dashboard to get and update 'kubernetes-dashboard-settings' config map.
- apiGroups: [""]
  resources: ["configmaps"]
  resourceNames: ["kubernetes-dashboard-settings"]
  verbs: ["get", "update"]
  # Allow Dashboard to get metrics from heapster.
- apiGroups: [""]
  resources: ["services"]
  resourceNames: ["heapster"]
  verbs: ["proxy"]
- apiGroups: [""]
  resources: ["services/proxy"]
  resourceNames: ["heapster", "http:heapster:", "https:heapster:"]
  verbs: ["get"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: kubernetes-dashboard-minimal
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: kubernetes-dashboard-minimal
subjects:
- kind: ServiceAccount
  name: kubernetes-dashboard
  namespace: kube-system

---
# ------------------- Dashboard Deployment ------------------- #

kind: Deployment
apiVersion: apps/v1beta2
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kube-system
spec:
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      k8s-app: kubernetes-dashboard
  template:
    metadata:
      labels:
        k8s-app: kubernetes-dashboard
    spec:
      containers:
      - name: kubernetes-dashboard
        image: k8s.gcr.io/kubernetes-dashboard-amd64:v1.8.3
        ports:
        - containerPort: 8443
          protocol: TCP
        args:
          - --auto-generate-certificates
          # Uncomment the following line to manually specify Kubernetes API server Host
          # If not specified, Dashboard will attempt to auto discover the API server and connect
          # to it. Uncomment only if the default does not work.
          # - --apiserver-host=http://my-address:port
        volumeMounts:
        - name: kubernetes-dashboard-certs
          mountPath: /certs
          # Create on-disk volume to store exec logs
        - mountPath: /tmp
          name: tmp-volume
        livenessProbe:
          httpGet:
            scheme: HTTPS
            path: /
            port: 8443
          initialDelaySeconds: 30
          timeoutSeconds: 30
      volumes:
      - name: kubernetes-dashboard-certs
        secret:
          secretName: kubernetes-dashboard-certs
      - name: tmp-volume
        emptyDir: {}
      serviceAccountName: kubernetes-dashboard
      # Comment the following tolerations if Dashboard must not be deployed on master
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule

---
# ------------------- Dashboard Service ------------------- #

kind: Service
apiVersion: v1
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kube-system
spec:
  ports:
    - port: 443
      targetPort: 8443
  selector:
    k8s-app: kubernetes-dashboard
  type: NodePort
{% endhighlight %}

Lalu jalankan dashboard tersebut dengan perintah

{% highlight bash %}
kubectl apply -f dashboard.yaml
{% endhighlight %}

Setelah selesai, tunggu hingga pods tersebut berjalan. Jika pods telah berjalan, silahkan lakukan pengecekan services dengan perintah.

{% highlight bash %}
kubectl get services --all-namespaces
{% endhighlight %}

Dan berikut adalah hasilnya

{% highlight bash %}
NAMESPACE     NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)         AGE
default       kubernetes             ClusterIP   10.96.0.1       <none>        443/TCP         31m
kube-system   kube-dns               ClusterIP   10.96.0.10      <none>        53/UDP,53/TCP   31m
kube-system   kubernetes-dashboard   NodePort    10.101.10.168   <none>        443:31601/TCP   2m
{% endhighlight %}

Dari konfigurasi diatas, dapat dilihat bahwa kubernetes dashboard jalan pada port `31601`, lalu silahkan akses kubernetes dashboard di `https://<ip>:31601` seperti berikut.

![Screenshot from 2018-03-25 18-36-20.png](../images/Screenshot from 2018-03-25 18-36-20.png)

Lalu pilih menu skip dan akan muncul halaman seperti berikut.

![Screenshot from 2018-03-25 18-37-31.png](../images/Screenshot from 2018-03-25 18-37-31.png)

## Arsitektur Cluster Apache Cassandra Pada Kubernetes

Arsitektur yang akan kita gunakan pada kubernetes tidak jauh berbeda dengan docker swarm. Pada kubernetes, kita akan melakukan deployment container pada sebuah deployment. Setiap 1 deployment akan mewakili 1 cassandra. Berikut adalah arsitektur yang akan digunakan.

![cassandra-kubernetes.png](../images/cassandra-kubernetes.png)

Silahkan buat sebuah file yaml `cassandra-service.yaml` lalu isikan dengan source berikut.

{% highlight yaml %}
apiVersion: v1
kind: Service
metadata:
  annotations:
    kompose.cmd: kompose convert
    kompose.version: 1.7.0 (767ab4b)
  creationTimestamp: null
  labels:
    io.kompose.service: cassandra-dc-1
  name: cassandra-dc-1
spec:
  ports:
  - name: "9042"
    port: 9042
    targetPort: 9042
  - name: "7000"
    port: 7000
    targetPort: 7000
  selector:
    io.kompose.service: cassandra-dc-1
status:
  loadBalancer: {}

---
apiVersion: v1
kind: Service
metadata:
  annotations:
    kompose.cmd: kompose convert
    kompose.version: 1.7.0 (767ab4b)
  creationTimestamp: null
  labels:
    io.kompose.service: cassandra-dc-2
  name: cassandra-dc-2
spec:
  ports:
  - name: "9042"
    port: 9042
    targetPort: 9042
  - name: "7000"
    port: 7000
    targetPort: 7000
  selector:
    io.kompose.service: cassandra-dc-2
status:
  loadBalancer: {}

---
apiVersion: v1
kind: Service
metadata:
  annotations:
    kompose.cmd: kompose convert
    kompose.version: 1.7.0 (767ab4b)
  creationTimestamp: null
  labels:
    io.kompose.service: cassandra-dc-3
  name: cassandra-dc-3
spec:
  ports:
  - name: "9042"
    port: 9042
    targetPort: 9042
  - name: "7000"
    port: 7000
    targetPort: 7000
  selector:
    io.kompose.service: cassandra-dc-3
status:
  loadBalancer: {}

---
apiVersion: v1
kind: Service
metadata:
  annotations:
    kompose.cmd: kompose convert
    kompose.version: 1.7.0 (767ab4b)
  creationTimestamp: null
  labels:
    io.kompose.service: cassandra-dr-1
  name: cassandra-dr-1
spec:
  ports:
  - name: "9042"
    port: 9042
    targetPort: 9042
  - name: "7000"
    port: 7000
    targetPort: 7000
  selector:
    io.kompose.service: cassandra-dr-1
status:
  loadBalancer: {}

---
apiVersion: v1
kind: Service
metadata:
  annotations:
    kompose.cmd: kompose convert
    kompose.version: 1.7.0 (767ab4b)
  creationTimestamp: null
  labels:
    io.kompose.service: cassandra-dr-2
  name: cassandra-dr-2
spec:
  ports:
  - name: "9042"
    port: 9042
    targetPort: 9042
  - name: "7000"
    port: 7000
    targetPort: 7000
  selector:
    io.kompose.service: cassandra-dr-2
status:
  loadBalancer: {}

---
apiVersion: v1
kind: Service
metadata:
  annotations:
    kompose.cmd: kompose convert
    kompose.version: 1.7.0 (767ab4b)
  creationTimestamp: null
  labels:
    io.kompose.service: cassandra-dr-3
  name: cassandra-dr-3
spec:
  ports:
  - name: "9042"
    port: 9042
    targetPort: 9042
  - name: "7000"
    port: 7000
    targetPort: 7000
  selector:
    io.kompose.service: cassandra-dr-3
status:
  loadBalancer: {}
{% endhighlight %}

Kemudian kembali ke menu dashboard, pilih menu create lalu isikan source diatas pada form seperti berikut.

![Screenshot from 2018-03-25 18-47-06.png](../images/Screenshot from 2018-03-25 18-47-06.png)

Silahkan buat sebuah file yaml `cassandra-deployment.yaml` lalu isikan dengan source berikut.

{% highlight yaml %}
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    kompose.cmd: kompose convert
    kompose.version: 1.7.0 (767ab4b)
  creationTimestamp: null
  labels:
    io.kompose.service: cassandra-dc-1
  name: cassandra-dc-1
spec:
  replicas: 1
  minReadySeconds: 60
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      creationTimestamp: null
      labels:
        io.kompose.service: cassandra-dc-1
    spec:
      containers:
      - args:
        - bash
        - -c
        - 'if [ -z "$$(ls -A /var/lib/cassandra/)" ] ; then sleep 0; fi && /docker-entrypoint.sh cassandra -f'
        image: cassandra:latest
        name: cassandra-dc-1
        imagePullPolicy: IfNotPresent
        env:
        - name: CASSANDRA_CLUSTER_NAME
          value: CassandraCluster
        - name: CASSANDRA_BROADCAST_ADDRESS
          value: cassandra-dc-1
        - name: CASSANDRA_SEEDS
          value: cassandra-dc-1,cassandra-dr-1
        - name: CASSANDRA_DC
          value: DC
        - name: CASSANDRA_RACK
          value: RACK1
        - name: CASSANDRA_ENDPOINT_SNITCH
          value: GossipingPropertyFileSnitch
        - name: MAX_HEAP_SIZE
          value: 50m
        - name: HEAP_NEWSIZE
          value: 10m
        resources: {}
      restartPolicy: Always
status: {}

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    kompose.cmd: kompose convert
    kompose.version: 1.7.0 (767ab4b)
  creationTimestamp: null
  labels:
    io.kompose.service: cassandra-dc-2
  name: cassandra-dc-2
spec:
  replicas: 1
  minReadySeconds: 60
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      creationTimestamp: null
      labels:
        io.kompose.service: cassandra-dc-2
    spec:
      containers:
      - args:
        - bash
        - -c
        - 'if [ -z "$$(ls -A /var/lib/cassandra/)" ] ; then sleep 120; fi && /docker-entrypoint.sh cassandra -f'
        image: cassandra:latest
        name: cassandra-dc-2
        imagePullPolicy: IfNotPresent
        env:
        - name: CASSANDRA_CLUSTER_NAME
          value: CassandraCluster
        - name: CASSANDRA_BROADCAST_ADDRESS
          value: cassandra-dc-2
        - name: CASSANDRA_SEEDS
          value: cassandra-dc-1,cassandra-dr-1
        - name: CASSANDRA_DC
          value: DC
        - name: CASSANDRA_RACK
          value: RACK2
        - name: CASSANDRA_ENDPOINT_SNITCH
          value: GossipingPropertyFileSnitch
        - name: MAX_HEAP_SIZE
          value: 50m
        - name: HEAP_NEWSIZE
          value: 10m
        resources: {}
      restartPolicy: Always
status: {}

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    kompose.cmd: kompose convert
    kompose.version: 1.7.0 (767ab4b)
  creationTimestamp: null
  labels:
    io.kompose.service: cassandra-dc-3
  name: cassandra-dc-3
spec:
  replicas: 1
  minReadySeconds: 60
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      creationTimestamp: null
      labels:
        io.kompose.service: cassandra-dc-3
    spec:
      containers:
      - args:
        - bash
        - -c
        - 'if [ -z "$$(ls -A /var/lib/cassandra/)" ] ; then sleep 240; fi && /docker-entrypoint.sh cassandra -f'
        image: cassandra:latest
        name: cassandra-dc-3
        imagePullPolicy: IfNotPresent
        env:
        - name: CASSANDRA_CLUSTER_NAME
          value: CassandraCluster
        - name: CASSANDRA_BROADCAST_ADDRESS
          value: cassandra-dc-3
        - name: CASSANDRA_SEEDS
          value: cassandra-dc-1,cassandra-dr-1
        - name: CASSANDRA_DC
          value: DC
        - name: CASSANDRA_RACK
          value: RACK3
        - name: CASSANDRA_ENDPOINT_SNITCH
          value: GossipingPropertyFileSnitch
        - name: MAX_HEAP_SIZE
          value: 50m
        - name: HEAP_NEWSIZE
          value: 10m
        resources: {}
      restartPolicy: Always
status: {}

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    kompose.cmd: kompose convert
    kompose.version: 1.7.0 (767ab4b)
  creationTimestamp: null
  labels:
    io.kompose.service: cassandra-dr-1
  name: cassandra-dr-1
spec:
  replicas: 1
  minReadySeconds: 60
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      creationTimestamp: null
      labels:
        io.kompose.service: cassandra-dr-1
    spec:
      containers:
      - args:
        - bash
        - -c
        - 'if [ -z "$$(ls -A /var/lib/cassandra/)" ] ; then sleep 60; fi && /docker-entrypoint.sh cassandra -f'
        image: cassandra:latest
        name: cassandra-dr-1
        imagePullPolicy: IfNotPresent
        env:
        - name: CASSANDRA_CLUSTER_NAME
          value: CassandraCluster
        - name: CASSANDRA_BROADCAST_ADDRESS
          value: cassandra-dr-1
        - name: CASSANDRA_SEEDS
          value: cassandra-dr-1,cassandra-dc-1
        - name: CASSANDRA_DC
          value: DR
        - name: CASSANDRA_RACK
          value: RACK1
        - name: CASSANDRA_ENDPOINT_SNITCH
          value: GossipingPropertyFileSnitch
        - name: MAX_HEAP_SIZE
          value: 50m
        - name: HEAP_NEWSIZE
          value: 10m
        resources: {}
      restartPolicy: Always
status: {}

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    kompose.cmd: kompose convert
    kompose.version: 1.7.0 (767ab4b)
  creationTimestamp: null
  labels:
    io.kompose.service: cassandra-dr-2
  name: cassandra-dr-2
spec:
  replicas: 1
  minReadySeconds: 60
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      creationTimestamp: null
      labels:
        io.kompose.service: cassandra-dr-2
    spec:
      containers:
      - args:
        - bash
        - -c
        - 'if [ -z "$$(ls -A /var/lib/cassandra/)" ] ; then sleep 180; fi && /docker-entrypoint.sh cassandra -f'
        image: cassandra:latest
        name: cassandra-dr-2
        imagePullPolicy: IfNotPresent
        env:
        - name: CASSANDRA_CLUSTER_NAME
          value: CassandraCluster
        - name: CASSANDRA_BROADCAST_ADDRESS
          value: cassandra-dr-2
        - name: CASSANDRA_SEEDS
          value: cassandra-dr-1,cassandra-dc-1
        - name: CASSANDRA_DC
          value: DR
        - name: CASSANDRA_RACK
          value: RACK2
        - name: CASSANDRA_ENDPOINT_SNITCH
          value: GossipingPropertyFileSnitch
        - name: MAX_HEAP_SIZE
          value: 50m
        - name: HEAP_NEWSIZE
          value: 10m
        resources: {}
      restartPolicy: Always
status: {}

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    kompose.cmd: kompose convert
    kompose.version: 1.7.0 (767ab4b)
  creationTimestamp: null
  labels:
    io.kompose.service: cassandra-dr-3
  name: cassandra-dr-3
spec:
  replicas: 1
  minReadySeconds: 60
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      creationTimestamp: null
      labels:
        io.kompose.service: cassandra-dr-3
    spec:
      containers:
      - args:
        - bash
        - -c
        - 'if [ -z "$$(ls -A /var/lib/cassandra/)" ] ; then sleep 300; fi && /docker-entrypoint.sh cassandra -f'
        image: cassandra:latest
        name: cassandra-dr-3
        imagePullPolicy: IfNotPresent
        env:
        - name: CASSANDRA_CLUSTER_NAME
          value: CassandraCluster
        - name: CASSANDRA_BROADCAST_ADDRESS
          value: cassandra-dr-3
        - name: CASSANDRA_SEEDS
          value: cassandra-dr-1,cassandra-dc-1
        - name: CASSANDRA_DC
          value: DR
        - name: CASSANDRA_RACK
          value: RACK3
        - name: CASSANDRA_ENDPOINT_SNITCH
          value: GossipingPropertyFileSnitch
        - name: MAX_HEAP_SIZE
          value: 50m
        - name: HEAP_NEWSIZE
          value: 10m
        resources: {}
      restartPolicy: Always
status: {}
{% endhighlight %}

Kemudian kembali ke menu dashboard, pilih menu create lalu isikan source diatas pada form, Berikut adalah jika pods dalam keadaan creating atau pull images.

![Screenshot from 2018-03-25 19-21-35.png](../images/Screenshot from 2018-03-25 19-21-35.png)

Dan berikut ketika seluruh pods telah berjalan.

![Screenshot from 2018-03-25 19-27-18.png](../images/Screenshot from 2018-03-25 19-27-18.png)

Lalu pada kubernetes dashboard, silahkan klik di salah satu pods, misalnya penulis ingin memilih pods `cassandra-dc-1-<code>`, contohnya `cassandra-dc-1-8f6fdf494-w247g`, maka akan muncul halaman seperti berikut.

![Screenshot from 2018-03-25 19-44-42.png](../images/Screenshot from 2018-03-25 19-44-42.png)

Lalu pilih menu `exec` dan akan muncul halaman seperti berikut.

![Screenshot from 2018-03-25 19-44-59.png](../images/Screenshot from 2018-03-25 19-44-59.png)

Pada halaman tersebut, kita dapat mengakses terminal yang terdapat pada pods tersebut. Lalu jalankan perintah berikut untuk melihat topology cassandra cluster yang telah terbentuk.

{% highlight bash %}
nodetool status
{% endhighlight %}

Jika berhasil maka akan muncul seperti ini

![Screenshot from 2018-03-25 19-46-15.png](../images/Screenshot from 2018-03-25 19-46-15.png)

Atau jika dalam bentuk bash

{% highlight bash %}
root@cassandra-dc-1-8f6fdf494-w247g:/# nodetool status
Datacenter: DC
==============
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
--  Address         Load       Tokens       Owns (effective)  Host ID                               Rack
UN  10.106.21.202   75.01 KiB  256          32.4%             f15606c4-e7e7-4dd3-81fc-5a072ac6b3d8  RACK3
UN  10.108.237.0    110.2 KiB  256          31.5%             a907cb28-3e39-4b0b-bce4-052e16485e99  RACK1
UN  10.108.167.162  74.97 KiB  256          33.7%             b9b8feb1-6ebc-442a-a386-44b8c9eea3da  RACK2
Datacenter: DR
==============
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
--  Address         Load       Tokens       Owns (effective)  Host ID                               Rack
UN  10.109.153.57   74.88 KiB  256          32.8%             f8d3f2b1-ebfc-4826-b1c9-39bc37e9c9bb  RACK1
UN  10.107.250.188  69.94 KiB  256          35.1%             00d659f4-d473-492f-afed-fc46ce1c0402  RACK2
UN  10.111.237.50   151.11 KiB  256         34.5%             7471c743-a2e1-4eb8-96f7-ad852d262e43  RACK3
{% endhighlight %}

Sekian artikel mengenai Instalasi Dan Setup Kubernetes Cluster dan terima kasih :).


text:
Recipes
=======

Adding a new language locally
-----------------------------

If you're translating a new language you'll need to translate the existing tg-react messages:

1. Make a new folder where you want to store the internationalization resources. Add this path to your LOCALE_PATHS setting.
2. Now create a subfolder for the language you want to translate. The folder should be named using locale name notation. For example: de, pt_BR, es_AR.
3. Now copy the base translations file from the tg-react source code into your translations folder.
4. Edit the django.po file you've just copied, translating all the messages.
5. Run manage.py compilemessages -l pt_BR to make the translations available for Django to use. You should see a message like processing file django.po in <...>/locale/pt_BR/LC_MESSAGES.
6. Restart your development server to see the changes take effect.

Changing the built-in translations
----------------------------------

Follow the process described in `Adding a new language locally`_ but instead of creating a new directory use the name of an
existing one.

redpajama_set_name: RedPajamaGithub


text:
  Le Fournet est
       un village
       localisé dans le département des Calvados en Basse-Normandie. Elle totalisait 50 habitants en 2008.

Si vous envisagez de emmenager à Le Fournet, vous pourrez aisément trouver une maison à acheter.

Le parc d'habitations, à Le Fournet, se décomposait en 2011 en zero appartements et 28 maisons soit
 un marché plutôt équilibré.

À proximité de Le Fournet sont localisées les communes de
Bonnebosq située à 1 km, 696 habitants,
Clarbec localisée à 5 km, 344 habitants,
Beaufour-Druval localisée à 5 km, 416 habitants,
Auvillars à 2 km, 205 habitants,
Montreuil-en-Auge située à 5 km, 49 habitants,
La Roque-Baignard à 2 km, 125 habitants,
 entre autres. De plus, Le Fournet est située à seulement onze km de Lisieux.


text:
#include <SDL.h>
#include <SDL_ttf.h>
#include <stdio.h>
#include <string>
#include <sstream>

#include "ball.h"
#include "paddle.h"

#define SCORE_LIMIT 3

struct GameTexture
{
    SDL_Texture* Texture;
    int Width;
    int Height;
};

class Game
{
private:
    GameTexture LoadTextTexture(std::string Text); //Color is implicit white

public:
    Game(); //Initialize, Load all the media files
    ~Game(); //Quit, destroy everything

    enum Game_Status {STARTING,READY_TO_LAUNCH,PLAYING,VICTORY_LEFT,VICTORY_RIGHT};
    Game_Status Status;

    int Screen_Height;
    int Screen_Width;

    //Window the game will run + the renderer
    SDL_Window *window;
    SDL_Renderer *renderer;

    //All are Test
    GameTexture LaunchText;
    GameTexture ScoreLeft;
    GameTexture ScoreRight;
    GameTexture VictoryLeft;
    GameTexture VictoryRight;
    GameTexture RestartText;
    TTF_Font *Font;

    //For paddle rendering
    SDL_Color WhiteColor;
    SDL_Color BlackColor;

    //Game Objects
    Ball_C Ball;
    Paddle_C Paddle_Player;
    Paddle_C Paddle_AI;

    //Mouse place
    int Mouse_x,Mouse_y; //Changes on Event

    //Score
    int ScoreLeftValue;
    int ScoreRightValue;

    //Cycle
    void LetTheGamesBegin(); //will handle all input

    //Auxiliary
    void Paddle_collision();
    //^ I cant come up with a better idea of solving this (due to needing access to both the ball and paddle..), thus this shall be a game-class function
    void Scoring();
    void Reset_Game();
};

#endif // _PONG_H_


text:
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Parse.Abstractions.Infrastructure;
using Parse.Abstractions.Infrastructure.Execution;
using Parse.Abstractions.Platform.Files;
using Parse.Infrastructure.Execution;
using Parse.Infrastructure.Utilities;

namespace Parse.Platform.Files
{
    public class ParseFileController : IParseFileController
    {
        IParseCommandRunner CommandRunner { get; }

        public ParseFileController(IParseCommandRunner commandRunner) => CommandRunner = commandRunner;

        public Task<FileState> SaveAsync(FileState state, Stream dataStream, string sessionToken, IProgress<IDataTransferLevel> progress, CancellationToken cancellationToken = default)
        {
            if (state.Location != null)
                // !isDirty

                return Task.FromResult(state);

            if (cancellationToken.IsCancellationRequested)
                return Task.FromCanceled<FileState>(cancellationToken);

            long oldPosition = dataStream.Position;

            return CommandRunner.RunCommandAsync(new ParseCommand($"files/{state.Name}", method: "POST", sessionToken: sessionToken, contentType: state.MediaType, stream: dataStream), uploadProgress: progress, cancellationToken: cancellationToken).OnSuccess(uploadTask =>
            {
                Tuple<HttpStatusCode, IDictionary<string, object>> result = uploadTask.Result;
                IDictionary<string, object> jsonData = result.Item2;
                cancellationToken.ThrowIfCancellationRequested();

                return new FileState
                {
                    Name = jsonData["name"] as string,
                    Location = new Uri(jsonData["url"] as string, UriKind.Absolute),
                    MediaType = state.MediaType
                };
            }).ContinueWith(task =>
            {
                // Rewind the stream on failure or cancellation (if possible).

                if ((task.IsFaulted || task.IsCanceled) && dataStream.CanSeek)
                    dataStream.Seek(oldPosition, SeekOrigin.Begin);

                return task;
            }).Unwrap();
        }
    }
}


text:
package co.colector.views;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import butterknife.BindView;
import butterknife.ButterKnife;
import co.colector.R;

/**
 * @author Gabriel Rodriuez
 * @version 1.0
 */
public class SectionItemView extends LinearLayout {

    @BindView(R.id.section_title)
    TextView title;
    @BindView(R.id.section_items_container)
    public LinearLayout sectionItemsContainer;

    public SectionItemView(Context context) {
        super(context);
        init(context);
    }

    public SectionItemView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public SectionItemView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        View view = LayoutInflater.from(context).inflate(R.layout.section_item_view, this, true);
        ButterKnife.bind(this, view);
    }

    /**
     * Set the title of the section Item View
     * @param title of the section
     */
    public void bind(String title) {
        this.title.setText(title);
    }
}


text:
package jk_5.nailed.api.text.selector;

/**
 * Utility class to create {@link Argument}s.
 */
public final class Arguments {

    private Arguments() {
    }

    /**
     * Creates a new {@link Argument} using the specified type and value.
     *
     * @param type The type of the argument
     * @param value The value of the argument
     * @param <T> The type of the argument value
     * @return The created argument
     */
    public static <T> Argument<T> create(ArgumentType<T> type, T value) {
        return Selectors.factory.createArgument(type, value);
    }

    /**
     * Creates a new {@link Argument.Invertible} using the specified type and
     * value. The created {@link Argument} will not be inverted.
     *
     * @param type The type of the invertible argument
     * @param value The value of the invertible argument
     * @param <T> The type of the argument value
     * @return The created invertible argument
     */
    public static <T> Argument.Invertible<T> create(ArgumentType.Invertible<T> type, T value) {
        return create(type, value, false);
    }

    /**
     * Creates a new {@link Argument.Invertible} using the specified type and
     * value. The created {@link Argument} will be inverted based on the given
     * parameter.
     *
     * @param type The type of the invertible argument
     * @param value The value of the invertible argument
     * @param inverted {@code true} if the argument should be inverted
     * @param <T> The type of the argument value
     * @return The created invertible argument
     */
    public static <T> Argument.Invertible<T> create(ArgumentType.Invertible<T> type, T value, boolean inverted) {
        return Selectors.factory.createArgument(type, value, inverted);
    }

    /**
     * Parses an {@link Argument} from the given argument string.
     *
     *
In Vanilla, it should be formatted like {@code key=value}.

     *
     * @param argument The argument string
     * @return The parsed argument
     * @throws IllegalArgumentException If the argument couldn't be parsed (e.g.
     *         due to invalid format)
     */
    public static Argument<?> parse(String argument) throws IllegalArgumentException {
        return Selectors.factory.parseArgument(argument);
    }

}


text:
<#
.SYNOPSIS
   This script updates the ClickOnce version in a project file (.csproj or .vbproj), and may update the MinimumRequiredVersion to be this same version.

.DESCRIPTION
   This script updates the current ClickOnce version in a project file (.csproj or .vbproj), and may update the MinimumRequiredVersion to be this same version.
   Setting the MinimumRequiredVersion property forces the ClickOnce application to update automatically without prompting the user.

.PARAMETER ProjectFilePath
	(required) Path of the .csproj and .vbproj file to process.

.PARAMETER Version
	The Version to update the ClickOnce version number to. This version must be of the format Major.Minor.Build or Major.Minor.Build.Revision.
	The Build and Revision parts will be overridden by the BuildSystemsBuildId parameter, if it is provided.
	The Revision parts will be overriden by the IncrementProjectFilesRevision parameter, if it is provided.

.PARAMETER BuildSystemsBuildId
	The build system's unique and auto-incrementing Build ID. This will be used to generate the Build and Revision parts of the new Version number.
	This will override the Build and Revision specified in the Version parameter, if it was provided.
	This parameter cannot be used with the IncrementProjectFilesRevision parameter.

.PARAMETER IncrementProjectFilesRevision
	If this switch is provided, the Revision from the project file will be incremented and used in the new ClickOnce Version.
	This will override the Revision specified in the Version parameter, if it was provided.
	This parameter cannot be used with the BuildSystemsBuildId parameter.

.PARAMETER UpdateMinimumRequiredVersionToCurrentVersion
	If this switch is provided, the ClickOnce MinimumRequiredVersion will be updated to match the new Version.
	Setting the MinimumRequiredVersion property forces the ClickOnce application to update automatically without prompting the user.

.PARAMETER PublishUrl
	If this string is provided, it will update the PublishUrl. If it is not provided, the PublishUrl will remain what it is currently. This should be a UNC type file path (e.g. \\servername\foldername)

.PARAMETER InstallUrl
	If this string is provided, it will update the InstallUrl. If it is not provided the InstallUrl will remain what it is currently. This should be a URL type path (e.g. http://servername/foldername)

.EXAMPLE
	Update a project file's ClickOnce version to the specified version.

	& .\Set-ProjectFilesClickOnceVersion.ps1 -ProjectFilePath "C:\SomeProject.csproj" -Version '1.2.3.4'

.EXAMPLE
	Update the Build and Revision parts of a project file's ClickOnce version, based on a unique, auto-incrementing integer, such as a build system's Build ID.

	& .\Set-ProjectFilesClickOnceVersion.ps1 -ProjectFilePath "C:\SomeProject.csproj" -BuildSystemsBuildId 123456

.EXAMPLE
	Increment the Revision of a project file's ClickOnce version.

	& .\Set-ProjectFilesClickOnceVersion.ps1 -ProjectFilePath "C:\SomeProject.csproj" -IncrementProjectFilesRevision

.EXAMPLE
	Update a project file's ClickOnce Minimum Required Version to match its current version.

	& .\Set-ProjectFilesClickOnceVersion.ps1 -ProjectFilePath "C:\SomeProject.csproj" -UpdateMinimumRequiredVersionToCurrentVersion

.EXAMPLE
	Update a project file's ClickOnce version, ignoring the Revision part and incrementing the Revision stored in the file, and update the Minimum Required Version to be this new version.

	& .\Set-ProjectFilesClickOnceVersion.ps1 -ProjectFilePath "C:\SomeProject.csproj" -Version '1.2.3' -IncrementProjectFilesRevision -UpdateMinimumRequiredVersionToCurrentVersion

.EXAMPLE
	Update a project file's ClickOnce version using both Version and a unique, auto-incrementing integer, such as a build system's Build ID. This will keep the major and minor versions you specify but update the build and revision (e.g. 1.0.1.5745)

	& .\Set-ProjectFilesClickOnceVersion.ps1 -ProjectFilePath "C:\SomeProject.csproj" -Version 1.0.0.0 -BuildSystemBuildId 123456

.EXAMPLE
	Update a project file's ClickOnce version and its install and publish url values.

	& .\Set-ProjectFilesClickOnceVersion.ps1 -ProjectFilePath "C:\SomeProject.csproj" -Version 1.0.1.9 -PublishUrl "\\fileshare\foldername" -InstallUrl "http://fileshare/foldername"

.LINK
	Project Home: https://github.com/deadlydog/Set-ProjectFilesClickOnceVersion

.NOTES
	Author: Daniel Schroeder
	Version: 2.1.0
#>
[CmdletBinding(DefaultParameterSetName="UseBuildSystemsBuildId")]
Param
(
	[Parameter(Mandatory=$true,HelpMessage="The project file to update the ClickOnce Version in.")]
	[string]$ProjectFilePath = [string]::Empty,

	[Parameter(Mandatory=$false,HelpMessage="The new version number to use for the ClickOnce application.")]
	[ValidatePattern('(?i)(^(\d+(\.\d+){2,3})$)')]
	[string]$Version = [string]::Empty,

	[Parameter(Mandatory=$false,HelpMessage="The build system's unique, auto-incrementing Build ID. This will be used to generate the Build and Revision version parts.",ParameterSetName="UseBuildSystemsBuildId")]
	[Alias("BuildId")]
	[Alias("Id")]
	[int]$BuildSystemsBuildId = -1,

	[Parameter(Mandatory=$false,HelpMessage="Use and increment the Revision part of the version number stored in the project file.",ParameterSetName="UseFilesRevision")]
	[Alias("IncrementRevision")]
	[switch]$IncrementProjectFilesRevision = $false,

	[Parameter(Mandatory=$false,HelpMessage="When the switch is provided, the ClickOnce Minimum Required Version will be updated to this new version.")]
	[switch]$UpdateMinimumRequiredVersionToCurrentVersion = $false,

	[Parameter(Mandatory = $false, HelpMessage="The Publish URL to update to.")]
	[string]$PublishUrl = [string]::Empty,

	[Parameter(Mandatory = $false, HelpMessage="The Install URL to update to.")]
	[string]$InstallUrl = [string]::Empty
)

# If we can't find the project file path to update, exit with an error.
$ProjectFilePath = Resolve-Path -Path $ProjectFilePath
if (!(Test-Path $ProjectFilePath -PathType Leaf))
{
	throw "Could not locate the project file to update at the path '$ProjectFilePath'."
}

# If there are no changes to make, just exit.
if ([string]::IsNullOrEmpty($Version) -and $BuildSystemsBuildId -lt 0 -and !$IncrementProjectFilesRevision -and !$UpdateMinimumRequiredVersionToCurrentVersion -and [string]::IsNullOrEmpty($InstallUrl) -and [string]::IsNullOrEmpty($PublishUrl))
{
	Write-Warning "None of the following parameters were provided, so nothing will be changed: Version, BuildSystemsBuildId, IncrementProjectFilesRevision, UpdateMinimumRequiredVersionToCurrentVersion, InstallUrl, and PublishUrl"
	return
}

function Get-XmlNamespaceManager([xml]$XmlDocument, [string]$NamespaceURI = "")
{
    # If a Namespace URI was not given, use the Xml document's default namespace.
	if ([string]::IsNullOrEmpty($NamespaceURI)) { $NamespaceURI = $XmlDocument.DocumentElement.NamespaceURI }

	# In order for SelectSingleNode() to actually work, we need to use the fully qualified node path along with an Xml Namespace Manager, so set them up.
	[System.Xml.XmlNamespaceManager]$xmlNsManager = New-Object System.Xml.XmlNamespaceManager($XmlDocument.NameTable)
	$xmlNsManager.AddNamespace("ns", $NamespaceURI)
    return ,$xmlNsManager		# Need to put the comma before the variable name so that PowerShell doesn't convert it into an Object[].
}

function Get-FullyQualifiedXmlNodePath([string]$NodePath, [string]$NodeSeparatorCharacter = '.')
{
    return "/ns:$($NodePath.Replace($($NodeSeparatorCharacter), '/ns:'))"
}

function Get-XmlNode([xml]$XmlDocument, [string]$NodePath, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.')
{
	$xmlNsManager = Get-XmlNamespaceManager -XmlDocument $XmlDocument -NamespaceURI $NamespaceURI
	[string]$fullyQualifiedNodePath = Get-FullyQualifiedXmlNodePath -NodePath $NodePath -NodeSeparatorCharacter $NodeSeparatorCharacter

	# Try and get the node, then return it. Returns $null if the node was not found.
	$node = $XmlDocument.SelectSingleNode($fullyQualifiedNodePath, $xmlNsManager)
	return $node
}

function Get-XmlNodes([xml]$XmlDocument, [string]$NodePath, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.')
{
	$xmlNsManager = Get-XmlNamespaceManager -XmlDocument $XmlDocument -NamespaceURI $NamespaceURI
	[string]$fullyQualifiedNodePath = Get-FullyQualifiedXmlNodePath -NodePath $NodePath -NodeSeparatorCharacter $NodeSeparatorCharacter

	# Try and get the nodes, then return them. Returns $null if no nodes were found.
	$nodes = $XmlDocument.SelectNodes($fullyQualifiedNodePath, $xmlNsManager)
	return $nodes
}

function Get-XmlElementsTextValue([xml]$XmlDocument, [string]$ElementPath, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.')
{
	# Try and get the node.
	$node = Get-XmlNode -XmlDocument $XmlDocument -NodePath $ElementPath -NamespaceURI $NamespaceURI -NodeSeparatorCharacter $NodeSeparatorCharacter

	# If the node already exists, return its value, otherwise return null.
	if ($node) { return $node.InnerText } else { return $null }
}

function Set-XmlElementsTextValue([xml]$XmlDocument, [string]$ElementPath, [string]$TextValue, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.')
{
	# Try and get the node.
	$node = Get-XmlNode -XmlDocument $XmlDocument -NodePath $ElementPath -NamespaceURI $NamespaceURI -NodeSeparatorCharacter $NodeSeparatorCharacter

	# If the node already exists, update its value.
	if ($node)
	{
		$node.InnerText = $TextValue
	}
	# Else the node doesn't exist yet, so create it with the given value.
	else
	{
		# Create the new element with the given value.
		$elementName = $ElementPath.Substring($ElementPath.LastIndexOf($NodeSeparatorCharacter) + 1)
 		$element = $XmlDocument.CreateElement($elementName, $XmlDocument.DocumentElement.NamespaceURI)
		$textNode = $XmlDocument.CreateTextNode($TextValue)
		$element.AppendChild($textNode) > $null

		# Try and get the parent node.
		$parentNodePath = $ElementPath.Substring(0, $ElementPath.LastIndexOf($NodeSeparatorCharacter))
		$parentNode = Get-XmlNode -XmlDocument $XmlDocument -NodePath $parentNodePath -NamespaceURI $NamespaceURI -NodeSeparatorCharacter $NodeSeparatorCharacter

		if ($parentNode)
		{
			$parentNode.AppendChild($element) > $null
		}
		else
		{
			throw "$parentNodePath does not exist in the xml."
		}
	}
}

function Set-XmlNodesElementTextValue([xml]$xml, $node, $elementName, $textValue)
{
	if ($null -eq $node.($elementName))
	{
		$element = $xml.CreateElement($elementName, $xml.DocumentElement.NamespaceURI)
		$textNode = $xml.CreateTextNode($textValue)
		$element.AppendChild($textNode) > $null
		$node.AppendChild($element) > $null
	}
	else
	{
		$node.($elementName) = $textValue
	}
}

# Define the max value that a version part is allowed to have (16-bit int).
[int]$maxVersionPartValueAllowed = 65535

# The regex used to obtain the Major.Minor.Build.Revision parts from a version number (revision is optional).
$versionNumberRegex = New-Object System.Text.RegularExpressions.Regex "(?<MajorMinor>^\d+\.\d+)\.(?<Build>\d+)(\.(?<Revision>\d+))?", SingleLine

# Open the Xml file and get the <PropertyGroup> elements with the ClickOnce properties in it.
[xml]$xml = Get-Content -Path $ProjectFilePath
$propertyGroups = Get-XmlNodes -XmlDocument $xml -NodePath 'Project.PropertyGroup'
[Array]$clickOncePropertyGroups = $propertyGroups | Where-Object {
	try
	{
		return ($_.ApplicationVersion -ne $null)
	}
	catch { return $false }
}

# If no ClickOnce deployment settings were found throw an error.
if ($null -eq $clickOncePropertyGroups -or $clickOncePropertyGroups.Count -eq 0)
{
	throw "'$ProjectFilePath' does not appear to have any ClickOnce deployment settings in it. You must publish the project at least once to create the ClickOnce deployment settings."
}

# Iterate over each <PropertyGroup> that has ClickOnce deployment settings and update them.
$numberOfClickOncePropertyGroups = $clickOncePropertyGroups.Length
$numberOfClickOncePropertyGroupsProcessed = 0
foreach ($clickOncePropertyGroup in $clickOncePropertyGroups)
{
	$numberOfClickOncePropertyGroupsProcessed++
	Write-Verbose "Processing ClickOnce property group $numberOfClickOncePropertyGroupsProcessed of $numberOfClickOncePropertyGroups in file '$ProjectFilePath'."

	# If publish url is provided, update it.
	if (![string]::IsNullOrEmpty($PublishUrl))
	{
		Write-Verbose "Publish Url is '$PublishUrl'"
		Write-Output "Updating PublishUrl to be '$PublishUrl'"
		Set-XmlNodesElementTextValue -xml $xml -node $clickOncePropertyGroup -elementName 'PublishUrl' -textValue "$PublishUrl"
	}

	# If install url is provided, update it.
	if (![string]::IsNullOrEmpty($InstallUrl))
	{
		Write-Verbose "Install Url is '$InstallUrl'"
		Write-Output "Updating Install Url to be '$InstallUrl'"
		Set-XmlNodesElementTextValue -xml $xml -node $clickOncePropertyGroup -elementName 'InstallUrl' -textValue "$InstallUrl"
	}

	# If the Version to use was not provided, get it from the project file.
	$appVersion = $Version
	if ([string]::IsNullOrEmpty($appVersion))
	{
		$appVersion = $clickOncePropertyGroup.ApplicationVersion
	}

	# Get the Major, Minor, and Build parts of the version number.
	$majorMinorBuildMatch = $versionNumberRegex.Match($appVersion)
	if (!$majorMinorBuildMatch.Success)
	{
		throw "The version number '$appVersion' does not seem to have valid Major.Minor.Build version parts."
	}
	$majorMinor = $majorMinorBuildMatch.Groups["MajorMinor"].Value
	[int]$build = $majorMinorBuildMatch.Groups["Build"].Value
	[int]$revision = -1

	# If a Revision was specified in the Version, get it.
	if (![string]::IsNullOrWhiteSpace($majorMinorBuildMatch.Groups["Revision"]))
	{
		$revision = [int]::Parse($majorMinorBuildMatch.Groups["Revision"])
	}

	# If we should be using the BuildSystemsBuildId for the Build and Revision.
	if ($BuildSystemsBuildId -gt -1)
	{
		# Use a calculation for the Build and Revision to prevent the Revision value from being too large, and to increment the Build value as the BuildSystemsBuildId continues to grow larger.
		$build = [int][Math]::Floor($BuildSystemsBuildId / $maxVersionPartValueAllowed)
		$revision = $BuildSystemsBuildId % $maxVersionPartValueAllowed

		Write-Verbose "Translated BuildSystemsBuildId '$BuildSystemsBuildId' into Build.Revision '$build.$revision'."
	}

	# Else if we should be incrementing the file's revision, or we don't have the revision yet, get the Revision from the project file.
	if ($IncrementProjectFilesRevision -or $revision -eq -1)
	{
		# If the Revision is missing from the file, or not in a valid format, throw an error.
		$applicationRevisionString = $clickOncePropertyGroup.ApplicationRevision
		if ($null -eq $applicationRevisionString)
		{
			throw "Could not find the <ApplicationRevision> element in the project file '$ProjectFilePath'."
		}
		if (!($applicationRevisionString -imatch '^\d+$'))
		{
			throw "The <ApplicationRevision> elements value '$applicationRevisionString' in the file '$ProjectFilePath' does not appear to be a valid integer."
		}

		$revision = [int]::Parse($applicationRevisionString)

		# If the Revision should be incremented, do it.
		if ($IncrementProjectFilesRevision)
		{
			$revision = $revision + 1

			# Make sure the Revision version part is not greater than the max allowed value.
			if ($revision -gt $maxVersionPartValueAllowed)
			{
				Write-Warning "The Revision value '$revision' to use for the last part of the version number is greater than the max allowed value of '$maxVersionPartValueAllowed'. The modulus will be used for the revision instead. If this results in your ClickOnce deployment not downloading the latest update and giving an error message like 'Cannot activate a deployment with earlier version than the current minimum required version of the application.' then you will need to increment the Build part of the ClickOnce <ApplicationVersion> value stored in the project file."
				$revision %= $maxVersionPartValueAllowed
			}
		}
	}

	# Create the version number to use for the ClickOnce version.
	$newMajorMinorBuild = "$majorMinor.$build"
	$newVersionNumber = "$newMajorMinorBuild.$revision"
	Write-Output "Updating version number to be '$newVersionNumber'."

	# Write the new values to the file.
	Set-XmlNodesElementTextValue -xml $xml -node $clickOncePropertyGroup -elementName 'ApplicationVersion' -textValue "$newMajorMinorBuild.%2a"
	Set-XmlNodesElementTextValue -xml $xml -node $clickOncePropertyGroup -elementName 'ApplicationRevision' -textValue $revision.ToString()
	if ($UpdateMinimumRequiredVersionToCurrentVersion)
	{
		Write-Output "Updating minimum required version to be '$newVersionNumber'."
		Set-XmlNodesElementTextValue -xml $xml -node $clickOncePropertyGroup -elementName 'MinimumRequiredVersion' -textValue "$newVersionNumber"
		Set-XmlNodesElementTextValue -xml $xml -node $clickOncePropertyGroup -elementName 'UpdateRequired' -textValue 'true'
		Set-XmlNodesElementTextValue -xml $xml -node $clickOncePropertyGroup -elementName 'UpdateEnabled' -textValue 'true'
	}
}

# Save the changes.
$xml.Save($ProjectFilePath)


text:
export { AuthenticationModule } from './authentication/authentication.module';
export { AccountModule } from './account/account.module';
export { ChallengesModule } from './challenges/challenges.module';
export { HomeModule } from './home/home.module';
export { JudgeModule } from './judge/judge.module';
export { TeamsModule } from './teams/teams.module';


text:
<!DOCTYPE html>
<html>

  {% include head.html %}

    <body id="page-top1" class="index">
    {% include modal.html %}
    {% include footer.html %}
    {% include js.html %}

    </body>
</html>


text:
/* TEMPLATE GENERATED TESTCASE FILE
Filename: CWE762_Mismatched_Memory_Management_Routines__new_array_free_int_83.h
Label Definition File: CWE762_Mismatched_Memory_Management_Routines__new_array_free.label.xml
Template File: sources-sinks-83.tmpl.h
*/
/*
 * @description
 * CWE: 762 Mismatched Memory Management Routines
 * BadSource:  Allocate data using new []
 * GoodSource: Allocate data using malloc()
 * Sinks:
 *    GoodSink: Deallocate data using delete []
 *    BadSink : Deallocate data using free()
 * Flow Variant: 83 Data flow: data passed to class constructor and destructor by declaring the class object on the stack
 *
 * */

#include "std_testcase.h"

namespace CWE762_Mismatched_Memory_Management_Routines__new_array_free_int_83
{

#ifndef OMITBAD

class CWE762_Mismatched_Memory_Management_Routines__new_array_free_int_83_bad
{
public:
    CWE762_Mismatched_Memory_Management_Routines__new_array_free_int_83_bad(int * dataCopy);
    ~CWE762_Mismatched_Memory_Management_Routines__new_array_free_int_83_bad();

private:
    int * data;
};

#endif /* OMITBAD */

#ifndef OMITGOOD

class CWE762_Mismatched_Memory_Management_Routines__new_array_free_int_83_goodG2B
{
public:
    CWE762_Mismatched_Memory_Management_Routines__new_array_free_int_83_goodG2B(int * dataCopy);
    ~CWE762_Mismatched_Memory_Management_Routines__new_array_free_int_83_goodG2B();

private:
    int * data;
};

class CWE762_Mismatched_Memory_Management_Routines__new_array_free_int_83_goodB2G
{
public:
    CWE762_Mismatched_Memory_Management_Routines__new_array_free_int_83_goodB2G(int * dataCopy);
    ~CWE762_Mismatched_Memory_Management_Routines__new_array_free_int_83_goodB2G();

private:
    int * data;
};

#endif /* OMITGOOD */

}


text:
<!DOCTYPE html>

		<meta charset="utf-8" />
		<title></title>
		<link rel="stylesheet" type="text/css" href="./css/crystal_style.css">

					◇ DPS Meter v2016.06.16

					Modify by Laiglinne - [KR] Altema

내 이름 표시 <input id="myname" type="checkbox" checked="checked" />
				<input id="mynameval" type="text" value="나" />

크기조절 표시 <input type="checkbox" checked="checked" onclick="resizeview();" id="showresize"/>

미터기 닉네임 표시 <input id="showname" type="checkbox" checked="checked" onclick="goTab(currenttab + 1, $('#tab' + currenttab));"/>

딜량

힐량

피격

기타


text:
#include "postgres.h"

#include <unistd.h>

#include "access/xact.h"
#include "miscadmin.h"
#include "replication/syncrep.h"
#include "replication/walsender.h"
#include "replication/walsender_private.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
#include "storage/procsignal.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/ps_status.h"
#include "utils/faultinjector.h"
#include "pgstat.h"
#include "cdb/cdbvars.h"

/* User-settable parameters for sync rep */
char	   *SyncRepStandbyNames;

#define SyncStandbysDefined() \
	(SyncRepStandbyNames != NULL && SyncRepStandbyNames[0] != '\0')

static bool announce_next_takeover = true;

static SyncRepConfigData *SyncRepConfig = NULL;
static int	SyncRepWaitMode = SYNC_REP_NO_WAIT;

static void SyncRepQueueInsert(int mode);
static void SyncRepCancelWait(void);

static bool SyncRepGetOldestSyncRecPtr(XLogRecPtr *writePtr,
						   XLogRecPtr *flushPtr,
						   XLogRecPtr *applyPtr,
						   bool *am_sync);
static int	SyncRepGetStandbyPriority(void);

#ifdef USE_ASSERT_CHECKING
static bool SyncRepQueueIsOrderedByLSN(int mode);
#endif

/*
 * ===========================================================
 * Synchronous Replication functions for normal user backends
 * ===========================================================
 */

/*
 * Wait for synchronous replication, if requested by user.
 *
 * Initially backends start in state SYNC_REP_NOT_WAITING and then
 * change that state to SYNC_REP_WAITING before adding ourselves
 * to the wait queue. During SyncRepWakeQueue() a WALSender changes
 * the state to SYNC_REP_WAIT_COMPLETE once replication is confirmed.
 * This backend then resets its state to SYNC_REP_NOT_WAITING.
 *
 * 'lsn' represents the LSN to wait for.  'commit' indicates whether this LSN
 * represents a commit record.  If it doesn't, then we wait only for the WAL
 * to be flushed if synchronous_commit is set to the higher level of
 * remote_apply, because only commit records provide apply feedback.
 */
void
SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
{
	char	   *new_status = NULL;
	const char *old_status;
	int			mode;

	/* Cap the level for anything other than commit to remote flush only. */
	if (commit)
		mode = SyncRepWaitMode;
	else
		mode = Min(SyncRepWaitMode, SYNC_REP_WAIT_FLUSH);

	/*
	 * SIGUSR1 is used to wake us up, cannot wait from inside SIGUSR1 handler
	 * as its non-reentrant, so check for the same and avoid waiting.
	 */
	if (AmIInSIGUSR1Handler())
	{
		return;
	}
	Assert(!am_walsender);
	elogif(debug_walrepl_syncrep, LOG,
			"syncrep wait -- This backend's commit LSN for syncrep is %X/%X.",
		   (uint32) (lsn >> 32), (uint32) lsn);

	/* Fast exit if user has not requested sync replication. */
	if (!SyncRepRequested())
		return;

	Assert(SHMQueueIsDetached(&(MyProc->syncRepLinks)));
	Assert(WalSndCtl != NULL);

	LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);
	Assert(MyProc->syncRepState == SYNC_REP_NOT_WAITING);

	if (IS_QUERY_DISPATCHER())
	{
		/*
		 * There could be a better way to figure out if there is any active
		 * standby.  But currently, let's move ahead by looking at the per WAL
		 * sender structure to see if anyone is really active, streaming (or
		 * still catching up within limits) and wants to be synchronous.
		 */
		bool		syncStandbyPresent = false;
		int			i;

		for (i = 0; i < max_wal_senders; i++)
		{
			/* use volatile pointer to prevent code rearrangement */
			volatile WalSnd *walsnd = &WalSndCtl->walsnds[i];

			SpinLockAcquire(&walsnd->mutex);
			syncStandbyPresent = (walsnd->pid != 0)
				&& ((walsnd->state == WALSNDSTATE_STREAMING)
					|| (walsnd->state == WALSNDSTATE_CATCHUP &&
						walsnd->caughtup_within_range));
			SpinLockRelease(&walsnd->mutex);

			if (syncStandbyPresent)
				break;
		}

		/* See if we found any active standby connected. If NO, no need to wait.*/
		if (!syncStandbyPresent)
		{
			elogif(debug_walrepl_syncrep, LOG,
					"syncrep wait -- Not waiting for syncrep because no active and synchronous "
					"standby (walsender) was found.");

			LWLockRelease(SyncRepLock);
			return;
		}
	}

	/*
	 * We don't wait for sync rep if WalSndCtl->sync_standbys_defined is not
	 * set.  See SyncRepUpdateSyncStandbysDefined.
	 *
	 * Also check that the standby hasn't already replied. Unlikely race
	 * condition but we'll be fetching that cache line anyway so it's likely
	 * to be a low cost check.
	 */
	if (((!IS_QUERY_DISPATCHER()) && !WalSndCtl->sync_standbys_defined) ||
		lsn <= WalSndCtl->lsn[mode])
	{
		elogif(debug_walrepl_syncrep, LOG,
				"syncrep wait -- Not waiting for syncrep because xlog upto LSN (%X/%X) which is "
				"greater than this backend's commit LSN (%X/%X) has already "
				"been replicated.",
			   (uint32) (WalSndCtl->lsn[mode] >> 32), (uint32) WalSndCtl->lsn[mode],
			   (uint32) (lsn >> 32), (uint32) lsn);

		LWLockRelease(SyncRepLock);
		return;
	}

	/*
	 * Set our waitLSN so WALSender will know when to wake us, and add
	 * ourselves to the queue.
	 */
	MyProc->waitLSN = lsn;
	MyProc->syncRepState = SYNC_REP_WAITING;
	SyncRepQueueInsert(mode);
	Assert(SyncRepQueueIsOrderedByLSN(mode));
	LWLockRelease(SyncRepLock);

	elogif(debug_walrepl_syncrep, LOG,
			"syncrep wait -- This backend is now inserted in the syncrep queue.");

	/* Alter ps display to show waiting for sync rep. */
	if (update_process_title)
	{
		int			len;

		old_status = get_real_act_ps_display(&len);
		/*
		 * The 32 represents the bytes in the string " waiting for %X/%X", as
		 * in upstream.  The 12 represents GPDB specific " replication" suffix.
		 */
		new_status = (char *) palloc(len + 32 + 12 + 1);
		memcpy(new_status, old_status, len);
		sprintf(new_status + len, " waiting for %X/%X replication",
				(uint32) (lsn >> 32), (uint32) lsn);
		set_ps_display(new_status, false);
		new_status[len] = '\0'; /* truncate off " waiting ..." */
	}

	/* Report the wait */
	pgstat_report_wait_start(WAIT_REPLICATION, 0);

	/*
	 * Wait for specified LSN to be confirmed.
	 *
	 * Each proc has its own wait latch, so we perform a normal latch
	 * check/wait loop here.
	 */
	for (;;)
	{
		/* Must reset the latch before testing state. */
		ResetLatch(MyLatch);

		/*
		 * Acquiring the lock is not needed, the latch ensures proper
		 * barriers. If it looks like we're done, we must really be done,
		 * because once walsender changes the state to SYNC_REP_WAIT_COMPLETE,
		 * it will never update it again, so we can't be seeing a stale value
		 * in that case.
		 */
		if (MyProc->syncRepState == SYNC_REP_WAIT_COMPLETE)
		{
			elogif(debug_walrepl_syncrep, LOG,
					"syncrep wait -- This backend's syncrep state is now 'wait complete'.");
			break;
		}

		SIMPLE_FAULT_INJECTOR("sync_rep_query_die");

		/*
		 * If a wait for synchronous replication is pending, we can neither
		 * acknowledge the commit nor raise ERROR or FATAL.  The latter would
		 * lead the client to believe that the transaction aborted, which is
		 * not true: it's already committed locally. The former is no good
		 * either: the client has requested synchronous replication, and is
		 * entitled to assume that an acknowledged commit is also replicated,
		 * which might not be true. So in this case we issue a WARNING (which
		 * some clients may be able to interpret) and shut off further output.
		 * We do NOT reset ProcDiePending, so that the process will die after
		 * the commit is cleaned up.
		 */
		if (ProcDiePending)
		{
			/*
			 * For QE we should have done FATAL here so that 2PC can retry, but
			 * FATAL here makes some shm exit callback functions panic or
			 * assert fail because the transaction is still not finished, so
			 * let's defer the quitting to exec_mpp_dtx_protocol_command().
			 */
			ereport(WARNING,
					(errcode(ERRCODE_ADMIN_SHUTDOWN),
					 errmsg("canceling the wait for synchronous replication and terminating connection due to administrator command")));
			whereToSendOutput = DestNone;
			SyncRepCancelWait();
			break;
		}

		/*
		 * GPDB: There are multiple code paths going through this function,
		 * e.g. prepare, commit, and abort. To ensure MPP cluster consistency,
		 * if primary already changed, then this backend has to wait for the
		 * xlog record replicate to the mirror to avoid inconsistency between
		 * the primary and the mirror, since they are under synced replication.
		 *
		 * If the mirror is indeed offline and prevents xlog to be synced, FTS
		 * will detect the mirror goes down, and failure handling will kick-in
		 * and mark the mirror down and out-of-sync with the primary to prevent
		 * failover. Then the syncrep will be turned off by the FTS to unblock
		 * backends waiting here.
		 */
		if (QueryCancelPending && commit)
		{
			QueryCancelPending = false;
			ereport(WARNING,
					(errmsg("ignoring query cancel request for synchronous replication to ensure cluster consistency"),
					 errdetail("The transaction has already changed locally, it has to be replicated to standby.")));
			SIMPLE_FAULT_INJECTOR("sync_rep_query_cancel");
		}

		/*
		 * If the postmaster dies, we'll probably never get an
		 * acknowledgement, because all the wal sender processes will exit. So
		 * just bail out.
		 */
		if (!PostmasterIsAlive())
		{
			ProcDiePending = true;
			ereport(WARNING,
				(errcode(ERRCODE_ADMIN_SHUTDOWN),
				 errmsg("canceling the wait for synchronous replication and terminating connection due to postmaster death")));
			whereToSendOutput = DestNone;
			SyncRepCancelWait();
			break;
		}

		elogif(debug_walrepl_syncrep, LOG,
				"syncrep wait -- This backend's syncrep state is still 'waiting'. "
				"Hence it will wait on a latch until awakend.")
		/*
		 * Wait on latch.  Any condition that should wake us up will set the
		 * latch, so no need for timeout.
		 */
		WaitLatch(MyLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1);
	}

	/*
	 * WalSender has checked our LSN and has removed us from queue. Clean up
	 * state and leave.  It's OK to reset these shared memory fields without
	 * holding SyncRepLock, because any walsenders will ignore us anyway when
	 * we're not on the queue.
	 */
	Assert(SHMQueueIsDetached(&(MyProc->syncRepLinks)));
	MyProc->syncRepState = SYNC_REP_NOT_WAITING;
	MyProc->waitLSN = 0;

	pgstat_report_wait_end();

	if (new_status)
	{
		/* Reset ps display */
		set_ps_display(new_status, false);
		pfree(new_status);
	}
}

/*
 * Insert MyProc into the specified SyncRepQueue, maintaining sorted invariant.
 *
 * Usually we will go at tail of queue, though it's possible that we arrive
 * here out of order, so start at tail and work back to insertion point.
 */
static void
SyncRepQueueInsert(int mode)
{
	PGPROC	   *proc;

	Assert(mode >= 0 && mode < NUM_SYNC_REP_WAIT_MODE);
	proc = (PGPROC *) SHMQueuePrev(&(WalSndCtl->SyncRepQueue[mode]),
								   &(WalSndCtl->SyncRepQueue[mode]),
								   offsetof(PGPROC, syncRepLinks));

	while (proc)
	{
		/*
		 * Stop at the queue element that we should after to ensure the queue
		 * is ordered by LSN.
		 */
		if (proc->waitLSN < MyProc->waitLSN)
			break;

		proc = (PGPROC *) SHMQueuePrev(&(WalSndCtl->SyncRepQueue[mode]),
									   &(proc->syncRepLinks),
									   offsetof(PGPROC, syncRepLinks));
	}

	if (proc)
		SHMQueueInsertAfter(&(proc->syncRepLinks), &(MyProc->syncRepLinks));
	else
		SHMQueueInsertAfter(&(WalSndCtl->SyncRepQueue[mode]), &(MyProc->syncRepLinks));
}

/*
 * Acquire SyncRepLock and cancel any wait currently in progress.
 */
static void
SyncRepCancelWait(void)
{
	LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);
	if (!SHMQueueIsDetached(&(MyProc->syncRepLinks)))
		SHMQueueDelete(&(MyProc->syncRepLinks));
	MyProc->syncRepState = SYNC_REP_NOT_WAITING;
	LWLockRelease(SyncRepLock);
}

void
SyncRepCleanupAtProcExit(void)
{
	if (!SHMQueueIsDetached(&(MyProc->syncRepLinks)))
	{
		LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);
		SHMQueueDelete(&(MyProc->syncRepLinks));
		LWLockRelease(SyncRepLock);
	}
}

/*
 * ===========================================================
 * Synchronous Replication functions for wal sender processes
 * ===========================================================
 */

/*
 * Take any action required to initialise sync rep state from config
 * data. Called at WALSender startup and after each SIGHUP.
 */
void
SyncRepInitConfig(void)
{
	int			priority;

	/*
	 * Determine if we are a potential sync standby and remember the result
	 * for handling replies from standby.
	 */
	priority = SyncRepGetStandbyPriority();
	if (MyWalSnd->sync_standby_priority != priority)
	{
		LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);
		MyWalSnd->sync_standby_priority = priority;
		LWLockRelease(SyncRepLock);
		ereport(DEBUG1,
			(errmsg("standby \"%s\" now has synchronous standby priority %u",
					application_name, priority)));
	}
}

/*
 * Update the LSNs on each queue based upon our latest state. This
 * implements a simple policy of first-valid-sync-standby-releases-waiter.
 *
 * Other policies are possible, which would change what we do here and
 * perhaps also which information we store as well.
 */
void
SyncRepReleaseWaiters(void)
{
	volatile WalSndCtlData *walsndctl = WalSndCtl;
	XLogRecPtr	writePtr;
	XLogRecPtr	flushPtr;
	XLogRecPtr	applyPtr;
	bool		got_oldest;
	bool		am_sync;
	int			numwrite = 0;
	int			numflush = 0;
	int			numapply = 0;

	/*
	 * If this WALSender is serving a standby that is not on the list of
	 * potential sync standbys then we have nothing to do. If we are still
	 * starting up, still running base backup or the current flush position
	 * is still invalid, then leave quickly also. Streaming or stopping WAL
	 * senders are allowed to release waiters.
	 */
	if (MyWalSnd->sync_standby_priority == 0 ||
		(MyWalSnd->state != WALSNDSTATE_STREAMING &&
		 MyWalSnd->state != WALSNDSTATE_STOPPING) ||
		XLogRecPtrIsInvalid(MyWalSnd->flush))
	{
		announce_next_takeover = true;
		return;
	}

	/*
	 * We're a potential sync standby. Release waiters if there are enough
	 * sync standbys and we are considered as sync.
	 */
	LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);

	/*
	 * Check whether we are a sync standby or not, and calculate the oldest
	 * positions among all sync standbys.
	 */
	got_oldest = SyncRepGetOldestSyncRecPtr(&writePtr, &flushPtr,
											&applyPtr, &am_sync);

	/*
	 * If we are managing a sync standby, though we weren't prior to this,
	 * then announce we are now a sync standby.
	 */
	if (announce_next_takeover && am_sync)
	{
		announce_next_takeover = false;
		ereport(LOG,
				(errmsg("standby \"%s\" is now a synchronous standby with priority %u",
						application_name, MyWalSnd->sync_standby_priority)));
	}

	/*
	 * If the number of sync standbys is less than requested or we aren't
	 * managing a sync standby then just leave.
	 */
	if (!got_oldest || !am_sync)
	{
		LWLockRelease(SyncRepLock);
		announce_next_takeover = !am_sync;
		return;
	}

	/*
	 * Set the lsn first so that when we wake backends they will release up to
	 * this location.
	 */
	if (walsndctl->lsn[SYNC_REP_WAIT_WRITE] < writePtr)
	{
		walsndctl->lsn[SYNC_REP_WAIT_WRITE] = writePtr;
		numwrite = SyncRepWakeQueue(false, SYNC_REP_WAIT_WRITE);
	}
	if (walsndctl->lsn[SYNC_REP_WAIT_FLUSH] < flushPtr)
	{
		walsndctl->lsn[SYNC_REP_WAIT_FLUSH] = flushPtr;
		numflush = SyncRepWakeQueue(false, SYNC_REP_WAIT_FLUSH);
	}
	if (walsndctl->lsn[SYNC_REP_WAIT_APPLY] < applyPtr)
	{
		walsndctl->lsn[SYNC_REP_WAIT_APPLY] = applyPtr;
		numapply = SyncRepWakeQueue(false, SYNC_REP_WAIT_APPLY);
	}

	LWLockRelease(SyncRepLock);

	elogif(debug_walrepl_syncrep, LOG,
		 "released %d procs up to write %X/%X, %d procs up to flush %X/%X, %d procs up to apply %X/%X",
		 numwrite, (uint32) (writePtr >> 32), (uint32) writePtr,
		 numflush, (uint32) (flushPtr >> 32), (uint32) flushPtr,
		 numapply, (uint32) (applyPtr >> 32), (uint32) applyPtr);
}

/*
 * Calculate the oldest Write, Flush and Apply positions among sync standbys.
 *
 * Return false if the number of sync standbys is less than
 * synchronous_standby_names specifies. Otherwise return true and
 * store the oldest positions into *writePtr, *flushPtr and *applyPtr.
 *
 * On return, *am_sync is set to true if this walsender is connecting to
 * sync standby. Otherwise it's set to false.
 */
static bool
SyncRepGetOldestSyncRecPtr(XLogRecPtr *writePtr, XLogRecPtr *flushPtr,
						   XLogRecPtr *applyPtr, bool *am_sync)
{
	List	   *sync_standbys;
	ListCell   *cell;

	*writePtr = InvalidXLogRecPtr;
	*flushPtr = InvalidXLogRecPtr;
	*applyPtr = InvalidXLogRecPtr;
	*am_sync = false;

	/* Get standbys that are considered as synchronous at this moment */
	sync_standbys = SyncRepGetSyncStandbys(am_sync);

	/*
	 * Quick exit if we are not managing a sync standby or there are not
	 * enough synchronous standbys.
	 */
	if (IS_QUERY_DISPATCHER())
	{
		if (list_length(sync_standbys) == 0)
			return false;
	}
	else if (!(*am_sync) ||
		SyncRepConfig == NULL ||
		list_length(sync_standbys) < SyncRepConfig->num_sync)
	{
		list_free(sync_standbys);
		return false;
	}

	/*
	 * Scan through all sync standbys and calculate the oldest Write, Flush
	 * and Apply positions.
	 */
	foreach(cell, sync_standbys)
	{
		WalSnd	   *walsnd = &WalSndCtl->walsnds[lfirst_int(cell)];
		XLogRecPtr	write;
		XLogRecPtr	flush;
		XLogRecPtr	apply;

		SpinLockAcquire(&walsnd->mutex);
		write = walsnd->write;
		flush = walsnd->flush;
		apply = walsnd->apply;
		SpinLockRelease(&walsnd->mutex);

		if (XLogRecPtrIsInvalid(*writePtr) || *writePtr > write)
			*writePtr = write;
		if (XLogRecPtrIsInvalid(*flushPtr) || *flushPtr > flush)
			*flushPtr = flush;
		if (XLogRecPtrIsInvalid(*applyPtr) || *applyPtr > apply)
			*applyPtr = apply;
	}

	list_free(sync_standbys);
	return true;
}

/*
 * Return the list of sync standbys, or NIL if no sync standby is connected.
 *
 * If there are multiple standbys with the same priority,
 * the first one found is selected preferentially.
 * The caller must hold SyncRepLock.
 *
 * On return, *am_sync is set to true if this walsender is connecting to
 * sync standby. Otherwise it's set to false.
 */
List *
SyncRepGetSyncStandbys(bool *am_sync)
{
	List	   *result = NIL;
	List	   *pending = NIL;
	int			lowest_priority;
	int			next_highest_priority;
	int			this_priority;
	int			priority;
	int			i;
	bool		am_in_pending = false;
	bool		syncStandbyPresent;
	volatile WalSnd *walsnd;	/* Use volatile pointer to prevent code
								 * rearrangement */

	/* Set default result */
	if (am_sync != NULL)
		*am_sync = false;

	if (IS_QUERY_DISPATCHER())
	{
		for (i = 0; i < max_wal_senders; i++)
		{
			walsnd = &WalSndCtl->walsnds[i];
			SpinLockAcquire(&walsnd->mutex);
			syncStandbyPresent = (walsnd->pid != 0)
				&& ((walsnd->state == WALSNDSTATE_STREAMING)
				|| (walsnd->state == WALSNDSTATE_CATCHUP &&
				walsnd->caughtup_within_range));
			SpinLockRelease(&walsnd->mutex);

			if (syncStandbyPresent)
			{
				result = lappend_int(result, i);
				if (am_sync)
					*am_sync = true;
				return result;
			}
		}
	}

	/* Quick exit if sync replication is not requested */
	if (SyncRepConfig == NULL)
		return NIL;

	lowest_priority = SyncRepConfig->nmembers;
	next_highest_priority = lowest_priority + 1;

	/*
	 * Find the sync standbys which have the highest priority (i.e, 1). Also
	 * store all the other potential sync standbys into the pending list, in
	 * order to scan it later and find other sync standbys from it quickly.
	 */
	for (i = 0; i < max_wal_senders; i++)
	{
		walsnd = &WalSndCtl->walsnds[i];

		/* Must be active */
		if (walsnd->pid == 0)
			continue;

		/* Must be streaming */
		if (walsnd->state != WALSNDSTATE_STREAMING)
			continue;

		/* Must be synchronous */
		this_priority = walsnd->sync_standby_priority;
		if (this_priority == 0)
			continue;

		/* Must have a valid flush position */
		if (XLogRecPtrIsInvalid(walsnd->flush))
			continue;

		/*
		 * If the priority is equal to 1, consider this standby as sync and
		 * append it to the result. Otherwise append this standby to the
		 * pending list to check if it's actually sync or not later.
		 */
		if (this_priority == 1)
		{
			result = lappend_int(result, i);
			if (am_sync != NULL && walsnd == MyWalSnd)
				*am_sync = true;
			if (list_length(result) == SyncRepConfig->num_sync)
			{
				list_free(pending);
				return result;	/* Exit if got enough sync standbys */
			}
		}
		else
		{
			pending = lappend_int(pending, i);
			if (am_sync != NULL && walsnd == MyWalSnd)
				am_in_pending = true;

			/*
			 * Track the highest priority among the standbys in the pending
			 * list, in order to use it as the starting priority for later
			 * scan of the list. This is useful to find quickly the sync
			 * standbys from the pending list later because we can skip
			 * unnecessary scans for the unused priorities.
			 */
			if (this_priority < next_highest_priority)
				next_highest_priority = this_priority;
		}
	}

	/*
	 * Consider all pending standbys as sync if the number of them plus
	 * already-found sync ones is lower than the configuration requests.
	 */
	if (list_length(result) + list_length(pending) <= SyncRepConfig->num_sync)
	{
		bool		needfree = (result != NIL && pending != NIL);

		/*
		 * Set *am_sync to true if this walsender is in the pending list
		 * because all pending standbys are considered as sync.
		 */
		if (am_sync != NULL && !(*am_sync))
			*am_sync = am_in_pending;

		result = list_concat(result, pending);
		if (needfree)
			pfree(pending);
		return result;
	}

	/*
	 * Find the sync standbys from the pending list.
	 */
	priority = next_highest_priority;
	while (priority <= lowest_priority)
	{
		ListCell   *cell;
		ListCell   *prev = NULL;
		ListCell   *next;

		next_highest_priority = lowest_priority + 1;

		for (cell = list_head(pending); cell != NULL; cell = next)
		{
			i = lfirst_int(cell);
			walsnd = &WalSndCtl->walsnds[i];

			next = lnext(cell);

			this_priority = walsnd->sync_standby_priority;
			if (this_priority == priority)
			{
				result = lappend_int(result, i);
				if (am_sync != NULL && walsnd == MyWalSnd)
					*am_sync = true;

				/*
				 * We should always exit here after the scan of pending list
				 * starts because we know that the list has enough elements to
				 * reach SyncRepConfig->num_sync.
				 */
				if (list_length(result) == SyncRepConfig->num_sync)
				{
					list_free(pending);
					return result;		/* Exit if got enough sync standbys */
				}

				/*
				 * Remove the entry for this sync standby from the list to
				 * prevent us from looking at the same entry again.
				 */
				pending = list_delete_cell(pending, cell, prev);

				continue;
			}

			if (this_priority < next_highest_priority)
				next_highest_priority = this_priority;

			prev = cell;
		}

		priority = next_highest_priority;
	}

	/* never reached, but keep compiler quiet */
	Assert(false);
	return result;
}

/*
 * Check if we are in the list of sync standbys, and if so, determine
 * priority sequence. Return priority if set, or zero to indicate that
 * we are not a potential sync standby.
 *
 * **Note:- Currently, GPDB does NOT apply the concept of standby priority as
 *   we allow max 1 walsender to be alive at a time. Hence, this function returns
 *   1.**
 */
static int
SyncRepGetStandbyPriority(void)
{
	/* Currently set the priority to 1 */
	return 1;
}

/*
 * Walk the specified queue from head.  Set the state of any backends that
 * need to be woken, remove them from the queue, and then wake them.
 * Pass all = true to wake whole queue; otherwise, just wake up to
 * the walsender's LSN.
 *
 * Must hold SyncRepLock.
 */
int
SyncRepWakeQueue(bool all, int mode)
{
	volatile WalSndCtlData *walsndctl = WalSndCtl;
	PGPROC	   *proc = NULL;
	PGPROC	   *thisproc = NULL;
	int			numprocs = 0;

	Assert(mode >= 0 && mode < NUM_SYNC_REP_WAIT_MODE);
	Assert(SyncRepQueueIsOrderedByLSN(mode));

	proc = (PGPROC *) SHMQueueNext(&(WalSndCtl->SyncRepQueue[mode]),
								   &(WalSndCtl->SyncRepQueue[mode]),
								   offsetof(PGPROC, syncRepLinks));

	while (proc)
	{
		/*
		 * Assume the queue is ordered by LSN
		 */
		if (!all && walsndctl->lsn[mode] < proc->waitLSN)
			return numprocs;

		/*
		 * Move to next proc, so we can delete thisproc from the queue.
		 * thisproc is valid, proc may be NULL after this.
		 */
		thisproc = proc;
		proc = (PGPROC *) SHMQueueNext(&(WalSndCtl->SyncRepQueue[mode]),
									   &(proc->syncRepLinks),
									   offsetof(PGPROC, syncRepLinks));

		/*
		 * Remove thisproc from queue.
		 */
		SHMQueueDelete(&(thisproc->syncRepLinks));

		/*
		 * Set state to complete; see SyncRepWaitForLSN() for discussion of
		 * the various states.
		 */
		thisproc->syncRepState = SYNC_REP_WAIT_COMPLETE;

		/*
		 * Wake only when we have set state and removed from queue.
		 */
		SetLatch(&(thisproc->procLatch));

		elogif(debug_walrepl_syncrep, LOG,
				"syncrep wakeup queue -- %d procid was removed from syncrep queue. "
				"Its state is changed to 'Wait Complete' and "
				"its latch is now set",
				thisproc->pid);

		numprocs++;
	}

	return numprocs;
}

/*
 * The checkpointer calls this as needed to update the shared
 * sync_standbys_defined flag, so that backends don't remain permanently wedged
 * if synchronous_standby_names is unset.  It's safe to check the current value
 * without the lock, because it's only ever updated by one process.  But we
 * must take the lock to change it.
 */
void
SyncRepUpdateSyncStandbysDefined(void)
{
	bool		sync_standbys_defined = SyncStandbysDefined();

	if (sync_standbys_defined != WalSndCtl->sync_standbys_defined)
	{
		LWLockAcquire(SyncRepLock, LW_EXCLUSIVE);

		/*
		 * If synchronous_standby_names has been reset to empty, it's futile
		 * for backends to continue to waiting.  Since the user no longer
		 * wants synchronous replication, we'd better wake them up.
		 */
		if (!sync_standbys_defined)
		{
			int			i;

			for (i = 0; i < NUM_SYNC_REP_WAIT_MODE; i++)
				SyncRepWakeQueue(true, i);
		}

		/*
		 * Only allow people to join the queue when there are synchronous
		 * standbys defined.  Without this interlock, there's a race
		 * condition: we might wake up all the current waiters; then, some
		 * backend that hasn't yet reloaded its config might go to sleep on
		 * the queue (and never wake up).  This prevents that.
		 */
		WalSndCtl->sync_standbys_defined = sync_standbys_defined;

		LWLockRelease(SyncRepLock);
	}
}

#ifdef USE_ASSERT_CHECKING
static bool
SyncRepQueueIsOrderedByLSN(int mode)
{
	PGPROC	   *proc = NULL;
	XLogRecPtr	lastLSN;

	Assert(mode >= 0 && mode < NUM_SYNC_REP_WAIT_MODE);

	lastLSN = 0;

	proc = (PGPROC *) SHMQueueNext(&(WalSndCtl->SyncRepQueue[mode]),
								   &(WalSndCtl->SyncRepQueue[mode]),
								   offsetof(PGPROC, syncRepLinks));

	while (proc)
	{
		/*
		 * Check the queue is ordered by LSN.
		 *
		 * In upstream this check also validates that multiple procs don't
		 * have matching LSNs. This restriction is lifted in GPDB as for
		 * commit-prepared retry case since we don't know the exact lsn of
		 * commit-prepared record, need to wait for latest flush point
		 * lsn. So, its possible due to concurrency multiple backends register
		 * in queue with same lsn value. The check here anyways seems little
		 * restrictive as actual queue usage only needs it in sorted order and
		 * not really relies on having unique entries. It just happens to be
		 * that if all usage of SyncRepWaitForLSN() feed unique lsn value
		 * upstream and in GPDB except from FinishPreparedTransaction(), but
		 * not required for correct functioning of the code.
		 */
		if (proc->waitLSN < lastLSN)
			return false;

		lastLSN = proc->waitLSN;

		proc = (PGPROC *) SHMQueueNext(&(WalSndCtl->SyncRepQueue[mode]),
									   &(proc->syncRepLinks),
									   offsetof(PGPROC, syncRepLinks));
	}

	return true;
}
#endif

/*
 * ===========================================================
 * Synchronous Replication functions executed by any process
 * ===========================================================
 */

bool
check_synchronous_standby_names(char **newval, void **extra, GucSource source)
{
	if (*newval != NULL && (*newval)[0] != '\0')
	{
		int			parse_rc;
		SyncRepConfigData *pconf;

		/* Reset communication variables to ensure a fresh start */
		syncrep_parse_result = NULL;
		syncrep_parse_error_msg = NULL;

		/* Parse the synchronous_standby_names string */
		syncrep_scanner_init(*newval);
		parse_rc = syncrep_yyparse();
		syncrep_scanner_finish();

		if (parse_rc != 0 || syncrep_parse_result == NULL)
		{
			GUC_check_errcode(ERRCODE_SYNTAX_ERROR);
			if (syncrep_parse_error_msg)
				GUC_check_errdetail("%s", syncrep_parse_error_msg);
			else
				GUC_check_errdetail("synchronous_standby_names parser failed");
			return false;
		}

		/* GUC extra value must be malloc'd, not palloc'd */
		pconf = (SyncRepConfigData *)
			malloc(syncrep_parse_result->config_size);
		if (pconf == NULL)
			return false;
		memcpy(pconf, syncrep_parse_result, syncrep_parse_result->config_size);

		*extra = (void *) pconf;

		/*
		 * We need not explicitly clean up syncrep_parse_result.  It, and any
		 * other cruft generated during parsing, will be freed when the
		 * current memory context is deleted.  (This code is generally run in
		 * a short-lived context used for config file processing, so that will
		 * not be very long.)
		 */
	}
	else
		*extra = NULL;

	return true;
}

void
assign_synchronous_standby_names(const char *newval, void *extra)
{
	SyncRepConfig = (SyncRepConfigData *) extra;
}

void
assign_synchronous_commit(int newval, void *extra)
{
	switch (newval)
	{
		case SYNCHRONOUS_COMMIT_REMOTE_WRITE:
			SyncRepWaitMode = SYNC_REP_WAIT_WRITE;
			break;
		case SYNCHRONOUS_COMMIT_REMOTE_FLUSH:
			SyncRepWaitMode = SYNC_REP_WAIT_FLUSH;
			break;
		case SYNCHRONOUS_COMMIT_REMOTE_APPLY:
			SyncRepWaitMode = SYNC_REP_WAIT_APPLY;
			break;
		default:
			SyncRepWaitMode = SYNC_REP_NO_WAIT;
			break;
	}
}


text:
title: "Part 1 - Project Setup"
date: 2019-12-23T18:44:50+01:00
desc: In this tutorial series, we are going to be building an Imgur clone using Lambda functions written using Node.js and a frontend built using Vue.js
author: Elliot Forbes
weight: 1
twitter: https://twitter.com/elliot_f
series: vuejsimgurclone
image: vuejs.png
tags:
- beginner
authorImage: https://images.tutorialedge.net/authors/profile.jpeg
---

In the first part of this tutorial series, we are going to be working on project setup for both the frontend and the backend of the Imgur image-hosting clone that we are going to be building.

By the end of this section, you will have two separate projects that built using the `vue` cli for our frontend Vue.js application and the `npm` cli for our backend Node.js REST API that will be powering our frontend.

# Introduction

This section of the course is going to show you the standard approach that is taken to kick-start new Node.js and Vue.js applications.

We'll be creating two distinct folder structures to house both applications but these will both live alongside each other in a single Github repository. The reason we'll be using a simple repository for this is to minimize complexity, this course is more focused on teaching you how to build web applications with Vue.js and Node.js as opposed to teaching you how to use git.

# Prerequisites

In order to complete this section of the course, you will need the following tools installed on your machine:

* `node` - This can be installed by following the instructions on the official [Node.js Downloads](https://nodejs.org/en/download/) page.
* `npm` - The `npm` cli typically comes bundled within the Node.js download.
* `git` - You will require a github.com account as well as the `git` cli installed on your local machine.

There will be more dependencies required throughout the course, but as and when they come up, I'll give you the commands necessary to install them on your local development machine.

For now, this is all we need to get started!

# Scaffolding our Frontend Application

The first thing we will want to create is the base for our frontend application. In order to install the cli on your local machine, you can run the following `npm install` command:

```output
$ npm install -g @vue/cli
$ vue --version
3.4.0
```

With this installed, we can then navigate into the root of our project's directory and run the following command:

```output
$ vue create frontend
```

Select the `default` presets when prompted and the `vue` cli will at that point start downloading all of the dependencies the base-project requires in order to run and build locally.

With that complete, navigate into the `frontend/` directory and start your new Vue.js application by calling `npm run serve`.

```output
$ cd frontend
$ npm run serve
```

This will start a live development server for our application which will automatically rebuild whenever a new change is made to any of our application's source code. This also exposes our application so that we can view it within a local browser.

Copy the `Local:` url that is displayed in the terminal into a new tab within a browser and you will see your application up and running!

# Scaffolding our Backend Application

Now that we have successfully managed to create the base for our frontend application, we can start looking at how we scaffold our backend REST API.

We'll start off by creating a new directory called `backend/` within the root of our project's directory and navigating into that directory using the `cd` command.

```output
$ mkdir -p backend
$ cd backend
```

Within this newly created directory, we will want to run `npm init` to initialize our new Node.js application.

Upon running `npm init` you will be prompted for information such as the `package name` and the `entry point` for your application, keep these all as their default values for now by simply pressing enter for each one. If we need to go back and change these later, we can do so by modifying the `package.json` file that is generated by this command.

With this complete, we can now create the base of our `express.js` based REST API.

Create a new `index.js` file within the `backend/` directory and add the following:

 backend/index.js

```js
const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => res.send('Hello World!'))

app.listen(port, () => console.log(`REST API Started on port ${port}`))
```

Now, in order for this to work, you will have to first install the `express.js` node package that we import on the first line of our file.

```output
$ npm install --save express
```

This will automatically append this dependency to a list of `dependencies` within the `package.json` that was generated for us earlier.

With this successfully installed, we can attempt to run our backend application by calling `node index.js`:

 $ node index.js

```output
REST API Started on port 3000
```

We can then open up `http://localhost:3000` within our browser and with any luck we should see our new backend application successfully returning `Hello World` to us!

# Initializing our Git Repository

Awesome, with this done, we can start looking at setting up the `github` repository we'll be using for this tutorial series.

> **Note** - For those that have never used `github` before, it is essentially a place for us to store our application source code as well as keep track of any changes we make to our application. It should be noted that `git` and `github` are not the same thing, the first is a tool that is compatible with any git-based source control system and the second an application built on top of `git`.

First, you will need to log in to your github.com account and create a new repository.

![Creating new Github Repo](https://images.tutorialedge.net/images/imgur-clone/create-repo.png)

This will instantly create a free repository for us in which we can store all of our applications source code. It is incredibly important to note that any and all files you push up to a **public** repository can be viewed by **anyone**, so please do not push up anything you do not wish the rest of the world to see like API credentials or usernames + passwords.

![Created Repo](https://images.tutorialedge.net/images/imgur-clone/empty-repo.png)

With this repository created, we can then initialize our local project and `add` then `commit` and finally `push` all of our source code up to this repository like so from the root of our project:

```output
$ git init
$ git remote add origin https://github.com/elliotforbes/imgur-clone-vuejs-nodejs.git
```

This may prompt your to enter your `username/password` for github if you haven't already authenticated.

Before we push up our application source code, we will want to create a `.gitignore` file within the root of our project's directory which will contain a list of any files + directories we do not wish to push up to github.

 .gitignore

```output
backend/node_modules/

frontend/node_modules
```

We will then want to run `git add .gitignore` so that `git` can see these new rules for ignoring files and will subsequently not push up the `node_modules/` to github.

The `vue` cli automatically creates a `.git` directory within the root of our `frontend/` directory, this confuses `git` and in order to clear up this confusion and keep things simple we will have to delete this `.git/` directory like so:

```output
$ cd frontend
$ rm -rf .git
```

And finally, we will be able to perform a `push` and push up all of the code within both directories like so:

```output
$ git add -A
$ git commit -m "Initial Commit"
$ git push origin master
```

Once these commands have been successfully performed, you will now see all within your repository both the `frontend/` and `backend/` directories as well as all of the code within those directories on github.

![Populated Repository](https://images.tutorialedge.net/images/imgur-clone/repo-after-push.png)

# Conclusion

Awesome, in this part of the series, we have successfully built the base for both our frontend Vue.js application as well as that of our Node.js REST API that will power our application and handle things such as authentication and file-uploading.

We have also looked at how we can set up a github.com repository within which we can store our application's source code. It's a great habit to use source control for everything you do, even if it happens to be a simple test project as it gives you more experience working with the `git` cli which is the current industry standard tool for source control.

## Next Tutorial:

In the next part of this tutorial series, we will be exploring the world of Vue.js and modifying our existing frontend application by creating new simple components.

* [Part 2 - Creating Simple Vue.js Components](/projects/building-imgur-clone-vuejs-nodejs/part-2-simple-components/)