Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom configuration is replaced with the default one during package install #378

Closed
artempyanykh opened this issue Oct 15, 2014 · 9 comments
Labels
documentation Documentation should be extended or updated

Comments

@artempyanykh
Copy link
Contributor

I'm not sure if what I'm doing is the right way of doing things. If there is a better way, please tell me.

However the problem is that when I install a deb. package with my Play 2 app, I get my custom production application.conf replaced with the default application.conf from sources. The whole story is as follows.

I have a Chef-repo with the description of my production configuration. As a part of provisioning process a custom application.conf with production settings (db, migrations, etc.) is placed at /etc/my_app/application.conf.

When the provisioning of production VM is completed, I continue with the my_app.deb package installation. I expect that after the package is installed, the application would be running with my production config. However during the installation production /etc/my_app/application.conf is replaced with default application.conf from the sources. Because of that application server can't start (and I actually get a flapping service with a stream of "can't connect to database[default]" in /var/upstart/my_app.log)
The same thing If I install a newer version of a package.

I really want to do the initial VM setup with Chef (including production application.conf generation), and want my production config to be persisted between package install/reinstall, but I don't understand where shall I put my config file and how I shall start a service, so the things would run smoothly.

@muuki88
Copy link
Contributor

muuki88 commented Oct 15, 2014

Actually you have three options. Use the one suited best for your use case
(and I'm keen to know which one you choose and why)

application.conf => reference.conf

The easiest solution would be to rename your application.conf to reference.conf.
Thus it's only used as a fallback which may already solve the problem.

Read about the standard behavior of typesafe config if you are not sure what's happening.

Specify config in etc-default

The Typesafe Config provides a way to specify where it should search for the configuration.

You can provide a etc-default in src/templates/ and specify the location of your config, e.g.

-Dconfig.file=/usr/share/${{app_name}}/conf/production.conf

# if you use play, you should specifiy a pid dir as well
-Dpidfile.path=/var/run/${{app_name}}/play.pid

Chef now provides a production.conf and play AFAIK should pick it up correctly.

This example uses -Dconfig.resource=production.conf, which may also work.

exclude application.conf from debian

The solution is actually pretty simple. Just don't package your application.conf.
There is a complete section in the docs about mappings.

The example is taken from Filter/Remove mappings. Note that this will affect all packaging types (as we filter the universal mappings).

mappings in Universal := {
    // universalMappings: Seq[(File,String)]
    val universalMappings = (mappings in Universal).value
    // removing means filtering
    universalMappings filter {
        case (file, name) =>  ! name.endsWith("application.conf")
    }
}

If you want a concise version

mappings in Universal := {
    (mappings in Universal).value filter {
        case (file, name) =>  ! name.endsWith("application.conf")
    }
}

The result will be a debian package that doesn't contain an application.conf thus not able to override anything. Your application will not work when not provisioned with chef.

@artempyanykh
Copy link
Contributor Author

@muuki88 that was a great answer, it helped me a lot! Thank you!

TLDR; It was necessary to use a mix of 'reference.conf' and 'etc-default' options. More detailed description below.


I don't really like the idea of completely excluding configuration from the package (I feel like every good package should have reference configuration file). And also this option would involve slight changes in CI build plan configuration. Not much work, but nevertheless.

I was up to "application.conf => reference.conf". It seemed like a good fit allowing me to provide my own production application.conf and also providing a fallback config so CI builds won't go red. However just dropping application.conf to /usr/share/my_app/conf/ wasn't enough. Since application.conf wasn't on classpath Play server failed with the following error:

Play server process ID is 6953
Oops, cannot start the server.
Configuration error: Configuration error[application: application.conf: java.io.IOException: resource not found on classpath: application.conf, application.json: java.io.IOException: resource not found on classpath: application.json, application.properties: java.io.IOException: resource not found on classpath: application.properties]

I'm not sure why, but init script doesn't signal any errors in this case. It just said:

* Starting my_app [ OK ]

Is it expected, or I'm just doing something wrong?


Since I need etc-default for the play pid-file configuration I decided to add the following line to etc-default:

-Dconfig.file=/etc/${{app_name}}/application.conf

That fixed a problem, and now application starts smoothly.

P.S. It feels like we need more detailed docs on Play native packaging and deployment then what we have at the moment. It is not a very complicated stuff, but it is confusing for new people like me when nothing works and you don't know if it is supposed to be so, or you just doing something wrong.

@muuki88 muuki88 added the documentation Documentation should be extended or updated label Oct 17, 2014
@muuki88
Copy link
Contributor

muuki88 commented Oct 17, 2014

Thanks for the detailed feedback as well :)

The problem that service your-app start not always yields if something went wrong is unfortunately not an easy problem to tackle. Different OS and SystemLoader work all in a different way. E.g. Upstart uses the debian start-stop-daemon, CentOS/Fedora use nohup & and applications may come or not with an own PID handling (like play). Point is that the application did start successfully, but internally the application failed, which is not visible to the outside at first.

It feels like we need more detailed docs on Play native packaging and deployment then what we have at the moment. It is not a very complicated stuff, but it is confusing for new people like me when nothing works and you don't know if it is supposed to be so, or you just doing something wrong.

You did are really good job describing your solution. I'll be happy to merge a pull request :)
Here is a guideline how to build the docs

@artempyanykh
Copy link
Contributor Author

You did are really good job describing your solution. I'll be happy to merge a pull request :)
Here is a guideline how to build the docs

Deal! I could describe deb- and rpm- packaging of Play 2 apps and place it in a section "Packaging Play 2 Applications" under "Advanced Topics". Does it seem reasonable?

@muuki88
Copy link
Contributor

muuki88 commented Oct 17, 2014

Sounds like a plan :) Looking forward to it

@cbornet
Copy link

cbornet commented Mar 24, 2015

Hello @muuki88,

I think this is more a bug : the debian conffile in target/DEBIAN/conffile gets filled with a /usr/share/{app}/conf//application.log.
Because of the double-slash in the path, the file is not considered as a config file and thus it gets overriden by dpkg.
The issue comes from Play! : https://github.com/playframework/playframework/blob/cc308ec87413081826d9a91d9cee10591880eaba/framework/src/sbt-plugin/src/main/scala/play/sbt/PlaySettings.scala

mappings in Universal ++= {
    val confDirectoryLen = confDirectory.value.getCanonicalPath.length
    val pathFinder = confDirectory.value ** ("*" -- "routes")
    pathFinder.get map {
        confFile: File =>
        confFile -> ("conf/" + confFile.getCanonicalPath.substring(confDirectoryLen))
    }
}

confDirectory is "/conf" so confFile.getCanonicalPath.substring(confDirectoryLen) is "/application.conf"

My workaround is to post process the mappings in build.sbt

mappings in Universal := {
    (mappings in Universal).value map {
        case (file,name) if name startsWith "conf//" => (file, "conf/" + name.substring(6))
        case (file,name) => (file,name)
    }
}

But maybe there is a better solution ?

@muuki88
Copy link
Contributor

muuki88 commented Mar 25, 2015

That's definitely a bug! Adding application.conf as debian conffile seems reasonable, however the path should be correct.

UPDATE

Sorry, I missread. application.log is added. This should not be the case!

@muuki88
Copy link
Contributor

muuki88 commented Mar 31, 2015

Did you open a bug for this on the playframework?

cc @jroper

@jroper
Copy link
Member

jroper commented Apr 8, 2015

This is probably fixed in Play, here:

playframework/playframework#4116

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Documentation should be extended or updated
Projects
None yet
Development

No branches or pull requests

4 participants