Giter Club home page Giter Club logo

applikatoni's Introduction

Applikatoni - Deployments Al Forno Build Status GoDoc

Applikatoni is a self-hosted deployment server for small teams, that takes your code from GitHub and deploys it to your servers, by running shell commands you define.

Introduction

Applikatoni was developed at flinc for internal use and later open sourced.

With its web-frontend and its deep GitHub integration it allows you to deploy your applications to multiple servers with the click of a button.

Besides giving your team a history of deployments, so you can see what has been deployed by whom to which servers, it also offers integration into a multitude of a third-party services, so your team never misses a deployment. Deploy pull requests from GitHub, check the Travis CI status of a branch before deploying, notify your team in Flowdock or Slack about deployments, tell NewRelic about the current revision of your application and reset Bugsnag after a deployment.

No database server is needed. Applikatoni stores information about deployment in a sqlite3 file. Easy to use, easy to maintain and easy to backup.

At its core, Applikatoni is technology agnostic. Deploy Rails, Node.js, Java and Go applications. It doesn't matter to Applikatoni. It doesn't do more than taking your code from GitHub and deploying it to your servers by running the shell commands you specify.

You can run different scripts on different hosts, if they fulfill different roles in your system. Your application server may need to run different commands than your background worker server, or your database server. Applikatoni can handle all of this.

With the ability to deploy multiple applications to different servers with varying deployment strategies you can use Applikatoni however you like.

And if clicking isn't your thing and you prefer a terminal over a web frontend: check out toni -- the CLI for your Applikatoni server.

Also: there is a lot of pizza involved! 🍕

Getting started

  1. Create a GitHub Application for your organization so Applikatoni can access your code:

       https://github.com/organizations/<YOUR ORGANIZATION>/settings/applications
    

    Enter the OAuth2 callback URL:

       <where your applikatoni instance will be hosted>/oauth2/callback
    
  2. Install Applikatoni on your server. See Installation for more information

  3. Configure Applikatoni. See Configuration for detailed instructions.

  4. Setup the database with the packaged goose binary. See Usage on how to do that.

  5. Start Applikatoni

Installation

Dependencies

Download a packaged version

See the Releases section and download a packaged version of Applikatoni.

Installing using go get

    go get github.com/applikatoni/applikatoni/server

Usage

  1. Make sure the database file is setup and migrated:

     go get bitbucket.org/liamstask/goose/cmd/goose
     vim db/dbconf.yml
     goose -env="production" up
    
  2. Create a configuration.json file for your needs. See Configuration for more information.

     cp configuration_example.json configuration.json
     vim configuration.json
    
  3. Start the server:

     ./applikatoni -port=:8080 -db=./db/production.db -conf=./configuration.json -env=production
    

How it works

Applikatoni is a server with a web-frontend that allows users to deploy specific commits hosted on GitHub to multiple servers.

A deployment process consists of multiple stages, which Applikatoni will execute sequentially on each server, but at the same time on all servers.

A stage is nothing more than a few lines of shell commands, that you specify. These shell commands can contain variables, like CommitSha, RubyVersion, WorkingDirectory and so on. Before the deployment process starts, Applikatoni "renders" these scripts templates into fully executable commands, by injecting the values of the variables.

With the templates fully rendered, Applikatoni then connects to the specified servers via SSH (and a username and private SSH key you specify) and executes these shell commands.

If one stage fails, the whole deployment process is stopped after the failed stage has finished on all servers, so there is no inconsistent state.

Which stages are executed on which servers depends on which roles each server fulfills in your system. If the server one.your-company.com has the role "application-server" and two.your-company.com has the role "queue-server" only the stages specified for the respective stages get executed on the servers.

Here is an excerpt from an example configuration.json:

"default_stages": ["PRE_DEPLOYMENT", "CODE_DEPLOYMENT", "POST_DEPLOYMENT"],
"available_stages": ["PRE_DEPLOYMENT", "CODE_DEPLOYMENT", "MIGRATE_DATABASE", "POST_DEPLOYMENT"],
"hosts": [
  {
    "name": "web.shipping-company.com:22",
    "roles": ["web", "migrator"]
  },
  {
    "name": "workers.shipping-company.com:22",
    "roles": ["workers"]
  }
],
"roles": [
  {
    "name": "workers",
    "script_templates": {
      "PRE_DEPLOYMENT": "sudo /etc/init.d/workers stop",
      "CODE_DEPLOYMENT": "cd {{.Dir}}/current && git fetch origin && git reset -q --hard {{.CommitSha}}",
      "POST_DEPLOYMENT": "sudo /etc/init.d/workers start"
    },
    "options": {
      "Dir": "/var/www/our-main-application"
    }
  },
  {
    "name": "web",
    "script_templates": {
      "PRE_DEPLOYMENT": "sudo /etc/init.d/application-server prepare",
      "CODE_DEPLOYMENT": "cd {{.Dir}}/current && git fetch origin && git reset -q --hard {{.CommitSha}}",
      "POST_DEPLOYMENT": "sudo /etc/init.d/application-server reload"
    },
    "options": {
      "Dir": "/var/www/our-main-application"
    }
  },
  {
    "name": "migrator",
    "script_templates": {
      "MIGRATE_DATABASE": "cd {{.Dir}} && make migrate-database"
    },
    "options": {
      "Dir": "/var/www/our-main-application",
    }
  }
]

The web.shipping-company.com host has the roles web and migrator. The workers.shipping-company.com host has the role workers.

With a default deployment, the stages PRE_DEPLOYMENT, CODE_DEPLOYMENT and POST_DEPLOYMENT will be run on those two hosts.

If you also select MIGRATE_DATABASE before creating the deployment, the stages PRE_DEPLOYMENT, CODE_DEPLOYMENT, MIGRATE_DATABASE and POST_DEPLOYMENT will be run on those two hosts. In this order. And since workers.shipping-company.com doesn't fulfill the migrator role, the MIGRATE_DATABASE stage will be skipped on this server.

Before running the commands, Applikatoni converts this:

cd {{.Dir}}/current && git fetch origin && git reset -q --hard {{.CommitSha}}

into this:

cd /var/www/our-main-application/current && git fetch origin && git reset -q --hard F00B4R

where F00B4R is the commit SHA you selected in the web frontend.

Terminology

  • application - Applikatoni can deploy multiple applications
  • target - The target environment of a deployment, e.g.: production
  • host - Each target has one or more hosts, the servers to which to deploy to. e.g.: web-application.production.shipping-company.com
  • role - Every host of each target fulfills different roles. What gets executed and when on which host depends on the roles this host has. e.g.: database-server or web-app-server.
  • stage - A deployment consists of one or more stages. A role defines one or more stages (by defining script_templates for each stage). A stage can succeed or fail while deploying the application. If a stage failed, the deployment stops after this change. All stages are executed synchronously on all hosts (but in parallel).

Configuration

Requirements

  • a user on the servers you want to deploy your application to with a SSH-Key
  • a GitHub OAuth application (See here)

The configuration.json

The Applikatoni is configured by reading a configuration.json file.

You can find a simplified example for a Rails application with two Unicorn web servers and one Sidekiq worker server here: configuration_example.json. Or read on to get a run down of what it's doing.

Sample

Here is a sample configuration.json for an application called our-main-application with its source code hosted under github.com/shipping-company/our-main-application.

The application will be deployed to three servers:

  • 1.unicorn.production.shipping-company.com
  • 2.unicorn.production.shipping-company.com
  • 1.workers.production.shipping-company.com

The deployment process reflects one of a typical Rails application with the Unicorn webserver and Sidekiq as a background worker process.

  1. Shut down sidekiq
  2. Pull down the code from GitHub (the CommitSha variable is injected by Applikatoni into the script templates and represents the commit the user wants to deploy)
  3. Install dependencies
  4. Hot-reload Unicorn / start sidekiq
{
  "ssl_enabled": false,
  "host": "applikatoni.shipping-company.com",
  "session_secret": "<SECRET>",
  "oauth2_state_string": "<UNGUESSABLE RANDOM OAUTH2 STATE STRING>",
  "github_client_id": "<CLIENT_ID>",
  "github_client_secret": "<CLIENT_SECRET>",
  "mandrill_api_key": "<API_KEY>",
  "mailgun_base_url": "<MAILGUN_BASE_URL>",
  "mailgun_api_key": "<API_KEY>",
  "applications": [
    {
      "name": "our-main-application",
      "read_usernames": ["<CAN READ DEPLOYMENT HISTORY BUT NOT DEPLOY>"],
      "github_owner": "shipping-company",
      "github_repo": "our-main-application",
      "github_branches": ["master", "develop", "production", "production"],
      "travis_image_url": "https://magnum.travis-ci.com/shipping-company/our-main-application.svg?token=<KEY HERE>",
      "daily_digest_receivers": ["[email protected]"],
      "daily_digest_target": "production",
      "targets": [
        {
          "name": "production",
          "deployment_user": "deploy",
          "deployment_ssh_key": "<SSH KEY>",
          "deploy_usernames": ["<YOUR GITHUB USERNAME>"],
          "default_stages": ["CHECK_CONNECTION", "PRE_DEPLOYMENT", "CODE_DEPLOYMENT", "POST_DEPLOYMENT"],
          "available_stages": ["CHECK_CONNECTION", "PRE_DEPLOYMENT", "CODE_DEPLOYMENT", "MIGRATE_DATABASE", "POST_DEPLOYMENT"],
          "bugsnag_api_key": "<YOUR BUGSNAG API KEY>",
          "flowdock_endpoint": "<FLOWDOCK REST ENTRY POINT WITH HTTP AUTH>",
          "new_relic_api_key": "<NEW RELIC API KEY>",
          "new_relic_app_id": "<NEW RELIC APP ID>",
          "slack_url": "<SLACK INCOMING WEBHOOK URL>",
          "webhooks": [
            "<URL>",
          ],
          "hosts": [
            {
              "name": "1.unicorn.production.shipping-company.com:22",
              "roles": ["web", "migrator"]
            },
            {
              "name": "2.unicorn.production.shipping-company.com:22",
              "roles": ["web"]
            },
            {
              "name": "1.workers.production.shipping-company.com:22",
              "roles": ["workers"]
            }
          ],
          "roles": [
            {
              "name": "workers",
              "script_templates": {
                "CHECK_CONNECTION": "test -d {{.Dir}}",
                "PRE_DEPLOYMENT": "sudo /etc/init.d/sidekiq stop",
                "CODE_DEPLOYMENT": "cd {{.Dir}}/current && git fetch --tags -q origin && git reset -q --hard {{.CommitSha}}\ncd {{.Dir}}/current && RAILS_ENV={{.RailsEnv}} bundle install --gemfile {{.Dir}}/current/Gemfile --path {{.Dir}}/shared/bundle --deployment --quiet --without development test",
                "POST_DEPLOYMENT": "sudo /etc/init.d/sidekiq start"
              },
              "options": {
                "Dir": "/var/www/our-main-application",
                "RailsEnv": "production"
              }
            },
            {
              "name": "web",
              "script_templates": {
                "CHECK_CONNECTION": "test -d {{.Dir}}",
                "PRE_DEPLOYMENT": "echo 'Unicorn will keep on working'",
                "CODE_DEPLOYMENT": "cd {{.Dir}}/current && git fetch --tags -q origin && git reset -q --hard {{.CommitSha}}\ncd {{.Dir}}/current && RAILS_ENV={{.RailsEnv}} bundle install --gemfile {{.Dir}}/current/Gemfile --path {{.Dir}}/shared/bundle --deployment --quiet --without development test",
                "POST_DEPLOYMENT": "sudo /etc/init.d/unicorn hot-reload"
              },
              "options": {
                "Dir": "/var/www/our-main-application",
                "RailsEnv": "production"
              }
            },
            {
              "name": "migrator",
              "script_templates": {
                "MIGRATE_DATABASE": "cd {{.Dir}} && RAILS_ENV={{.RailsEnv}} bundle exec rake db:migrate"
              },
              "options": {
                "Dir": "/var/www/our-main-application",
                "RailsEnv": "production"
              }
            }
          ]
        }
      ]
    }
  ]
}

General Properties

  • ssl_enabled - Turn this on if your Applikatoni instance is accessed via https.
  • host - The host of your Applikatoni instance. Example: applikatoni.shipping-company.com
  • session_secret - The secret for encrypt sessions in cookies. Use a generated, random secret.
  • oauth2_state_string - A random, unguessable string to confirm that the Applikatoni instance is the one specified at GitHub.
  • github_client_id - The client ID from your GitHub OAuth2 application.
  • github_client_secret - The client secret from your GitHub OAuth2 application.
  • mandrill_api_key - The API key of your Mandrill account. Optional, but this is needed to send daily digest emails. If this is blank or left out, no daily digest email will be sent.
  • mailgun_base_url and mailgun_api_key - The base URL and API key of your Mailgun account. Optional, but this is needed to send daily digest emails. If this is blank or left out, the configuration is checked for Mandrill credentials, if none are found, no daily digest email will be sent.
  • applications - An array of application configurations that Applikatoni can deploy.

Application Properties

Inside the applications array applications need to be configured.

  • name - The name of the application. Shows up in the web interface, notifications, and so on.
  • read_usernames - An array of GitHub usernames. Users with these names have "read" access to the application on Applikatoni. They can only look at the deployment history, but cannot deploy.
  • github_owner - The owner of the GitHub repository. It's the company in github.com/company/rails-app.
  • github_repo - The name of the GitHub repository. It's the rails-app in github.com/company/rails-app.
  • github_branches - An array of branch names. These branches will show up with their current status on the application page in Applikatoni to easily deploy them with a click.
  • travis_image_url - The URL to the Travis CI status image, including the token.
  • daily_digest_receivers - An array of email addresses to which the daily digest should be sent (if mandrill_api_key or mailgun_base_url and mailgun_api_key are not set, no daily digest will be sent).
  • daily_digest_target - The name of the target for which the daily digest should be sent. For example: if you have test, staging and production targets, it makes sense to only send out daily digest emails for production.

Target Properties

Inside the targets array of each configured application the targets to which Applikatoni can deploy need to be configured.

Examples for targets are production, staging, testing, and so on. Each target has its own number of hosts.

  • name - The name of the target.

  • deployment_user - The user on the target hosts that has access via SSH.

  • deployment_ssh_key - The private SSH key of the deployment user. The public key of the user must be added to the hosts, so Applikatoni can access the host without password authentication

  • deploy_username - An array of GitHub usernames. Users with these names have "deploy" access to this target.

  • bugsnag_api_key - Your Bugsnag API key. If this is set, Applikatoni will notify Bugsnag about a deployment to this target after a successful deployment. If this is left blank, Applikatoni will not notify NewRelic about deployments.

  • flowdock_endpoint - The Flowdock Message URL including the auth information. Example: https://[email protected]/flows/acme/main/messages. If this is left blank, Applikatoni will not notify Flowdock about deployments.

  • newrelic_api_key - The NewRelic API key. If this and newrelic_app_id are set, Applikatoni will notify NewRelic about successful deployments. If this is left blank, Applikatoni will not notify NewRelic about deployments.

  • newrelic_app_id - The NewRelic Application ID. If this and newrelic_api_key are set, Applikatoni will notify NewRelic about successful deployments. If this is left blank, Applikatoni will not notify NewRelic about deployments.

  • hosts - An array of hosts, where each host needs the properties name and roles. Example:

          {
            "name": "webapp.staging.company.com:22",
            "roles": ["web", "migrator"]
          },
          {
            "name": "workers.staging.company.com:22",
            "roles": ["workers"]
          }
    
          IMPORTANT: The host name _must_ include the port!
    
  • default_stages - An array of stage names. These get executed per default on each deployment, if nothing else is specified in the web interface. Order is important! The order determines the deployment order!

  • available_stages - An array of all available stages. These are all the available stages that can be selected in the web interface. Order is important! The order determines the deployment order!

  • roles - An array of roles. The names of these roles must match the role names specified for the hosts.

Role Properties

  • name - The name of this role. Examples: "worker-server", "webapp", "database".
  • options - A hash of options. The keys are the names of available variables in the script_templates.
  • script_templates - A hash where the keys are the name of the corresponding stage (and they must match a name in available_stages, otherwise they won't get executed). The values are templates in the syntax of Go's text/template package.

A small example illustrates how this works:

{
  "name": "web",
  "script_templates": {
    "CHECK_CONNECTION": "test -d {{.Dir}}",
    "PRE_DEPLOYMENT": "echo 'Unicorn will keep on working'",
    "CODE_DEPLOYMENT": "cd {{.Dir}}/current && git fetch origin && git reset --hard {{.CommitSha}}\ncd {{.Dir}}/current && RAILS_ENV={{.RailsEnv}} bundle install --gemfile {{.Dir}}/current/Gemfile --path {{.Dir}}/shared/bundle --deployment --quiet --without development test",
    "POST_DEPLOYMENT": "sudo /etc/init.d/unicorn hot-reload"
  },
  "options": {
    "Dir": "/var/www/our-main-application",
    "RailsEnv": "production"
  }
}

When these stages are executed {{.Dir}} and {{.RailsEnv}} in the templates are replaced with the values specified in options. This:

        cd {{.Dir}} && RAILS_ENV={{.RailsEnv}} bundle exec rake db:migrate

gets turned into this before execution:

        cd /var/www/our-main-application && RAILS_ENV=production bundle exec rake db:migrate

The other variable, CommitSha, is a special variable. It gets merged into the options field when a deployment is started. You can see the other special variables here.

Script Templates

Script templates, after being fully rendered with the options passed in, are executed line by line.

Important: Script templates do not keep state between stages and commands! Even though Applikatoni re-uses the SSH connections to each host, for each line a new SSH session is used.

That means, that the working directory needs to be set for each line.

This is wrong:

"CODE_DEPLOYMENT": "cd {{.Dir}}/current\ngit fetch origin"

This is correct:

"CODE_DEPLOYMENT": "cd {{.Dir}}/current && git fetch origin"

If one line in a template fails, the whole stage is considered failed.

Testing

Make sure you have sqlite3 and goose installed.

cd server
goose -env="test" up
cd ..
go test ./...

Contributing

All contributions are welcome! Is the documentation lacking something? Did you find a bug? Do you have a question about how Applikatoni works? Open a issue!

If you want to contribute code to Applikatoni, use the standard GitHub model of contributing:

  1. Fork the repository
  2. Create a branch in which you'll work (e.g. config_validation)
  3. Make your commits in this branch
  4. Add yourself to CHANGELOG.md
  5. Send a pull request to merge your branch into the master branch

Here is a great explanation of how to handle GitHub forks and Go import paths: Contributing to Open Source Git Repositories in Go.

Make sure that you have the dependencies installed. See Installation.

To get the tests running locally, you have to make sure that your test database is migrated to the newest version. See Testing.

Before sending a pull request, make sure that the tests are green and the build runs fine:

  1. go test ./...
  2. cd server && go build -o applikatoni .

Make sure you run go fmt before committing changes!

Should you add a test? It depends. If it's easy to do: by all means, go ahead and do it! But especially the code in the server package is not that testable, so I'd understand if you won't add a test.

Then send a pull request. If any of this doesn't work and you don't know why: send a pull request (or open a issue) and we'll see what we can do!

To get Applikatoni running locally see Installation, Getting Started and Usage. You will also need a configuration.json file to play around with -- see Sample for this. (And use GitHub developer applications to login locally).

There is also a Vagrantfile that installs Applikatoni and runs the tests. So, a vagrant up in this repository is enough to get Applikatoni fully installed in a virtual machine.

If you want to contribute and don't know where to start, check out the issues tagged with help wanted.

If you're looking for easy issues, look no further than the issues tagged as easy.

Is it production ready?

We've been using Applikatoni at flinc for over half a year with multiple applications without any major problems. Several hundreds of deployments by different users to a lot of servers.

Authors

License

MIT License. See LICENSE.

For ease of use, Applikatoni server ships with goose. The license of goose is the MIT license.

For an improved user experience, Applikatoni server ships with clickspark.js. The license of clickspark.js is the MIT license.

Developed at & sponsored by

applikatoni's People

Contributors

codelingobot avatar fabrik42 avatar hparker avatar mrnugget avatar nikai3d avatar wind0r avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

applikatoni's Issues

Add ability to filter the list of deployments by target

At the moment the list of deployments shows all the deployments of one application. This can get quite frustrating if one only wants to see deployments of a certain target, e.g. production or staging.

The idea is to add a filtering mechanism to the index of deployments found under /<application>/deployments. Probably above the list of deployments here:

2015-11-10 at 17 21

So, what's needed?

  1. Extend the handler here in handlers.go to accept filtering parameters (and validate them)
  2. Extend/add function that returns the deployments for the selected filtering paramters here in database.go
  3. Add tests for the extended/added functions in database.go to the database_test.go file here
  4. Add a UI element that sends the filtering parameters to the server and results in a reload of the page to the template here

To get started on this issue, read through the Contributing section of the README to see how to set up the Applikatoni for development on your machine and how to send a pull request that fixes this issue.

If you have any questions: feel free to ask!

Encoding for messages sent to Flowdock broken

Whenever you use a character like ' in a deployment comment and the comment is sent to Flowdock the encoding is broken:

2015-12-14 at 14 32

In this example " gets turned into &#34;.

The complete code for the Flowdock notifier can be found here.

I'll see what I can dig up in regards to the encoding and the Flowdock API.

Add a footer to the main layout

I think it would be great to have a footer in the layout.

What should be displayed in the footer? I guess for now:

So something like

Applikatoni 1.1.0
© flinc 2015

Implementing this should be pretty straight forward:

  • Adding a <footer> element to the layout here.
  • Adding a <div class="container"> with rows and columns to the footer
  • Style it, so it looks like the header

Maybe also link to applikatoni.com? Maybe link to GitHub? Different styling? I don't know :) Ideas welcome! But it sure would be great to have the current version handy and visible in the footer.

To get started on this issue, read through the Contributing section of the README to see how to set up the Applikatoni for development on your machine and how to send a pull request that fixes this issue.

If you have any questions or ideas: feel free to ask!

[Question] Local use?

Can Applikatoni work on the same host as the deployment target server? ie: without ssh

Generic HTTP Webhooks

Hi,

I think it would be a cool idea, if we could support generic HTTP webhooks, like e.g. Travis CI.
This way, you would not need to write your own plugin for simple notifiers.

How I imagine this:

  • You can add a list of URLs as webhooks to your config file.
  • Every time the state of a deployment changes (start, success fail), the webhooks will be triggered.
  • Triggered means: A POST request will be sent to the URLs, with payload containing information about the deploy.
  • The payload should be very comprehensive, basically including all kind of information that is interesting in the context of the deploy, so we can cover a lot of use cases.

We could add another section in the configuration file to provide a list URLs that should be triggered, when the status of a deployment changes. Example:

{
  applications: [{
    targets: [{
      webhooks: [
        "https://example.org/notifyme",
        "https://anotherservice.org/notifications"
      ]
    }]
  }]
}

The Travis CI docs could be helpful. Here is an example payload.

Gitlab / Gitlab CI integration

It would be amazing if you could use Applikatoni with Gitlab as it's also great to run self-hosted and Gitlab-CI is a really nice, simple integrated CI system.

Love the app, well done!

nil pointer dereference in Bugsnag notifier

We just ran into a crash in production resulting in the following output:

2016/01/13 13:35:37 Successfully notified Flowdock about deployment of flincOnRails on staging, 0681af76ce1f84fdca26de38122571b42a643def!
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x0 pc=0x40a9e1]

goroutine 15182 [running]:
main.SendBugsnagRequest(0xb40d00, 0x21, 0xc8201b4000, 0xc820094800, 0xc8200c1550)
        /home/mrnugget/code/go/src/github.com/applikatoni/applikatoni/server/bugsnag_notifier.go:61 +0x961
main.NotifyBugsnag(0xc8201ae000, 0xb40d00, 0x21, 0x412)
        /home/mrnugget/code/go/src/github.com/applikatoni/applikatoni/server/bugsnag_notifier.go:41 +0x581
created by main.newBugsnagNotifier.func1
        /home/mrnugget/code/go/src/github.com/applikatoni/applikatoni/server/bugsnag_notifier.go:72 +0x123

goroutine 1 [IO wait]:
net.runtime_pollWait(0x7fc71e282330, 0x72, 0xc82000e1b0)
        /usr/local/go/src/runtime/netpoll.go:157 +0x60
net.(*pollDesc).Wait(0xc820200220, 0x72, 0x0, 0x0)
        /usr/local/go/src/net/fd_poll_runtime.go:73 +0x3a
net.(*pollDesc).WaitRead(0xc820200220, 0x0, 0x0)
        /usr/local/go/src/net/fd_poll_runtime.go:78 +0x36
net.(*netFD).accept(0xc8202001c0, 0x0, 0x7fc71e282428, 0xc820b3e080)
        /usr/local/go/src/net/fd_unix.go:408 +0x27c
net.(*TCPListener).AcceptTCP(0xc820028920, 0x47b540, 0x0, 0x0)
        /usr/local/go/src/net/tcpsock_posix.go:254 +0x4d
net/http.tcpKeepAliveListener.Accept(0xc820028920, 0x0, 0x0, 0x0, 0x0)
        /usr/local/go/src/net/http/server.go:2135 +0x41
net/http.(*Server).Serve(0xc8201d71a0, 0x7fc71e2823f0, 0xc820028920, 0x0, 0x0)
        /usr/local/go/src/net/http/server.go:1887 +0xb3
net/http.(*Server).ListenAndServe(0xc8201d71a0, 0x0, 0x0)
        /usr/local/go/src/net/http/server.go:1877 +0x136
net/http.ListenAndServe(0xaa08b0, 0x5, 0x7fc71e281358, 0xc8201e4fc0, 0x0, 0x0)
        /usr/local/go/src/net/http/server.go:1967 +0x8f
main.main()
        /home/mrnugget/code/go/src/github.com/applikatoni/applikatoni/server/main.go:167 +0x1d98
[...]

So the nil dereference comes from bugsnag_notifier.go, line 61, which looks like this:

    resp, err := http.PostForm(endpoint, params)
    if err != nil || resp.StatusCode != 200 {
        log.Printf("Error while notifying Bugsnag about deployment of %v on %v, %v! err: %s, resp: %s\n",
            d.ApplicationName,
            d.TargetName,
            d.CommitSha,
            err,
            resp.Status)  // LINE 61
        return
    }

The problem is that if err != nil then resp may be nil, according to the source code in the Go stdlib.

So, how do we fix this?

  1. We need two checks: if err != nil and if resp.StatusCode != 200.
  2. If err != nil we need to log this, but can't access the resp when logging
  3. If resp.StatusCode != 200 we need to log this
  4. Fix this in every notifier

To get started on this issue, read through the Contributing section of the README to see how to set up the Applikatoni for development on your machine and how to send a pull request that fixes this issue.

If you have any questions, ideas, comments: feel free to ask!

Add a super cool startup banner with ASCII art

Applikatoni needs a ASCII banner that shows up when Applikatoni is booted. I'm really jealous of Sidekiq and the awesome banner it has:

2015-12-17 at 09 30

So! Applikatoni needs this too. I'm thinking about the Applikatoni logo in ASCII, pizza in ASCII, "Applikatoni" in ASCII...

How to add this?

  1. Create a mind-blowing ASCII banner
  2. Add the ASCII banner as constant to the server package
  3. Check if the environment is development (we do not want to print this in production, in case someone wants to redirect the output to a logfile)
  4. Check if STDOUT is a TTY (again, we do not want to print the banner if STDOUT is redirect to a file, or if it's a pipe)
  5. If 3. and 4. are true, the print the banner here after everything is fully booted up

To get started on this issue, read through the Contributing section of the README to see how to set up the Applikatoni for development on your machine and how to send a pull request that fixes this issue.

If you have any questions, ideas, comments: feel free to ask!

Make applikatoni easy to use

Hi

Correct me if I get wrong.

I think not all users store their code in github, then I have idea.
We will support login from google. Code store in somewhere support, when deploy, we pull code, and restart monitor app, ex: supervisorctl restart app

How to run in dev mode

I try go run server/main.go but i get error:

# command-line-arguments
server/main.go:42: undefined: Configuration
server/main.go:47: undefined: KillRegistry
server/main.go:70: undefined: readConfiguration
server/main.go:75: undefined: parseTemplates
server/main.go:97: undefined: failUnfinishedDeployments
server/main.go:110: undefined: NewKillRegistry
server/main.go:113: undefined: SendDailyDigests
server/main.go:126: undefined: newLogEntrySaver
server/main.go:128: undefined: newBugsnagNotifier
server/main.go:130: undefined: newFlowdockNotifier
server/main.go:130: too many errors

Sorry, I'm go newbie

Add SHA and link to "this is what will be deployed"-diff

When selecting a target and SHA to deploy Applikatoni will show a diff of the changes to be deployed:

2015-11-10 at 17 30

Now, there is a "See diff on GitHub" link, which is quite helpful. But what would be even more awesome would be the SHA of every commit in this diff and a link to the commit on GitHub.

This diff is generated by making a AJAX call and then inserting the diff into the DOM. See the applikatoni.js file here. The response from the server is generated in this handler.

The response from the server already contains the SHA of each commit. That means displaying the SHA should only require changes here in applikatoni.js and in the hogan_templates.tmpl here.

Including the URL of the commit requires changes to the GitHub client used by Applikatoni. It should work by adding a Url field to the GitHubCommit struct here in github.go. See the GitHub documentation for the full response.

With the URL being returned by the server the same changes need to be made that displayed the SHA: in applikatoni.js and hogan_templates.tmpl.

To get started on this issue, read through the Contributing section of the README to see how to set up the Applikatoni for development on your machine and how to send a pull request that fixes this issue.

If you have any questions: feel free to ask!

Add CLI usage information and `-h` flag

At the moment there is no helpful output regarding the command line parameters the server takes. We should add "usage information" like toni does.

The usage information should explain the different parameters. It's shown if the -h flag is passed to the server.

The best place to add this would probably be here in main.go. If you're not sure what needs to be added, see the link to toni's toni.go above.

To get started on this issue, read through the Contributing section of the README to see how to set up the Applikatoni for development on your machine and how to send a pull request that fixes this issue.

Webhooks unreliable - Often run into a database lock

While testing around locally I ran into an issue with the webhooks, implemented in #31. It seems like the webhooks run into database locks really, really often. I'm still not sure why exactly but I know "how" this is happening: the webhook notifier loads a deployment from the database on every log message sent through the system. And most of these SELECT statements fail. Just to give you an idea of how often this happens, here is the log output:

2016/01/19 07:51:53 STARTING DEPLOYMENT: deployment_id=235
::1 - - [19/Jan/2016:07:51:52 +0100] "POST /super-duper-app/deployments HTTP/1.1" 303 0
::1 - - [19/Jan/2016:07:51:53 +0100] "GET /super-duper-app/deployments/235 HTTP/1.1" 200 10689
2016/01/19 07:51:53 STARTING STAGE: CHECK_CONNECTION
2016/01/19 07:51:54 2.web.shipping-company.com:22 -- STARTING: test -d /var/www/shipping-company.com
2016/01/19 07:51:54 1.web.shipping-company.com:22 -- STARTING: test -d /var/www/shipping-company.com
2016/01/19 07:51:54 1.workers.shipping-company.com:22 -- STARTING: test -d /var/www/shipping-company.com && cd /var/www/shipping-company.com/current
2016/01/19 07:51:54 1.web.shipping-company.com:22 -- SUCCESS: "test -d /var/www/shipping-company.com"
2016/01/19 07:51:54 1.workers.shipping-company.com:22 -- SUCCESS: "test -d /var/www/shipping-company.com && cd /var/www/shipping-company.com/current"
2016/01/19 07:51:54 2.web.shipping-company.com:22 -- SUCCESS: "test -d /var/www/shipping-company.com"
2016/01/19 07:51:54 STAGE RESULT: 1.web.shipping-company.com:22 - execution of stage CHECK_CONNECTION successful (144.395246ms)
2016/01/19 07:51:54 STAGE RESULT: 1.workers.shipping-company.com:22 - execution of stage CHECK_CONNECTION successful (293.338033ms)
2016/01/19 07:51:55 STAGE RESULT: 2.web.shipping-company.com:22 - execution of stage CHECK_CONNECTION successful (505.786888ms)
2016/01/19 07:51:56 STAGE SUCCESS: CHECK_CONNECTION
2016/01/19 07:51:56 STARTING STAGE: PRE_DEPLOYMENT
2016/01/19 07:51:56 2.web.shipping-company.com:22 -- STARTING: cd /var/www/shipping-company.com && pwd
2016/01/19 07:51:56 1.web.shipping-company.com:22 -- STARTING: cd /var/www/shipping-company.com && pwd
2016/01/19 07:51:56 1.workers.shipping-company.com:22 -- STARTING: echo 'shutting down workers'
2016/01/19 07:51:56 1.web.shipping-company.com:22 -- STDOUT -- /var/www/shipping-company.com
2016/01/19 07:51:56 1.web.shipping-company.com:22 -- SUCCESS: "cd /var/www/shipping-company.com && pwd"
2016/01/19 07:51:56 2.web.shipping-company.com:22 -- STDOUT -- /var/www/shipping-company.com
2016/01/19 07:51:56 2.web.shipping-company.com:22 -- SUCCESS: "cd /var/www/shipping-company.com && pwd"
2016/01/19 07:51:56 1.workers.shipping-company.com:22 -- STDOUT -- shutting down workers
2016/01/19 07:51:57 1.workers.shipping-company.com:22 -- SUCCESS: "echo 'shutting down workers'"
2016/01/19 07:51:58 STAGE RESULT: 1.web.shipping-company.com:22 - execution of stage PRE_DEPLOYMENT successful (128.502005ms)
2016/01/19 07:51:59 Could not find deployment with id 235, database is locked
2016/01/19 07:51:59 Could not find deployment with id 235, database is locked
2016/01/19 07:51:59 Could not find deployment with id 235, database is locked
2016/01/19 07:51:59 Could not find deployment with id 235, database is locked
2016/01/19 07:51:59 Could not find deployment with id 235, database is locked
2016/01/19 07:51:59 Could not find deployment with id 235, database is locked
2016/01/19 07:51:59 STAGE RESULT: 2.web.shipping-company.com:22 - execution of stage PRE_DEPLOYMENT successful (130.945111ms)
2016/01/19 07:51:59 STAGE RESULT: 1.workers.shipping-company.com:22 - execution of stage PRE_DEPLOYMENT successful (132.300165ms)
2016/01/19 07:51:59 STAGE SUCCESS: PRE_DEPLOYMENT
2016/01/19 07:51:59 STARTING STAGE: CODE_DEPLOYMENT
2016/01/19 07:51:59 2.web.shipping-company.com:22 -- STARTING: echo 'deploying code and sleeping' && sleep 20
2016/01/19 07:51:59 1.web.shipping-company.com:22 -- STARTING: echo 'deploying code and sleeping' && sleep 20
2016/01/19 07:51:59 1.workers.shipping-company.com:22 -- STARTING: echo 'deploying code and sleeping' && sleep 20
2016/01/19 07:51:59 1.workers.shipping-company.com:22 -- STDOUT -- deploying code and sleeping
2016/01/19 07:51:59 1.web.shipping-company.com:22 -- STDOUT -- deploying code and sleeping
2016/01/19 07:51:59 2.web.shipping-company.com:22 -- STDOUT -- deploying code and sleeping
2016/01/19 07:52:00 Could not find deployment with id 235, database is locked
2016/01/19 07:52:14 1.workers.shipping-company.com:22 -- SUCCESS: "echo 'deploying code and sleeping' && sleep 20"
2016/01/19 07:52:14 2.web.shipping-company.com:22 -- SUCCESS: "echo 'deploying code and sleeping' && sleep 20"
2016/01/19 07:52:14 1.web.shipping-company.com:22 -- SUCCESS: "echo 'deploying code and sleeping' && sleep 20"
2016/01/19 07:52:14 STAGE RESULT: 1.workers.shipping-company.com:22 - execution of stage CODE_DEPLOYMENT successful (20.124435914s)
2016/01/19 07:52:14 STAGE RESULT: 2.web.shipping-company.com:22 - execution of stage CODE_DEPLOYMENT successful (20.141649303s)
2016/01/19 07:52:14 STAGE RESULT: 1.web.shipping-company.com:22 - execution of stage CODE_DEPLOYMENT successful (20.142226488s)
2016/01/19 07:52:15 STAGE SUCCESS: CODE_DEPLOYMENT
2016/01/19 07:52:15 STARTING STAGE: POST_DEPLOYMENT
2016/01/19 07:52:15 2.web.shipping-company.com:22 -- STARTING: echo 'restarting webserver'
2016/01/19 07:52:15 1.web.shipping-company.com:22 -- STARTING: echo 'restarting webserver'
2016/01/19 07:52:15 1.workers.shipping-company.com:22 -- STARTING: echo 'restarting workers'
2016/01/19 07:52:15 1.workers.shipping-company.com:22 -- STDOUT -- restarting workers
2016/01/19 07:52:15 1.workers.shipping-company.com:22 -- SUCCESS: "echo 'restarting workers'"
2016/01/19 07:52:15 1.web.shipping-company.com:22 -- STDOUT -- restarting webserver
2016/01/19 07:52:15 1.web.shipping-company.com:22 -- SUCCESS: "echo 'restarting webserver'"
2016/01/19 07:52:16 2.web.shipping-company.com:22 -- STDOUT -- restarting webserver
2016/01/19 07:52:17 2.web.shipping-company.com:22 -- SUCCESS: "echo 'restarting webserver'"
2016/01/19 07:52:18 STAGE RESULT: 1.workers.shipping-company.com:22 - execution of stage POST_DEPLOYMENT successful (100.106365ms)
2016/01/19 07:52:19 Could not find deployment with id 235, database is locked
2016/01/19 07:52:19 Could not find deployment with id 235, database is locked
2016/01/19 07:52:19 Could not find deployment with id 235, database is locked
2016/01/19 07:52:19 STAGE RESULT: 1.web.shipping-company.com:22 - execution of stage POST_DEPLOYMENT successful (141.476426ms)
2016/01/19 07:52:19 STAGE RESULT: 2.web.shipping-company.com:22 - execution of stage POST_DEPLOYMENT successful (151.186347ms)
2016/01/19 07:52:20 Could not find deployment with id 235, database is locked
2016/01/19 07:52:20 Could not find deployment with id 235, database is locked
2016/01/19 07:52:20 Could not find deployment with id 235, database is locked
2016/01/19 07:52:20 Could not find deployment with id 235, database is locked
2016/01/19 07:52:20 Could not find deployment with id 235, database is locked
2016/01/19 07:52:20 Could not find deployment with id 235, database is locked
2016/01/19 07:52:20 Could not find deployment with id 235, database is locked
2016/01/19 07:52:20 Could not find deployment with id 235, database is locked
2016/01/19 07:52:20 Could not find deployment with id 235, database is locked
2016/01/19 07:52:20 STAGE SUCCESS: POST_DEPLOYMENT
2016/01/19 07:52:21 Could not find deployment with id 235, database is locked
2016/01/19 07:52:21 DEPLOYMENT FINISHED: deployment_id=235

The interesting lines are these:

2016/01/19 07:52:20 Could not find deployment with id 235, database is locked

I confirmed that this log output comes from the webhook notifier by changing the log output.

This makes the webhooks feature pretty unreliable since a lot (look at the ratio of log entries vs. "could not find deployment" messages) of possible events are not acted upon.

I'm still not sure how to fix this issue since I don't know yet who holds the lock. Maybe we need to change the architecture of the webhooks feature to reduce the amount of queries? (This won't fix the issue itself, but reduce the probability of it arising: configure the webhook notifier in main() only if any target has webhooks configured, instead of checking on every log message)

Deploy in localhost

I only have one server, I setup applikatoni and web app in this, how to config

Ensure the database is migrated to newest version when booting up

We should check whether the development or production database is migrated to the newest version when booting up.

At the moment this doesn't happen and we might run into run-time errors because some column or table doesn't exist.

The solution probably looks a lot like the one in database_test.go, where we make sure that the test database is set up. See here

But instead of using the YAML file for production database configuration, we should probably keep the -db flag for now, since this would be a breaking change.

The best place to add this check would probably here in main.go, after we connected to the database.

To get started on this issue, read through the Contributing section of the README to see how to set up the Applikatoni for development on your machine and how to send a pull request that fixes this issue.

Deployments overview layout breaks in Firefox with long deployment messages

The deployments overview layout breaks in Firefox when a deployment has "long" deployment message without the necessary linebreaks.

Screenshot:

2015-11-10 at 17 25

And I, um, have no idea why that is.

To get started on this issue, read through the Contributing section of the README to see how to set up the Applikatoni for development on your machine and how to send a pull request that fixes this issue.

If you have any questions: feel free to ask!

Add Slack integration

Besides Flowdock it would be awesome to have Applikatoni send messages to Slack (or to either one of them, depending on the configuration).

As I (not a heavy Slack user) understand it, it's possible to create a Incoming Webhook for a Slack room. That allows to send a message to the room with a single POST request.

That makes it pretty easy to implement and largely similar to the Flowdock integration. So in order to add Slack integration, I guess it's necessary to do the following:

When this is added we can also add it to applikatoni.com.

To get started on this issue, read through the Contributing section of the README to see how to set up the Applikatoni for development on your machine and how to send a pull request that fixes this issue.

If you have any questions or ideas: feel free to ask!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.