November 15, 2012

Beat the binary repository developer (a.k.a. User Plugins)

From our experience with thousands of Artifactory users we know one thing for sure: we don't know better. Every organization does its ALM differently: artifact approval flow, snapshot retention policies, build-to-release flow, governance, needed metadata and much, much more - they are all different. We definitely have some ideas on how the build and deploy process should look, but there are so many things that make your process unique. And that's good. After all, you aren't paid for working within the ideal deployment cycle, but for solving a business problem. At least I hope so.

Acknowledging the fact that we don't know better complicates our lives as creators of a binary repository... and not only by hurting our ego. We want to give you the perfect tool for the job, but how can we do it without dictating to you what your job is? The solution is well known - extensions, a.k.a. add-ons, user plugins, you name it.

"OMG!", you might say. "Code! Joy-joy! Finally, an excuse to hack around!" Or "OMG! Code! It's your job to code those things into your product, not mine!" Look, either way, we don't have much choice, do we? When it comes to customizations, you have to tell Artifactory what you want it to do. We can only try to do our best to make it simple for you. So, we developed a simple DSL.

In this post, I'll show you how easy it is to customize Artifactory with user plugins. Here's the story: you want to prevent downloading of deprecated artifacts. The deprecation information is attached as custom metadata to the artifacts by some quality-assurance mechanism.

Let's say, for example, the artifacts to be banned from download are marked with property "deprecated=true". Artifactory allows you react (with code) to various events in the system. You can find the list of available callbacks in the User Plugins documentation. So, we are going to write Download plugin and the callback we are looking for is the altResponse. In this callback, we can provide an alternative response instead of the one Artifactory was asked for. Here's the code:



 1 download {
 2     altResponse { request, responseRepoPath ->
 3         def deprecated = repositories.getProperties(responseRepoPath)
.getFirst('deprecated')
 4         if (deprecated && deprecated.toBoolean()) {
 5             status = 403
 6             message = 'This artifact was deprecated, please use some 
alternative.'
 7             log.warn "Request was made for deprecated artifact: 
$responseRepoPath.";
 8         }
 9     }
10 }

10 lines of code. That’s all. Let's examine them:
  • It's Groovy! If you are into it, good for you, enjoy! If you aren't, don't worry. It's amost like Java, so you'll read it without problems and will be productive from day 0.
  • It's super-simple. 
Here we go, line by line:
  1. Declares that it's a Download plugin.
  2. Defines the callback type we want (altResponse). When we are implementing alternative response Artifactory provides us with 2 objects:
    • The request, instance of org.artifactory.request.Request. It encapsulates the information about incoming request, such as client details and the information requested
    • And responseRepoPath, instance of org.artifactory.repo.RepoPath. It encapsulates the information about the artifact to be returned.
  3. We want to get the first value of 'deprecated' property, if defined on the artifact represented by responseRepoPath.
  4. If the value exists and it is 'true', 1 or 'y' (as declared by toBoolean()) ...
  5. set return code to 403 (Forbidden) and
  6. set the correct error message and
  7. - optionally - issue warning to the Artifactory log.
Well, that's all. Now, when you saw that the dragon of user plugins isn't so scary, just think about the unique ways you could automate your delivery cycle, apply regulations and checks, or provide your corporate users with better Artifactory experience. Here are some samples and community contributed plugins to ignite your imagination.
Enjoy your build!












8 comments:

  1. One more useful example may be banning outdated SNAPSHOTs. If a component changes its coordinates then other components where the dependency wasn't updated may still pull horribly outdated SNAPSHOTs from Artifactory if they aren't cleaned up for some reason. In this case one can set up a plugin saying "Reject SNAPSHOTs more than a week old".

    ReplyDelete
    Replies
    1. Yeah, great example too (and also very simple to implement). Thanks!

      Delete
  2. this plugin system is excellent well done.

    i'm attempting to use it with a lot of success at the moment for talking through to our Ivy repo for maven builds.

    one thing that would have helped earlier on (hopefully i just didn't find it) would have been a flow diagram showing when all the closure methods are called (in my case particularly for the download closure).

    in hindsight it's pretty obvious - but that knowledge took a little while to grow.

    ReplyDelete
  3. I am evaluating Artifactory Pro (artifactory-2.6.7-13201.noarch)


    and interested in a user plugin that accepts a REST API call describing an artifact using maven GAVC params and returning JSON data (version/url key pairs) in a specific format (unlike the built-in search REST API call). I am limited to use of a GET method, not POST, as this type of request will originate from another technology (not a user or script) and trying to integrate these two components in a larger devops toolchain.

    I have reviewed the executions plugin, i see that POST methods are only allowed. Is there a way to use a GET method for this?

    ReplyDelete
    Replies
    1. Hi Chuck, great question :)
      We are on it: https://issues.jfrog.org/jira/browse/RTFACT-5586

      Delete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Hi,

    Is there any way to hook into or extend the Search capability? I would like to search within .tar.gz, .zip artifacts that contain non-Java files.

    ReplyDelete
    Replies
    1. Hi Arvi, it's there, all you need is searches.archiveEntriesByName

      http://repo.jfrog.org/artifactory/libs-releases-local/org/artifactory/artifactory-papi/%5BRELEASE%5D/artifactory-papi-%5BRELEASE%5D-javadoc.jar!/org/artifactory/search/Searches.html#archiveEntriesByName%28java.lang.String,%20java.lang.String...%29

      Delete