Showing posts with label schedule. Show all posts
Showing posts with label schedule. Show all posts

Thursday, January 9, 2014

Scheduling java unit tests as cron jobs in Heroku

Most folks use Heroku for more than just one application. At any given time, you may have one production application running (with more than one dyno) while tinkering with other dev apps (only one dyno) that are nearing maturity and require regular upkeep for demos to customers.

Heroku will put any dev instances (only one dyno) to sleep after one hour of inactivity and when you start a demo, your first few requests may timeout and customers will be left with a poor impression of how your app performs.

So how do you keep your dev apps awake and available until you have enough market traction to scale them up?
  1. Since you can only have one process named web that gets HTTP requests routed to it, it is impossible to host two war files (prod-war and dev-war) side-by-side without merging them into one war with a common web.xml, spring configuration files, and non-conflicting URL endpoints. This is a lot of work.
  2. A neat workaround that I like to employ is using the Heroku Scheduler from my production instances to run functional unit tests against my dev instances to keep them alive.

  1. Anything that you can run as a one-off task in Heroku via: heroku run 'some command' ... can be run via this scheduler. Simply place 'some command' in the text input field under the task column and change the frequency to hourly.
  2. It may take some trial & error to figure out the right classpath for running your unit tests so I would advise utilizing heroku run 'some command' to figure out what it is that will work for you, before putting anything in the scheduler. Here's a sample:
    heroku run 'java -DunitTestNeedsSomeArg1=value_1 -DunitTestNeedsSomeArg2="complex value 2" -cp target/test-classes:target/myProjectName/WEB-INF/classes:"target/dependency/*":"target/myProjectName/WEB-INF/lib/*" org.junit.runner.JUnitCore com.my.test.TestRestRequest'
    • You may also realize that certain jars are available locally but missing from Heroku because you defined them as test-scoped in your maven project's pom.xml file ... you will need to remove the scope limitation in order to get the unit tests working on the Heroku side.
    • Once you have something working, then just cut/paste the content inside the single-quotes as the scheduled task to run hourly.
  3. When looking through the logs, any lines spit out by the hourly one-off task will be prefixed like so:

Thursday, August 12, 2010

Hudson: Deploy to container based on a schedule

Challenges:
1) The schedules in Hudson jobs apply to Build Triggers. There isn't any separate place to specify a schedule for Post Build Actions like Deploy war/ear to a container.
2) A job without any build information and only deployment info, will not run the Post Build Actions.
3) Cloning an existing job only for the sake of changing the schedule for deployment will mean throwing off the Hudson build numbers placed inside of your artifacts. Even if you un-check the Archive the artifacts option in the cloned job's configuration it will be meaningless ... the clone's artifacts will continue to be published/installed into your local repository over the original ones by maven itself.
4) The clone might get associated as a downstream job and therefore would end up running whenever your original job had finished running. This would make the custom schedule pointless!

Solutions:
  1. Clone the job for which you want to schedule deployments.
  2. If you are using a pom.xml which is set up to use child modules then just directly have the cloned configuration reference the pom.xml of the child which actually puts together the war/ear artifact ... instead of the main parent project's pom.xml file.
  3. Uncheck the following option from the clone's configuration to decouple the upstream/downstream relationship if there is any:
    Build whenever a SNAPSHOT dependency is built
  4. Within the pom.xml file which actually generates your war/ear artifact, place the cargo plugin and its configuration to deploy remotely.
    1. It wouldn't hurt to start with the original documentation for reference on how to do this exactly. But it doesn't talk about the most crucial part which is the use of the cargo.tomcat.manager.url property which is covered in this blog. It is a very good example on how to do this.
    2. Since you are going to commit the altered pom.xml file you may be worried about the side-effects on your original job that performs the build. Well if you build only runs up to the install phase, you don't have to worry about anything as cargo goals are not part of the maven lifecycle up until that point. Everything about your original build will continue to work as it did.
    3. You can also check This build is parameterized and specify string parameters so that you don't have to go changing your pom.xml everytime that your source location of the war/ear file changes or the tomcat that you want to deploy to changes. Here's a sample (+/-)
        <properties>
          <artifactFileLocation>${ARTIFACT_FILE_LOCATION}</artifactFileLocation>
          <remoteTomcat>${REMOTE_TOMCAT_URL}</remoteTomcat>
        </properties>
        ...
            <plugin>
              <groupId>org.codehaus.cargo</groupId>
              <artifactId>cargo-maven2-plugin</artifactId>
              <configuration>
                <!-- Container configuration -->
                <container>
                  <containerId>tomcat6x</containerId>
                  <type>remote</type>
                </container>
                <!-- Configuration to use with the container -->
                <configuration>
                  <type>runtime</type>
                  <properties>
                    <cargo.tomcat.manager.url>${remoteTomcat}/manager</cargo.tomcat.manager.url>
                    <cargo.remote.username>username</cargo.remote.username>
                    <cargo.remote.password>password</cargo.remote.password>
                  </properties>
                </configuration>
                <!-- Deployer configuration -->
                <deployer>
                  <type>remote</type>
                  <deployables>
                    <deployable>
                      <location>${artifactFileLocation}</location>
                      <pingURL>${remoteTomcat}</pingURL>
                    </deployable>
                  </deployables>
                </deployer>
              </configuration>
            </plugin>
    4. Come to think of it, username and password would also benefit from being parameterized.
  5. Take advantage of the new configuration in your artifact generating pom.xml file by configuring the following goal in Hudson for the cloned job:
    org.codehaus.cargo:cargo-maven2-plugin:deployer-redeploy