Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing gradle plugin with nested extension objects

Tags:

plugins

gradle

I am working on a gradle plugin (i dont know groovy, and just started using gradle) that integrates with Asgard (from netflix). I want my extension object to look like this for the user:

asgard {
  url = "http://asgard"
  regions {
    "us-east-1" {
      autoScaling {
        {
          devPhase = "test"
          min = 3
          max = 6
          availabilityZones = ["us-east-1a", "us-east-1b", "us-east-1c"]
          ami = "Base AMI 2013-07-11"
          instanceType = "m3.xlarge"
          securityGroups = ["base", "test-application"]
        }
      }
    }
  }
}

Or something close to that. I have been able to get close to this by making autoScaling a List, but when ever i try to get a property from that class it seems to return a dynamic property and never the value. Here is the below starter plugin:

import org.gradle.internal.reflect.Instantiator

class AsgardPlugin implements Plugin<Project> {
  void apply(Project project) {
    project.extensions.create("asgard", AsgardPluginExtension, project)
    project.asgard.extensions.regions = project.container(AsgardRegion) {String name ->
      AsgardRegion region = project.gradle.services.get(Instantiator).newInstance(AsgardRegion, name)
      assert region instanceof ExtensionAware

      region.extensions.add("autoScaling", project.container(AsgardAutoScaling))

      return region
    }


    project.task('displayConfigs') << {
      if(project.asgard.applicationName == null) project.asgard.applicationName = project.name
      println "Asgard URL $project.asgard.url"
      println "Application name $project.asgard.applicationName"
      println "Runs on regions..."
      project.asgard.regions.each() {region ->
        println "\tRegion $region.name"
        println "\tAutoScaling groups..."
        region.autoScaling.each() {asg ->
          println "\t\tdevPhase $asg"
          println "\t\tdevPhase $asg.devPhase"
          println "\t\tdevPhase $asg.get('devPhase')"
        }
      }
    }
  }
}

class AsgardPluginExtension {
  String url = "http://asgard.sisa.samsung.com"
  String applicationName

  AsgardPluginExtension(Project project) {
    applicationName = project.name
  }
}

class AsgardRegion {
  def String name
  List<AsgardAutoScaling> autoScaling
  AsgardRegion(String name) { this.name = name }
}

class AsgardAutoScaling {
  String devPhase
  int min
  int max
  List<String> availabilityZones
  String ami
  String instanceType
  String sshKey
  List<String> securityGroups
}

When I run the task, this is what I see in the logs:

$  ./gradlew displayConfigs
:displayConfigs
Asgard URL http://asgard
Application name gradle-asgard-plugin
Runs on regions...
        Region us-east-1
        AutoScaling groups...
Creating properties on demand (a.k.a. dynamic properties) has been deprecated and is scheduled to be removed in Gradle 2.0. Please read http://gradle.org/docs/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html for information on the replacement for dynamic properties.
Deprecated dynamic property: "devPhase" on "AsgardRegion_Decorated@65087be0", value: "test".
Deprecated dynamic property: "min" on "AsgardRegion_Decorated@65087be0", value: "3".
Deprecated dynamic property: "max" on "AsgardRegion_Decorated@65087be0", value: "6".
Deprecated dynamic property: "availabilityZones" on "AsgardRegion_Decorated@65087be0", value: "[us-east-1a, us-east-1...".
Deprecated dynamic property: "ami" on "AsgardRegion_Decorated@65087be0", value: "Base AMI 2013-07-11".
Deprecated dynamic property: "instanceType" on "AsgardRegion_Decorated@65087be0", value: "m3.xlarge".
Deprecated dynamic property: "securityGroups" on "AsgardRegion_Decorated@65087be0", value: "[base, test-application]".
                devPhase
                devPhase test
                devPhase AsgardRegion_Decorated@14860315('devPhase')

BUILD SUCCESSFUL

Total time: 1.996 secs

Am I going about this all wrong? If not how can I get the value from the dynamic property (tried get and value, but those don't seem to work).

Thanks for your time reading this.

EDIT based off @peter-niederwieser feedback

Thanks peter-niederwieser for your hints. After taking your advice and looking at how the closures work I think i have a better understanding of how to set out to do what I wanted to try out.

Here is the syntax of the plugin now:

asgard {
  url = "http://asgard"
  regions {
    "us-east-1" {
      autoScaling {
          devPhase = "test"
          min = 3
          max = 6
          availabilityZones = ["a", "b", "c"]
          ami = "Base AMI 2013-07-11"
          instanceType = "m3.xlarge"
          securityGroups = ["base", "test-application"]
      }
      autoScaling {
        devPhase = "stage"
      }
    }

    "us-west-1" {
      autoScaling {
        devPhase = "test"
      }
      autoScaling {
        devPhase = "stage"
      }
    }

  }
} 

The code to support this can be found here:

import org.gradle.internal.reflect.Instantiator

class AsgardPlugin implements Plugin<Project> {
  void apply(Project project) {
    project.extensions.create("asgard", AsgardPluginExtension, project)
    project.asgard.extensions.regions = project.container(AsgardRegion) {String name ->
      AsgardRegion region = project.gradle.services.get(Instantiator).newInstance(AsgardRegion, name, project)
      assert region instanceof ExtensionAware

      return region
    }


    project.task('displayConfigs') << {
      if(project.asgard.applicationName == null) project.asgard.applicationName = project.name
      println "Asgard URL $project.asgard.url"
      println "Application name $project.asgard.applicationName"
      println "Runs on regions..."
      project.asgard.regions.each() {region ->
        println "\tRegion $region.name"
        println "\tAutoScaling groups..."
        region.autoScaling.each() {asg ->
          println "\t\tdevPhase $asg.devPhase"
        }
      }
    }
  }
}

class AsgardPluginExtension {
  String urlgg
  String applicationName

  AsgardPluginExtension(Project project) {
    applicationName = project.name
  }
}

class AsgardRegion {
  String name
  Project project
  List<AsgardAutoScaling> autoScaling = []

  AsgardRegion(String name, Project project) {
    this.name = name
    this.project = project
  }

  void autoScaling(Closure closure) {
    def asg = new AsgardAutoScaling()
    project.configure(asg, closure)
    autoScaling.add(asg)
  }
}

class AsgardAutoScaling {
  String devPhase
  int min
  int max
  List<String> availabilityZones
  String ami
  String instanceType
  String sshKey
  List<String> securityGroups
}

Here is the output I see when I run:

$  ./gradlew displayConfigs
:displayConfigs
Asgard URL http://asgard
Application name gradle-asgard-plugin
Runs on regions...
        Region us-east-1
        AutoScaling groups...
                devPhase test
                devPhase stage
        Region us-west-1
        AutoScaling groups...
                devPhase test
                devPhase stage

BUILD SUCCESSFUL

Total time: 1.929 secs
like image 779
ekaqu Avatar asked Jul 13 '13 03:07

ekaqu


1 Answers

Some hints:

  • There is no need to add an autoScaling extension to region because the AsgardRegion class already has an autoScaling property. You just need to initialize that property and perhaps add a void autoScaling(Closure closure) { project.configure(autoScaling, closure) } convenience method to support the autoScaling { ... } syntax.

  • project.container creates a NamedDomainObjectContainer. The literal syntax for this container is someName { ... }; otherName { ... }. There is no "plain list" syntax.

  • Your test code sets devPhase etc. for the autoScaling container, which doesn't have these properties. That's why you get the dynamic properties warnings. The innermost { ... } is a block (not a closure) and is redundant.

To solve the list problem, either define a name property for AsgardAutoScaling and use the regular NamedDomainObjectContainer literal syntax, or don't use NamedDomainObjectContainer and implement your own syntax. For example, you could declare an autoScaling method on AsgardRegion that accepts a closure, creates an instance of AsgardAutoScaling, and adds it to the list.

like image 132
Peter Niederwieser Avatar answered Oct 31 '22 01:10

Peter Niederwieser