Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
d0d211b
Initial commit
SiarheiKresik Dec 3, 2018
ea4772a
Implement client
SiarheiKresik Dec 15, 2018
69a5d82
Add dockerfile
SiarheiKresik Dec 17, 2018
80a3267
Add gitignore file
SiarheiKresik Dec 17, 2018
cc457c7
Add makefile
SiarheiKresik Dec 17, 2018
5af629d
Update dockerfile
SiarheiKresik Dec 17, 2018
1f02fee
Update makefile
SiarheiKresik Dec 17, 2018
0921290
Update dockerfile
SiarheiKresik Dec 17, 2018
e7d2129
Update makefile
SiarheiKresik Dec 17, 2018
4ba8093
Update makefile
SiarheiKresik Dec 17, 2018
f4d8890
Add code checks into makefile
SiarheiKresik Dec 17, 2018
e3989f8
Update makefile
SiarheiKresik Dec 17, 2018
b748ad8
Refactor code checks in the makefile
SiarheiKresik Dec 18, 2018
1b60b6a
Set a image in the run-dev target to the dev-image
SiarheiKresik Dec 18, 2018
b9d512d
Add test targets into the makefile
SiarheiKresik Dec 18, 2018
efc01a1
Reorder check order in the makefile
SiarheiKresik Dec 18, 2018
ed3372d
Add coverage.out into gitignore
SiarheiKresik Dec 18, 2018
6f17329
Add test coverage target into the makefile
SiarheiKresik Dec 18, 2018
5454fd0
Add clean target into the makefile
SiarheiKresik Dec 18, 2018
b8703d6
Refactor the makefile
SiarheiKresik Dec 18, 2018
b8500aa
Implement client
SiarheiKresik Dec 18, 2018
3c88d85
Implement database
SiarheiKresik Dec 18, 2018
71ac094
Improve client log messages
SiarheiKresik Dec 19, 2018
73498de
Update gitignore
SiarheiKresik Dec 19, 2018
7c7f346
Implement database
SiarheiKresik Dec 19, 2018
2bb639d
Implement server
SiarheiKresik Dec 19, 2018
3c4868c
Refactor client
SiarheiKresik Dec 19, 2018
d253adc
Fix coverage target of the makefile
SiarheiKresik Dec 19, 2018
f6e3543
Add .editorconfig
SiarheiKresik Dec 19, 2018
da3d544
Refactor KEY command to filter keys by glob pattern
SiarheiKresik Dec 20, 2018
7269f88
Update code comments
SiarheiKresik Dec 20, 2018
06b2faf
Fix typo
SiarheiKresik Dec 20, 2018
0cc3bea
Add comments
SiarheiKresik Dec 20, 2018
e8dcb3a
Remove unnecessary log print
SiarheiKresik Dec 20, 2018
582fa27
Implement verbose server mode and client ids
SiarheiKresik Dec 20, 2018
636527c
Make logs formatting more consistent
SiarheiKresik Dec 20, 2018
7a41945
Now server keep on running after accept error
SiarheiKresik Dec 20, 2018
b381169
Remove unneccesery code
SiarheiKresik Dec 20, 2018
7fda69c
Make string formatting more consistent
SiarheiKresik Dec 20, 2018
24d9aac
Update readme file
SiarheiKresik Dec 20, 2018
1fe248e
Fix type in the readme file
SiarheiKresik Dec 20, 2018
95182c0
Add comments
SiarheiKresik Dec 20, 2018
f489399
Fix typo
SiarheiKresik Dec 20, 2018
771af49
Correct log message
SiarheiKresik Dec 20, 2018
c82f95c
Refactor database tests
SiarheiKresik Dec 20, 2018
203cf21
Remove client flags for non implemented features
SiarheiKresik Dec 20, 2018
a32d74b
Update readme file
SiarheiKresik Dec 20, 2018
6eb8112
Update readme file
SiarheiKresik Dec 20, 2018
d577ce3
Remove wrong setting from the editorconfig
SiarheiKresik Dec 20, 2018
6b39598
Add comments
SiarheiKresik Dec 20, 2018
e186d85
Remove trailing whitespaces to pass goimports check
SiarheiKresik Dec 20, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
root = true

[*]
indent_size = 4
16 changes: 4 additions & 12 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, build with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out
.vscode/go-kvdb.code-workspace
bin/
coverage.out
go-kvdb.db*
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

ARG GO_VERSION=1.11.1
FROM golang:${GO_VERSION}-alpine AS dev
RUN apk add git
RUN go get golang.org/x/lint/golint
RUN go get golang.org/x/tools/cmd/goimports

FROM alpine AS prod
WORKDIR /app/
COPY ./bin/client .
COPY ./bin/server .
EXPOSE 9090
ENTRYPOINT ["/app/server"]
108 changes: 108 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
.DEFAULT_GOAL := build

DEV := dev
PROD := prod
TAG := latest

BIN := bin
SRC := src

APP_BASE_NAME := go-kvdb
APP_CLIENT_NAME := client
APP_SERVER_NAME := server

REPOSITORY_PATH := github.com/SiarheiKresik

DEV_GOPATH := /go
DEV_GOPATH_BIN := $(DEV_GOPATH)/$(BIN)
DEV_GOPATH_SRC := $(DEV_GOPATH)/$(SRC)
DEV_WORKDIR := $(DEV_GOPATH_SRC)/$(REPOSITORY_PATH)/$(APP_BASE_NAME)

SRC_MOUNT := "$(PWD):$(DEV_GOPATH_SRC)/$(REPOSITORY_PATH)/$(APP_BASE_NAME)"
BIN_MOUNT := "$(PWD)/$(BIN):$(DEV_GOPATH_BIN)"

DEV_IMAGE := $(APP_BASE_NAME)-${DEV}
PROD_IMAGE := $(APP_BASE_NAME)

BUILDER := docker build
RUNNER := docker run --rm
CHECKER := $(RUNNER) -v $(SRC_MOUNT) -w $(DEV_WORKDIR) $(DEV_IMAGE)

COVERAGE := coverage.out

SEARCH_GOFILES = find -not -path '*/vendor/*' -type f -name "*.go"

DELIMITER="----------------------"
define print_target_name
@echo $(DELIMITER)
@echo $(1)
@echo $(DELIMITER)
endef

### targets ###

build: build-dev-image build-dev build-prod-image clean

build-dev-image:
$(call print_target_name, "Building an image with go tools for development...")
$(BUILDER) -t $(DEV_IMAGE) --target $(DEV) .

build-dev:
$(call print_target_name, "Compile binaries...")
@mkdir $(BIN)
$(RUNNER) -v $(BIN_MOUNT) -v $(SRC_MOUNT) -w $(DEV_WORKDIR) $(DEV_IMAGE) sh -c "go install ./..."

build-prod-image:
$(call print_target_name, "Building an image with server and client binaries")
$(BUILDER) -t $(PROD_IMAGE) --target $(PROD) .

test: test-ut coverage

test-ut:
$(call print_target_name, "Run unit tests...")
@$(CHECKER) sh -c 'CGO_ENABLED="0" go test ./...'

coverage:
$(call print_target_name, "Measure test coverage...")
@$(CHECKER) sh -c '\
CGO_ENABLED="0" go test ./... -coverprofile=/dev/null | tee $(COVERAGE) \
&& chmod 666 $(COVERAGE)'

check: build-dev-image check-goimports check-golint check-govet

check-govet:
$(call print_target_name, "Checks (go vet)...")
@$(CHECKER) sh -c 'go tool vet -v . | grep -v "Checking file"'

check-goimports:
$(call print_target_name, "Checks (goimports)...")
@$(CHECKER) sh -c 'test -z "`goimports -e -d .`"'

check-golint:
$(call print_target_name, "Checks (golint)...")
@$(CHECKER) sh -c 'test -z "`golint ./...`"'

check-golint-verbose:
$(call print_target_name, "Checks (golint)...")
@$(CHECKER) sh -c 'golint ./...'

run: run-server

run-server:
$(call print_target_name, "Run server...")
$(RUNNER) -p 9090:9090 $(APP_BASE_NAME):$(TAG) $(ARGS)

run-client:
$(call print_target_name, "Run client...")
$(RUNNER) -it --entrypoint /app/client $(APP_BASE_NAME):$(TAG) $(ARGS)

run-dev:
$(RUNNER) -it -v $(SRC_MOUNT) -w $(DEV_WORKDIR) $(DEV_IMAGE) sh

clean:
$(call print_target_name, "Cleaning...")
@rm -rf ./$(BIN)/
@rm -f $(COVERAGE) \

prune:
docker image prune
78 changes: 78 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# go-kvdb

Go implementation of the server-client solution for storing key-value data.

## Requirements

- [make](https://www.gnu.org/software/make/)
- [docker](https://www.docker.com/)
Note: in order to run docker commands in the makefile targets without sudo you have to follow the next [guide](https://docs.docker.com/install/linux/linux-postinstall/).

## Getting source files

If you have `go` tool installed:

- get source files: `go get github.com/SiarheiKresik/go-kvdb`
- `cd $GOPATH/src/github.com/SiarheiKresik/go-kvdb`

Or get source files by cloning repository directly:

- get source files: `git clone https://github.com/SiarheiKresik/go-kvdb.git`
- `cd go-kvdb`

## Building

Run `make`. This builds the `go-kvdb` docker container with the server and client binaries.

## Running

Run server in a docker container:

- `make run [ARGS="<args>"]` or
- `docker run --rm -p 9090:9090 go-kvdb:latest [ARG...]`

Run client in a docker container:

- `make run-client [ARGS="<args>"]` or
- `docker run --rm -it --entrypoint /app/client go-kvdb:latest [ARG...]`

## Other make targets

- `make check`—runs code linting and verification
- `make test`—runs tests for the project files
- `make coverage`—measures test coverage, saves result to the `coverage.out` file

## Server

Starting options:

- `-m`, `--mode`
The possible storage option (_disk_—stores database to disk, _memory_—database runs only in memory)
- `-p`, `--port`
The port for listening on (default—_9090_)
- `-v`, `--verbose`
Verbose mode, full log of the client requests

## Client

Starting options:

- `-p`, `--port`
The port to connect to the server (default—_9090_)
- `-h`, `--host`
The host to connect to the server (default—_127.0.0.1_)

## Supported commands

- **SET** - updates a key at a time with the given value
- **GET** - returns the value of a key
- **DEL** - removes a key
- **KEYS** - returns all keys matching pattern; pattern could include '\*' symbol which matches zero or
more characters
- **DUMP** - (optional) - returns database data in the json format

## References

1. https://systembash.com/a-simple-go-tcp-server-and-tcp-client/
2. https://www.oreilly.com/ideas/building-messaging-in-go-network-clients
3. https://medium.com/@kpbird/golang-serialize-struct-using-gob-part-1-e927a6547c00
39 changes: 39 additions & 0 deletions client/args.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package main

import (
"flag"
)

// Config represents a set of parsed flags for client settings.
type Config struct {
port int
host string
dump bool
restore bool
}

var config Config

func init() {
const (
defaultPort = 9090
portDesc = "The port to connect to the server"

defaultHost = "127.0.0.1"
hostDesc = "The host to connect to the server"

defaultDump = false
dumpDesc = "Dump the whole database to the JSON format on STDOUT"

defaultRestore = false
restoreDesc = "Restore the database from the dumped file"
)

shorhand := " (shorthand)"

flag.IntVar(&config.port, "port", defaultPort, portDesc)
flag.IntVar(&config.port, "p", defaultPort, portDesc+shorhand)

flag.StringVar(&config.host, "host", defaultHost, hostDesc)
flag.StringVar(&config.host, "h", defaultHost, hostDesc+shorhand)
}
34 changes: 34 additions & 0 deletions client/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package main

import (
"bufio"
"fmt"
"os"
)

// Cli represents command line interface of a client.
type Cli struct {
r *bufio.Reader
}

// NewCli returns an initialized Cli struct.
func NewCli() *Cli {
return &Cli{
r: bufio.NewReader(os.Stdin),
}
}

// Write writes text to stdout.
func (c *Cli) Write(text string) {
fmt.Print(text)
}

// Read returns user's input text.
func (c *Cli) Read() (string, error) {
return c.r.ReadString('\n')
}

// Prompt formats text and writes it to stdout.
func (c *Cli) Prompt(text string) {
fmt.Printf("%s > ", text)
}
44 changes: 44 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package main

import (
"bufio"
"fmt"
"net"
)

// Client connects and communicates with a server.
type Client struct {
conn net.Conn
w *bufio.Writer
ch chan string
}

// NewClient initializes a new client.
func NewClient() *Client {
return &Client{}
}

// Connect connects the client to a server on the specified address.
func (c *Client) Connect(addr string) (net.Addr, error) {
conn, err := net.Dial(protocol, addr)
if err != nil {
return nil, err
}
c.conn = conn
c.w = bufio.NewWriter(conn)
return c.conn.RemoteAddr(), nil
}

// Close closes connection with a server.
func (c *Client) Close() {
c.conn.Close()
}

// Send sends a message to a server.
func (c *Client) Send(msg string) error {
_, err := fmt.Fprintf(c.conn, msg)
if err != nil {
return err
}
return nil
}
Loading