Thursday, August 13, 2015

Golang. REST Microservices in Go with Gin. Gorm.

10 Jul 2014
http://txt.fliglio.com/2014/07/restful-microservices-in-go-with-gin/
Microservices are cool. Simply described, they're a way to take encapsulation to the next level. This design pattern allows for components of your system to be developed in isolation (even in different languages), keep internal business logic truly internal (no more well intentioned hacks that break encapsulation), and allow for each component to be deployed in isolation. These three characteristics go a long way toward making development and deployment easier.
Here's a walk-through of how I designed a simple Todo microservice in Go (with some help from GinGorm, andcodegangsta/cli).
n.b. I'll be walking through the process of building the microservice, but you can get the finished project on github.

Getting Started

First step is to wire up a foundation. For starters, we'll need:
  • server cli: This is what we'll run to start up our server.
  • service controller: This is where we'll manage our service; wiring up routes and injecting dependencies etc.
  • todo api model: A data model shared by the server and the client (and the database) to communicate with.
  • todo resource: A grouping of handlers to manage api requests made regarding todos.
  • todo http client: An http client library that can be imported by any applications wishing to use our microservice.
  • integration tests: By leveraging the client, we can very easily write symmetrical integration tests that fully exercise our service's REST api.

Server CLI

This is the entry point to our app. Right now it's pretty simple because configuration etc. is hard coded in the Service Controller. Eventually it will be in charge of parsing a config and running different aspects of our application (e.g. performing database migrations)
func main() {

    // not much here; it'll grow as we externalize config and add options
    svc := service.TodoService{}
    svc.Run()
}

Service Controller

This is the front controller for our service. We can construct our dependencies to inject into various resources and wire up the routes to different resource functions.
type TodoService struct {
}

func (s *TodoService) Run() {
    // we'll pass in configuration later
    connectionString := "user:pass@tcp(localhost:3306)/Todo?charset=utf8&parseTime=True"

    db, _ := gorm.Open("mysql", connectionString)

    // initialize the resource and inject our db connection
    todoResource := &TodoResource{db: db}

    r := gin.Default()

    // to start out, we'll build the ability to add, get, and delete a todo
    r.POST("/todo", todoResource.CreateTodo)
    r.GET("/todo/:id", todoResource.GetTodo)
    r.DELETE("/todo/:id", todoResource.DeleteTodo)

    // we'll pass in configuration later
    r.Run(":8080")
}

Todo API Model

This structure can be leveraged by both the service, to decode requests and integrate with the database, and by the client, to build and process requests of the service. Later we will put it in its own package so a client implementation can import "api" and "client" and the server only needs "api" and "service"
type Todo struct {
    Id          int32  `json:"id"`
    Created     int32  `json:"created"`
    Status      string `json:"status"`
    Title       string `json:"title"`
    Description string `json:"description"`
}

Todo Resource

This is a very rudimentary first pass at the resource. There is little error handling and there are obvious omissions (like the ability to update a todo), but it illustrates how to group the todo resource's controller functionality and abstract it from the details of bootstrapping the app.
type TodoResource struct {
    db gorm.DB
}

func (tr *TodoResource) CreateTodo(c *gin.Context) {
    var todo api.Todo

    c.Bind(&todo)
    todo.Status = api.TodoStatus
    todo.Created = int32(time.Now().Unix())

    tr.db.Save(&todo)
    c.JSON(201, todo)
}

func (tr *TodoResource) GetTodo(c *gin.Context) {
    idStr := c.Params.ByName("id")
    idInt, _ := strconv.Atoi(idStr)
    id := int32(idInt)

    var todo api.Todo

    if tr.db.First(&todo, id).RecordNotFound() {
        c.JSON(404, gin.H{"error": "not found"})
    } else {
        c.JSON(200, todo)
    }
}

func (tr *TodoResource) DeleteTodo(c *gin.Context) {
    idStr := c.Params.ByName("id")
    idInt, _ := strconv.Atoi(idStr)
    id := int32(idInt)

    var todo api.Todo

    if tr.db.First(&todo, id).RecordNotFound() {
        c.JSON(404, gin.H{"error": "not found"})
    } else {
        tr.db.Delete(&todo)
        c.Data(204, "application/json", make([]byte, 0))
    }
}

Todo HTTP Client

This enables our other go apps to leverage our service without knowing the details of what the REST API look like. A client application need only import the client and api to be able to treat the service like a local library.
Even if we don't have any go applications lined up to use our service, building the client implementation in conjunction with the service is very helpful for testing the API -- more on that later.
type TodoClient struct {
    Host string
}

func (tc *TodoClient) CreateTodo(title string, description string) (api.Todo, error) {
    var respTodo api.Todo
    todo := api.Todo{Title: title, Description: description}

    url := tc.Host + "/todo"
    r, err := makeRequest("POST", url, todo)
    if err != nil {
        return respTodo, err
    }
    err = processResponseEntity(r, &respTodo, 201)
    return respTodo, err
}

func (tc *TodoClient) GetTodo(id int32) (api.Todo, error) {
    var respTodo api.Todo

    url := tc.Host + "/todo/" + strconv.FormatInt(int64(id), 10)
    r, err := makeRequest("GET", url, nil)
    if err != nil {
        return respTodo, err
    }
    err = processResponseEntity(r, &respTodo, 200)
    return respTodo, err
}

func (tc *TodoClient) DeleteTodo(id int32) error {
    url := tc.Host + "/todo/" + strconv.FormatInt(int64(id), 10)
    r, err := makeRequest("DELETE", url, nil)
    if err != nil {
        return err
    }
    return processResponse(r, 204)
}
The referenced helper functions can be found here

Tests

Of course we need more testing, but here's a start to illustrate how to use the client to test our service. n.b. We will need to have a copy of the server running for them to work.
func TestCreateTodo(t *testing.T) {

    // given
    client := client.TodoClient{Host: "http://localhost:8080"}

    // when
    todo, err := client.CreateTodo("foo", "bar")

    //then
    if err != nil {
        t.Error(err)
    }

    if todo.Title != "foo" && todo.Description != "bar" {
        t.Error("returned todo not right")
    }

    // cleanup
    _ = client.DeleteTodo(todo.Id)
}

func TestGetTodo(t *testing.T) {

    // given
    client := client.TodoClient{Host: "http://localhost:8080"}
    todo, _ := client.CreateTodo("foo", "bar")
    id := todo.Id

    // when
    todo, err := client.GetTodo(id)

    // then
    if err != nil {
        t.Error(err)
    }

    if todo.Title != "foo" && todo.Description != "bar" {
        t.Error("returned todo not right")
    }

    // cleanup
    _ = client.DeleteTodo(todo.Id)
}

func TestDeleteTodo(t *testing.T) {

    // given
    client := client.TodoClient{Host: "http://localhost:8080"}
    todo, _ := client.CreateTodo("foo", "bar")
    id := todo.Id

    // when
    err := client.DeleteTodo(id)

    // then
    if err != nil {
        t.Error(err)
    }

    _, err = client.GetTodo(id)
    if err == nil {
        t.Error(err)
    }
}

Next Steps

Hopefully this shows how easy it is to get started building your own microservice infrastructure in go.
In addition to building out missing functionality, we also need to externalize our configuration and provide a way to manage the database. We also need to organize the components of our app (api, client, and service) into separate packages so that a client application need not import the service code but the api can be shared.
I've published a complete example on github that takes care of these things:

Usage

$ ./cmd/server/server 
NAME:
   todo - work with the `todo` microservice

USAGE:
   todo [global options] command [command options] [arguments...]

VERSION:
   0.0.1

COMMANDS:
   server     Run the http server
   migratedb  Perform database migrations
   help, h    Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --config, -c 'config.yaml'   config file to use
   --version, -v                print the version
   --help, -h                   show help

Bootstrapping the database

Create an empty database, fill in the supplied config: config.yaml and then run migratedb to initialize the todo table.
mysql -u root -p -e 'Create Database Todo;'

./cmd/server/server --config config.yaml migratedb
Since this command is separate from running your service, you can use a different config (with different database credentials.) Additionally it can perform updates to the table if your todo model expands (just update the todostructure.)

Starting The Server

./cmd/server/server --config config.yaml server

Trying it out

With the server running, you can also try out the example todo cli app included with the project:
$ todo add foo bar
{Id:17 Created:1405039312 Status:todo Title:foo Description:bar}

$ todo add hello world
{Id:18 Created:1405039324 Status:todo Title:hello Description:world}

$ todo ls
{Id:18 Created:1405039324 Status:todo Title:hello Description:world}
{Id:17 Created:1405039312 Status:todo Title:foo Description:bar}

$ todo done 18
{Id:18 Created:1405039324 Status:done Title:hello Description:world}

$ todo ls
{Id:18 Created:1405039324 Status:done Title:hello Description:world}
{Id:17 Created:1405039312 Status:todo Title:foo Description:bar}
(not the prettiest output, I know...)

Some Things To Look At...

Makefile

One update in the github repo is the addition of a Makefile. It takes care of running the server for the tests as well as building both the server binary and an example "todo" cli app that leverages the client (these can be found in the cmddirectory).

CLI API

To support the cli interface with flags and subcommands, I included codegangsta/cli. In addition, I've leveraged Canonical's goyaml to externalize the configuration into a yaml config.
These components aren't by any means necessary for a solid microservice platform, but are a nice start. (Consulintegration might be a nice alternative ;))

GORM

GORM is by no means the ubiquitous choice for database abstraction, but for this simple example it suited my needs. One nice thing about microservices is that one size need not fit all, and we could use something completely different if we had a different problem to solve.

TL;DR

Microservices are cool, and they might be more than a fad. With new technologies like Docker and OpenStack popping up, composing your applications into microservices, which can be scaled and refactored individually, makes increasingly more sense.

Tuesday, August 11, 2015

Техники редиректа в ASP.NET MVC

http://www.codeproject.com/Tips/583469/MVC-Different-Redirection-Techniques-Razor

This tip lists different redirection techniques for MVC4.
  1. HTML Tag
    <input type="button" value="Forgot Password" 
    onclick="window.location.href('@Url.Action("ForgotPassword","Account")')" />
       
  2. For posting, you could use a form:
    <form method="post" id="signin" action="@Url.Action("ForgotPassword", "Account")">
    <input type="button" value="Forgot Password" />
    </form>
       
  3. Using script:
    <script type="text/javascript">
        $(document).ready(function () {     
          $('#btnForgotPassword').click(function (e)      {
              location.href = '@Url.Content("~/Account/ForgotPassword/")';
          });
        });
    </script>
       
  4. If it is a link:
    @Html.ActionLink("some text", "actionName", "controllerName")
       
  5. For posting, use a form:
    @ using(Html.BeginForm("actionName", "controllerName")) { 
        <input type="submit" value="Some text" />
    }
       
  6. For Anchor Tag:
    <a href="http://www.codeproject.com/Controller/View" class="Button">Text</a>
       
  7. Using RenderAction:
    @{ Html.RenderAction("ActionName", "ControllerName", 
    new { FranchiseSetId = Model.FranchiseSetID }); }

Tuesday, August 4, 2015

Руководство по установке и настройке Web Deploy в среде Windows Server 2008 R2

http://habrahabr.ru/post/192150/

В этом руководстве мы настроим Web Deploy в среде Windows Server 2008 R2 для простого развертывания приложений из Visual Studio 1 кликом мыши. Web Deploy позволяет настроить публикацию пользователем, не обладающим правами администратора.

Установка Web Deploy


Web Deploy можно установить двумя способами (мне именно так и пришлось).

Web Platform Installer


Первый способ через Web Platform Installer. Его необходимо скачать и установить на сервер. Затем открыть и выбрать такие пункты как: Web Deploy, Web Deploy for Hosting Servers и IIS Recommendated Configuration (на скриншоте не видно)

Web Platform Installer установка Веб Деплой

Пакеты, устанавливаемые через WPI включают в себя дополнительные компоненты, некоторым может это не понравится. Однако для новчиков этот путь будет самый простой. 

Установка через msi


Необходимо скачать файл установки Web Deploy (на сегодня версии 3.5) и установить его. При ручной установке необходимо будет вручную установить необходимые службы IIS. Делается это через панель управления сервером: Roles > IIS > Add Role Services

Добавление необходимые сервисов в IIS

Необходимо выбрать и установить следующие:
  1. Logging Tools
  2. Management Service (IIS Management Console может понадобиться)

После всех установок в панели IIS Manager должны появиться Features: Management Service, Management Service Delefation. Если устанавливаете Web Deploy вручную, необходимо в Management Service разрешить удаленное управление IIS:

IIS Management Service

Настройка сайта для удаленной публикации


После всех дейсвий должен был появиться пункт меню

Deploy Menu

Создаем пользователя не администратора и задаем ему сложный пароль. Также даем ему права на запись в папку сайта в wwwroot.
Далее настраиваем удаленную публикацию:

Настройка удаленной публикации Web Deploy

  1. Выбираем пользователя, с которым мы будем публиковать наш сайт;
  2. Указываем URL для публикации. Это должен быть внешний IP сервера с открытым портом по умолчанию 8172. При установке через WPI создается правило в Firewall и порт открывается.
  3. Указываем место, куда сохранить файл настроек.
  4. Нажимаем Setup

Получаем:
Publish enabled for 'WIN-9APS8Q11R9V\InDeploy' Granted 'WIN-9APS8Q11R9V\InDeploy' full control on 'C:\inetpub\wwwroot\mysite' Successfully created settings file 'C:\Users\Administrator\Desktop\WIN-9APS8Q11R9V_InDeploy.PublishSettings'

Проверить настройку можно перейдя по адресу в браузере, который мы указывали в настройке публикации вида:http://111.111.111.111:8172/msdeploy.axd — браузер должен предложить авторизоваться или написать что служба найдена, но мы не авторизованы.
Также необходимо проверить:
  • Служба msdepsvc запущена в автозапуске и работает сейчас net stop msdepsvc & net start msdepsvc
  • Аналогично служба wmsvc net stop wmsvc & net start wmsvc
  • Также необходимо уточнить, возможен ли пинг до 80 порта (для службы Web Deployment Agent Service (MsDepSvc)) и 8172 для службы Web Management Service (WmSvc, handler).

Если публикация не настроена или возникают проблемы можно включить логи (Enabling Web Management Service Failed Request Tracing)

Настройка публикации Web Deploy в Visual Studio


После успешной настройки на стороне сервера необходимо настроить публикацию в Visual Studio. Для этого создаем профиль публикации, в методе публикации выбираем Web Deploy.

Настройка веб деплой 1

На следующем шаге выбираем конфигурацию Release. Здесь же можно указать строку подключения, на которую заменит VS при публикации (не пробовал). Получится удобная публикация решения в 1 клик:

готовая настройка

Важно: название сайта должно быть таким же как оно отображается в IIS Management.
Важно: Если вы установили подписанный сертификат SSL на удаленном сервере, убедитесь, что вы установите флажок “Разрешить ненадежный сертификат” флажок. По умолчанию Web Deploy установит сертификат для вас, чтобы он был уникальный, однако он будет самоподписанный.
Также есть настройка, которая позволяет уточнить «Оставлять ли лишние файлы или нет». Веб деплой будет копировать только изменившиеся файлы с момента последней публикации.

Резюме


Web Deploy предоставляет мощный и гибкий инструмент автоматизированного развертывания приложений ASP.NET на удаленном сервере. Кроме того, он позволяет публиковать не только файлы но и схемы базы данных и скрипты обновления и скрипты настройки параметров ACL (Access Control List)
Можно использовать Visual Studio, чтобы автоматически публиковать решения на сервер или же создать Web Deploy Package и вручную установить необходимые обновления на сервере.
Надеюсь с этим руководством станет проще публиковать свои сайты.

Ссылки