Argh, getting hang of golang and kubernetes. Initial handshake done.
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c61a5e8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.pb.go
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..698a47b
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,31 @@
+FROM ubuntu:latest
+
+RUN apt-get update --fix-missing
+RUN apt-get -y upgrade
+RUN apt-get -y install wget git bash unzip
+
+WORKDIR /tmp
+RUN wget https://dl.google.com/go/go1.14.linux-amd64.tar.gz
+RUN tar -xvf go1.14.linux-amd64.tar.gz
+RUN mv go /usr/local
+RUN rm go1.14.linux-amd64.tar.gz
+
+ENV GOROOT=/usr/local/go
+ENV GOPATH=/src/go
+ENV GOBIN=$GOPATH/bin
+ENV PATH=$GOBIN:$GOROOT/bin:$PATH
+
+RUN go version
+
+RUN go get -u google.golang.org/grpc
+
+WORKDIR /src/protoc
+RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v3.11.4/protoc-3.11.4-linux-x86_64.zip
+RUN unzip protoc-3.11.4-linux-x86_64.zip
+RUN rm protoc-3.11.4-linux-x86_64.zip
+ENV PATH=/src/protoc/bin:$PATH
+
+RUN go get -u github.com/golang/protobuf/protoc-gen-go
+RUN go get -u google.golang.org/protobuf/encoding/prototext
+
+WORKDIR /src/go/src/pcloud
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ab369b6
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# PCloud
diff --git a/chunk/chunk.go b/chunk/chunk.go
new file mode 100644
index 0000000..1dde057
--- /dev/null
+++ b/chunk/chunk.go
@@ -0,0 +1,76 @@
+package main
+
+import "context"
+import "flag"
+import "fmt"
+import "log"
+import "net"
+import "time"
+
+import "google.golang.org/grpc"
+
+import pc "pcloud"
+
+var masterAddress string
+var selfAddress string
+
+func init() {
+	flag.StringVar(&masterAddress, "master", "localhost:123", "Metadata storage address.")
+	flag.StringVar(&selfAddress, "self", "", "Metadata storage address.")
+}
+
+type chunkStorage struct {
+}
+
+func (s *chunkStorage) ListChunks(
+	ctx context.Context,
+	request *pc.ListChunksRequest) (*pc.ListChunksResponse, error) {
+	return nil, nil
+}
+
+func (s *chunkStorage) ReadChunk(
+	ctx context.Context,
+	request *pc.ReadChunkRequest) (*pc.ReadChunkResponse, error) {
+	return nil, nil
+}
+
+func (s *chunkStorage) StoreChunk(
+	ctx context.Context,
+	request *pc.StoreChunkRequest) (*pc.StoreChunkResponse, error) {
+	return nil, nil
+}
+
+func main() {
+	flag.Parse()
+	log.Print("Chunk server starting")
+
+	// Create Master server client.
+	var opts []grpc.DialOption
+	opts = append(opts, grpc.WithInsecure())
+	opts = append(opts, grpc.WithBlock())
+	conn, err := grpc.Dial(masterAddress, opts...)
+	if err != nil {
+		log.Fatalf("Failed to dial %s: %v", masterAddress, err)
+	}
+	defer conn.Close()
+	client := pc.NewMetadataStorageClient(conn)
+
+	// Register current Chunk server with Master.
+	ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
+	_, err = client.AddChunkServer(
+		ctx,
+		&pc.AddChunkServerRequest{Address: selfAddress})
+	if err != nil {
+		log.Fatalf("failed to register chunk server: %v", err)
+	}
+	log.Print("Registered myself")
+
+	// Start RPC server
+	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", 234))
+	if err != nil {
+		log.Fatalf("failed to listen: %v", err)
+	}
+	server := grpc.NewServer()
+	pc.RegisterChunkStorageServer(server, &chunkStorage{})
+	server.Serve(lis)
+}
diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml
new file mode 100644
index 0000000..449013e
--- /dev/null
+++ b/k8s/deployment.yaml
@@ -0,0 +1,78 @@
+---
+kind: Service 
+apiVersion: v1
+metadata:
+  name: pcloud-master-service
+spec:
+  type: ClusterIP
+  selector:
+    app: pcloud-master 
+  ports:
+    - nodePort: 
+      port: 111
+      targetPort: 123
+---
+kind: Deployment
+apiVersion: apps/v1
+metadata:
+  name: pcloud-master
+spec:
+  selector:
+    matchLabels:
+      app: pcloud-master
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: pcloud-master
+    spec:
+      containers:
+      - name: pcloud-master
+        image: pcloud:latest
+        imagePullPolicy: Never
+        ports:
+        - containerPort: 123
+        volumeMounts:
+        - name: code
+          mountPath: /src/go/src/pcloud
+        command: ["/bin/sh"]
+        args: ["-c", "protoc pfs.proto --go_out=plugins=grpc:. && cd master && go install master.go && master --port=123"]
+      volumes:
+      - name: code
+        hostPath:
+          path: "/Users/lekva/dev/src/pcloud"
+---
+kind: Deployment
+apiVersion: apps/v1
+metadata:
+  name: pcloud-chunk
+spec:
+  selector:
+    matchLabels:
+      app: pcloud-chunk
+  replicas: 3
+  template:
+    metadata:
+      labels:
+        app: pcloud-chunk
+    spec:
+      containers:
+      - name: pcloud-chunk
+        image: pcloud:latest
+        imagePullPolicy: Never
+        ports:
+        - containerPort: 234
+        env:
+        - name: SELF_IP
+          valueFrom:
+            fieldRef:
+              fieldPath: status.podIP
+        volumeMounts:
+        - name: code
+          mountPath: /src/go/src/pcloud
+        command: ["/bin/sh"]
+        args: ["-c", "protoc pfs.proto --go_out=plugins=grpc:. && cd chunk && go install chunk.go && chunk --master=pcloud-master-service:111 --self=$(SELF_IP):234"]
+      volumes:
+      - name: code
+        hostPath:
+          path: "/Users/lekva/dev/src/pcloud"
diff --git a/master/core b/master/core
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/master/core
diff --git a/master/master.go b/master/master.go
new file mode 100644
index 0000000..73ac143
--- /dev/null
+++ b/master/master.go
@@ -0,0 +1,60 @@
+package main
+
+import "context"
+import "flag"
+import "fmt"
+import "log"
+import "net"
+
+import "google.golang.org/grpc"
+
+import pc "pcloud"
+
+var port int
+
+func init() {
+	flag.IntVar(&port, "port", 123, "Port to listen on.")
+}
+
+type chunkServer struct {
+	address string
+}
+
+type metadataStorage struct {
+	chunkServer []string
+}
+
+func (s *metadataStorage) AddChunkServer(
+	ctx context.Context,
+	request *pc.AddChunkServerRequest) (*pc.AddChunkServerResponse, error) {
+	s.chunkServer = append(s.chunkServer, request.GetAddress())
+	log.Printf("Registered Chunk server: %s", request.GetAddress())
+	return &pc.AddChunkServerResponse{}, nil
+}
+
+func (s *metadataStorage) CreateBlob(
+	ctx context.Context,
+	request *pc.CreateBlobRequest) (*pc.CreateBlobResponse, error) {
+	return nil, nil
+}
+
+func (s *metadataStorage) GetBlobMetadata(
+	ctx context.Context,
+	request *pc.GetBlobMetadataRequest) (*pc.GetBlobMetadataResponse, error) {
+	return nil, nil
+}
+
+func main() {
+	flag.Parse()
+	log.Print("Master server starting")
+
+	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
+	if err != nil {
+		log.Fatalf("Failed to listen on port %d: %v", port, err)
+	}
+	log.Printf("Listening on port: %d", port)
+	server := grpc.NewServer()
+	pc.RegisterMetadataStorageServer(server, &metadataStorage{})
+	log.Print("Master serving")
+	server.Serve(lis)
+}
diff --git a/pfs.proto b/pfs.proto
new file mode 100644
index 0000000..4c338f6
--- /dev/null
+++ b/pfs.proto
@@ -0,0 +1,88 @@
+syntax = "proto3";
+
+package pcloud;
+
+message Chunk {
+	string chunk_id = 1;
+	int32 size_bytes = 2;
+	bytes data = 3;
+}
+
+// ChunkStorage
+
+service ChunkStorage {
+	rpc ListChunks(ListChunksRequest) returns (ListChunksResponse) {}
+
+	rpc ReadChunk(ReadChunkRequest) returns (ReadChunkResponse) {}
+
+	rpc StoreChunk(StoreChunkRequest) returns (StoreChunkResponse) {}
+}
+
+message ListChunksRequest {
+}
+
+message ListChunksResponse {
+	repeated string chunk_id = 1;
+}
+
+message ReadChunkRequest {
+	string chunk_id = 1;
+	int32 offset = 2;
+	int32 num_bytes = 3;
+}
+
+message ReadChunkResponse {
+	bytes data = 1;
+}
+
+message StoreChunkRequest {
+	string chunk_id = 1;
+	bytes data = 2;
+}
+
+message StoreChunkResponse {
+}
+
+// MetadataStorage
+
+message ChunkStorageMetadata {
+	string chunk_id = 1;
+	int32 size_bytes = 2;
+	repeated string server = 3;
+}
+
+service MetadataStorage {
+	rpc AddChunkServer(AddChunkServerRequest) returns (AddChunkServerResponse) {}
+	
+	rpc CreateBlob(CreateBlobRequest) returns (CreateBlobResponse) {}
+
+	rpc GetBlobMetadata(GetBlobMetadataRequest) returns (GetBlobMetadataResponse) {}
+}
+
+message AddChunkServerRequest {
+	string address = 1;
+}
+
+message AddChunkServerResponse {
+}
+
+message CreateBlobRequest {
+	int32 size_bytes = 1;
+	int32 chunk_size_bytes = 2;
+	int32 num_replicas = 3;
+}
+
+message CreateBlobResponse {
+	string blob_id = 1;
+	repeated ChunkStorageMetadata chunk = 2;
+}
+
+message GetBlobMetadataRequest {
+	string blob_id = 1;
+}
+
+message GetBlobMetadataResponse {
+	string blob_id = 1;
+	int32 size_bytes = 2;
+	repeated ChunkStorageMetadata chunk = 3;
+}
\ No newline at end of file