diff --git a/crd-generator/api/pom.xml b/crd-generator/api/pom.xml
index bc8690b677..f367dae773 100644
--- a/crd-generator/api/pom.xml
+++ b/crd-generator/api/pom.xml
@@ -36,6 +36,11 @@
compile
+
+ io.fabric8
+ generator-annotations
+
+
io.fabric8
kubernetes-model-common
diff --git a/crd-generator/api/src/main/java/io/fabric8/crd/generator/AbstractJsonSchema.java b/crd-generator/api/src/main/java/io/fabric8/crd/generator/AbstractJsonSchema.java
index 59fbfd70d6..747ad6ad7c 100644
--- a/crd-generator/api/src/main/java/io/fabric8/crd/generator/AbstractJsonSchema.java
+++ b/crd-generator/api/src/main/java/io/fabric8/crd/generator/AbstractJsonSchema.java
@@ -17,7 +17,6 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import io.fabric8.crd.generator.annotation.SchemaSwap;
import io.fabric8.crd.generator.utils.Types;
import io.fabric8.kubernetes.api.model.Duration;
import io.fabric8.kubernetes.api.model.IntOrString;
@@ -60,6 +59,8 @@ public abstract class AbstractJsonSchema {
protected static final TypeDef DATE = TypeDef.forName(Date.class.getName());
protected static final TypeRef DATE_REF = DATE.toReference();
+ private static final String VALUE = "value";
+
private static final String INT_OR_STRING_MARKER = "int_or_string";
private static final String STRING_MARKER = "string";
private static final String INTEGER_MARKER = "integer";
@@ -78,6 +79,11 @@ public abstract class AbstractJsonSchema {
public static final String ANNOTATION_JSON_IGNORE = "com.fasterxml.jackson.annotation.JsonIgnore";
public static final String ANNOTATION_JSON_ANY_GETTER = "com.fasterxml.jackson.annotation.JsonAnyGetter";
public static final String ANNOTATION_JSON_ANY_SETTER = "com.fasterxml.jackson.annotation.JsonAnySetter";
+ public static final String ANNOTATION_MIN = "io.fabric8.generator.annotation.Min";
+ public static final String ANNOTATION_MAX = "io.fabric8.generator.annotation.Max";
+ public static final String ANNOTATION_PATTERN = "io.fabric8.generator.annotation.Pattern";
+ public static final String ANNOTATION_NULLABLE = "io.fabric8.generator.annotation.Nullable";
+ public static final String ANNOTATION_REQUIRED = "io.fabric8.generator.annotation.Required";
public static final String ANNOTATION_NOT_NULL = "javax.validation.constraints.NotNull";
public static final String ANNOTATION_SCHEMA_FROM = "io.fabric8.crd.generator.annotation.SchemaFrom";
public static final String ANNOTATION_SCHEMA_SWAP = "io.fabric8.crd.generator.annotation.SchemaSwap";
@@ -110,6 +116,51 @@ public static String getSchemaTypeFor(TypeRef typeRef) {
return type;
}
+ protected static class SchemaPropsOptions {
+ final Optional min;
+ final Optional max;
+ final Optional pattern;
+ final boolean nullable;
+ final boolean required;
+
+ SchemaPropsOptions() {
+ min = Optional.empty();
+ max = Optional.empty();
+ pattern = Optional.empty();
+ nullable = false;
+ required = false;
+ }
+
+ public SchemaPropsOptions(Optional min, Optional max, Optional pattern,
+ boolean nullable, boolean required) {
+ this.min = min;
+ this.max = max;
+ this.pattern = pattern;
+ this.nullable = nullable;
+ this.required = required;
+ }
+
+ public Optional getMin() {
+ return min;
+ }
+
+ public Optional getMax() {
+ return max;
+ }
+
+ public Optional getPattern() {
+ return pattern;
+ }
+
+ public boolean isNullable() {
+ return nullable;
+ }
+
+ public boolean getRequired() {
+ return nullable;
+ }
+ }
+
/**
* Creates the JSON schema for the particular {@link TypeDef}. This is template method where
* sub-classes are supposed to provide specific implementations of abstract methods.
@@ -264,7 +315,15 @@ private T internalFromImpl(TypeDef definition, Set visited, List min;
+ private Optional max;
+ private Optional pattern;
+ private boolean nullable;
private boolean required;
private boolean ignored;
private boolean preserveUnknownFields;
@@ -297,6 +360,10 @@ private PropertyOrAccessor(Collection annotations, String name, S
this.name = name;
this.propertyName = propertyName;
type = isMethod ? "accessor" : "field";
+
+ min = Optional.empty();
+ max = Optional.empty();
+ pattern = Optional.empty();
}
static PropertyOrAccessor fromProperty(Property property) {
@@ -310,17 +377,34 @@ static PropertyOrAccessor fromMethod(Method method, String propertyName) {
public void process() {
annotations.forEach(a -> {
switch (a.getClassRef().getFullyQualifiedName()) {
+ case ANNOTATION_NULLABLE:
+ nullable = true;
+ break;
+ case ANNOTATION_MAX:
+ max = Optional.of((Double) a.getParameters().get(VALUE));
+ break;
+ case ANNOTATION_MIN:
+ min = Optional.of((Double) a.getParameters().get(VALUE));
+ break;
+ case ANNOTATION_PATTERN:
+ pattern = Optional.of((String) a.getParameters().get(VALUE));
+ break;
case ANNOTATION_NOT_NULL:
+ LOGGER.warn("Annotation: {} on property: {} is deprecated. Please use: {} instead", ANNOTATION_NOT_NULL, name,
+ ANNOTATION_REQUIRED);
+ required = true;
+ break;
+ case ANNOTATION_REQUIRED:
required = true;
break;
case ANNOTATION_JSON_PROPERTY:
- final String nameFromAnnotation = (String) a.getParameters().get("value");
+ final String nameFromAnnotation = (String) a.getParameters().get(VALUE);
if (!Strings.isNullOrEmpty(nameFromAnnotation) && !propertyName.equals(nameFromAnnotation)) {
renamedTo = nameFromAnnotation;
}
break;
case ANNOTATION_JSON_PROPERTY_DESCRIPTION:
- final String descriptionFromAnnotation = (String) a.getParameters().get("value");
+ final String descriptionFromAnnotation = (String) a.getParameters().get(VALUE);
if (!Strings.isNullOrEmpty(descriptionFromAnnotation)) {
description = descriptionFromAnnotation;
}
@@ -343,6 +427,22 @@ public String getRenamedTo() {
return renamedTo;
}
+ public boolean isNullable() {
+ return nullable;
+ }
+
+ public Optional getMax() {
+ return max;
+ }
+
+ public Optional getMin() {
+ return min;
+ }
+
+ public Optional getPattern() {
+ return pattern;
+ }
+
public boolean isRequired() {
return required;
}
@@ -387,6 +487,10 @@ private static class PropertyFacade {
private final Set matchedSchemaSwaps;
private String renamedTo;
private String description;
+ private Optional min;
+ private Optional max;
+ private Optional pattern;
+ private boolean nullable;
private boolean required;
private boolean ignored;
private boolean preserveUnknownFields;
@@ -414,6 +518,9 @@ public PropertyFacade(Property property, Map potentialAccessors,
if (method != null) {
propertyOrAccessors.add(PropertyOrAccessor.fromMethod(method, name));
}
+ min = Optional.empty();
+ max = Optional.empty();
+ pattern = Optional.empty();
}
public Property process() {
@@ -450,6 +557,22 @@ public Property process() {
}
}
+ if (p.getMin().isPresent()) {
+ min = p.getMin();
+ }
+
+ if (p.getMax().isPresent()) {
+ max = p.getMax();
+ }
+
+ if (p.getPattern().isPresent()) {
+ pattern = p.getPattern();
+ }
+
+ if (p.isNullable()) {
+ nullable = true;
+ }
+
if (p.isRequired()) {
required = true;
} else if (p.isIgnored()) {
@@ -502,7 +625,7 @@ private String extractUpdatedNameFromJacksonPropertyIfPresent(Property property)
.findAny()
// if we found an annotated accessor, override the property's name if needed
.map(a -> {
- final String fromAnnotation = (String) a.getParameters().get("value");
+ final String fromAnnotation = (String) a.getParameters().get(VALUE);
if (!Strings.isNullOrEmpty(fromAnnotation) && !name.equals(fromAnnotation)) {
return fromAnnotation;
} else {
@@ -527,7 +650,7 @@ private String extractUpdatedNameFromJacksonPropertyIfPresent(Property property)
* @param builder the builder representing the schema being built
* @param schema the built schema for the property being added
*/
- public abstract void addProperty(Property property, B builder, T schema);
+ public abstract void addProperty(Property property, B builder, T schema, SchemaPropsOptions options);
/**
* Finishes up the process by actually building the final JSON schema based on the provided
diff --git a/crd-generator/api/src/main/java/io/fabric8/crd/generator/v1/JsonSchema.java b/crd-generator/api/src/main/java/io/fabric8/crd/generator/v1/JsonSchema.java
index 9875c5b9f3..01ab888288 100644
--- a/crd-generator/api/src/main/java/io/fabric8/crd/generator/v1/JsonSchema.java
+++ b/crd-generator/api/src/main/java/io/fabric8/crd/generator/v1/JsonSchema.java
@@ -22,6 +22,7 @@
import io.sundr.model.Property;
import io.sundr.model.TypeDef;
import io.sundr.model.TypeRef;
+
import java.util.List;
public class JsonSchema extends AbstractJsonSchema {
@@ -29,11 +30,11 @@ public class JsonSchema extends AbstractJsonSchema requir
@Override
protected JSONSchemaProps arrayLikeProperty(JSONSchemaProps schema) {
return new JSONSchemaPropsBuilder()
- .withType("array")
- .withNewItems()
- .withSchema(schema)
- .and()
- .build();
+ .withType("array")
+ .withNewItems()
+ .withSchema(schema)
+ .and()
+ .build();
}
@Override
protected JSONSchemaProps mapLikeProperty(JSONSchemaProps schema) {
return new JSONSchemaPropsBuilder()
- .withType("object")
- .withNewAdditionalProperties()
- .withSchema(schema)
- .endAdditionalProperties()
- .build();
+ .withType("object")
+ .withNewAdditionalProperties()
+ .withSchema(schema)
+ .endAdditionalProperties()
+ .build();
}
@Override
@@ -108,7 +117,7 @@ protected JSONSchemaProps enumProperty(JsonNode... enumValues) {
@Override
protected JSONSchemaProps addDescription(JSONSchemaProps schema, String description) {
return new JSONSchemaPropsBuilder(schema)
- .withDescription(description)
- .build();
+ .withDescription(description)
+ .build();
}
}
diff --git a/crd-generator/api/src/main/java/io/fabric8/crd/generator/v1beta1/JsonSchema.java b/crd-generator/api/src/main/java/io/fabric8/crd/generator/v1beta1/JsonSchema.java
index 06729c515c..732a6514d5 100644
--- a/crd-generator/api/src/main/java/io/fabric8/crd/generator/v1beta1/JsonSchema.java
+++ b/crd-generator/api/src/main/java/io/fabric8/crd/generator/v1beta1/JsonSchema.java
@@ -22,6 +22,7 @@
import io.sundr.model.Property;
import io.sundr.model.TypeDef;
import io.sundr.model.TypeRef;
+
import java.util.List;
public class JsonSchema extends AbstractJsonSchema {
@@ -29,21 +30,21 @@ public class JsonSchema extends AbstractJsonSchema requir
@Override
protected JSONSchemaProps arrayLikeProperty(JSONSchemaProps schema) {
return new JSONSchemaPropsBuilder()
- .withType("array")
- .withNewItems()
- .withSchema(schema)
- .and()
- .build();
+ .withType("array")
+ .withNewItems()
+ .withSchema(schema)
+ .and()
+ .build();
}
@Override
protected JSONSchemaProps mapLikeProperty(JSONSchemaProps schema) {
return new JSONSchemaPropsBuilder()
- .withType("object")
- .withNewAdditionalProperties()
- .withSchema(schema)
- .endAdditionalProperties()
- .build();
+ .withType("object")
+ .withNewAdditionalProperties()
+ .withSchema(schema)
+ .endAdditionalProperties()
+ .build();
}
@Override
protected JSONSchemaProps singleProperty(String typeName) {
return new JSONSchemaPropsBuilder()
- .withType(typeName)
- .build();
+ .withType(typeName)
+ .build();
}
@Override
@@ -111,7 +120,7 @@ protected JSONSchemaProps enumProperty(JsonNode... enumValues) {
@Override
protected JSONSchemaProps addDescription(JSONSchemaProps schema, String description) {
return new JSONSchemaPropsBuilder(schema)
- .withDescription(description)
- .build();
+ .withDescription(description)
+ .build();
}
}
diff --git a/crd-generator/api/src/test/java/io/fabric8/crd/example/annotated/AnnotatedSpec.java b/crd-generator/api/src/test/java/io/fabric8/crd/example/annotated/AnnotatedSpec.java
index a45c891f1b..7ff0b398ed 100644
--- a/crd-generator/api/src/test/java/io/fabric8/crd/example/annotated/AnnotatedSpec.java
+++ b/crd-generator/api/src/test/java/io/fabric8/crd/example/annotated/AnnotatedSpec.java
@@ -18,8 +18,12 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
+import io.fabric8.generator.annotation.Max;
+import io.fabric8.generator.annotation.Min;
+import io.fabric8.generator.annotation.Nullable;
+import io.fabric8.generator.annotation.Pattern;
+import io.fabric8.generator.annotation.Required;
-import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
public class AnnotatedSpec {
@@ -29,10 +33,16 @@ public class AnnotatedSpec {
private int foo;
@JsonProperty
private String unnamed;
+ private int min;
+ private int max;
+ private String singleDigit;
+ private String nullable;
@NotNull
private boolean emptySetter;
+ @Required
+ private boolean emptySetter2;
private AnnotatedEnum anEnum;
- @Min(0) // a non-string value attribute
+ @javax.validation.constraints.Min(0) // a non-string value attribute
private int sizedField;
@JsonIgnore
@@ -56,11 +66,36 @@ public boolean getIgnoredBar() {
return ignoredBar;
}
+ @Max(5.0)
+ public int getMax() {
+ return 1;
+ }
+
+ @Min(-5)
+ public int getMin() {
+ return 1;
+ }
+
+ @Pattern("\\b[1-9]\\b")
+ public String getSingleDigit() {
+ return "1";
+ }
+
+ @Nullable
+ public String getNullable() {
+ return null;
+ }
+
@JsonProperty
public void setEmptySetter(boolean emptySetter) {
this.emptySetter = emptySetter;
}
+ @JsonProperty
+ public void setEmptySetter2(boolean emptySetter2) {
+ this.emptySetter2 = emptySetter2;
+ }
+
public enum AnnotatedEnum {
non,
@JsonProperty("oui")
diff --git a/crd-generator/api/src/test/java/io/fabric8/crd/generator/v1/JsonSchemaTest.java b/crd-generator/api/src/test/java/io/fabric8/crd/generator/v1/JsonSchemaTest.java
index a0715d3cd4..1790fb63b3 100644
--- a/crd-generator/api/src/test/java/io/fabric8/crd/generator/v1/JsonSchemaTest.java
+++ b/crd-generator/api/src/test/java/io/fabric8/crd/generator/v1/JsonSchemaTest.java
@@ -18,10 +18,10 @@
import com.fasterxml.jackson.databind.JsonNode;
import io.fabric8.crd.example.annotated.Annotated;
import io.fabric8.crd.example.basic.Basic;
+import io.fabric8.crd.example.extraction.Extraction;
import io.fabric8.crd.example.extraction.IncorrectExtraction;
import io.fabric8.crd.example.extraction.IncorrectExtraction2;
import io.fabric8.crd.example.json.ContainingJson;
-import io.fabric8.crd.example.extraction.Extraction;
import io.fabric8.crd.example.person.Person;
import io.fabric8.crd.generator.utils.Types;
import io.fabric8.kubernetes.api.model.apiextensions.v1.JSONSchemaProps;
@@ -44,16 +44,16 @@ void shouldCreateJsonSchemaFromClass() {
Map properties = schema.getProperties();
assertEquals(7, properties.size());
final List personTypes = properties.get("type").getEnum().stream().map(JsonNode::asText)
- .collect(Collectors.toList());
+ .collect(Collectors.toList());
assertEquals(2, personTypes.size());
assertTrue(personTypes.contains("crazy"));
assertTrue(personTypes.contains("crazier"));
final Map addressProperties = properties.get("addresses").getItems()
- .getSchema().getProperties();
+ .getSchema().getProperties();
assertEquals(5, addressProperties.size());
final List addressTypes = addressProperties.get("type").getEnum().stream()
- .map(JsonNode::asText)
- .collect(Collectors.toList());
+ .map(JsonNode::asText)
+ .collect(Collectors.toList());
assertEquals(2, addressTypes.size());
assertTrue(addressTypes.contains("home"));
assertTrue(addressTypes.contains("work"));
@@ -80,7 +80,7 @@ void shouldAugmentPropertiesSchemaFromAnnotations() {
assertEquals(2, properties.size());
final JSONSchemaProps specSchema = properties.get("spec");
Map spec = specSchema.getProperties();
- assertEquals(6, spec.size());
+ assertEquals(11, spec.size());
// check descriptions are present
assertTrue(spec.containsKey("from-field"));
@@ -97,10 +97,35 @@ void shouldAugmentPropertiesSchemaFromAnnotations() {
assertNull(spec.get("emptySetter").getDescription());
assertTrue(spec.containsKey("anEnum"));
+ final JSONSchemaProps min = spec.get("min");
+ assertEquals(-5.0, min.getMinimum());
+ assertNull(min.getMaximum());
+ assertNull(min.getPattern());
+ assertNull(min.getNullable());
+
+ final JSONSchemaProps max = spec.get("max");
+ assertEquals(5.0, max.getMaximum());
+ assertNull(max.getMinimum());
+ assertNull(max.getPattern());
+ assertNull(max.getNullable());
+
+ final JSONSchemaProps pattern = spec.get("singleDigit");
+ assertEquals("\\b[1-9]\\b", pattern.getPattern());
+ assertNull(pattern.getMinimum());
+ assertNull(pattern.getMaximum());
+ assertNull(pattern.getNullable());
+
+ final JSONSchemaProps nullable = spec.get("nullable");
+ assertTrue(nullable.getNullable());
+ assertNull(nullable.getMinimum());
+ assertNull(nullable.getMaximum());
+ assertNull(nullable.getPattern());
+
// check required list, should register properties with their modified name if needed
final List required = specSchema.getRequired();
- assertEquals(2, required.size());
+ assertEquals(3, required.size());
assertTrue(required.contains("emptySetter"));
+ assertTrue(required.contains("emptySetter2"));
assertTrue(required.contains("from-getter"));
// check the enum values
diff --git a/doc/CRD-generator.md b/doc/CRD-generator.md
index 3a69a2fdcb..4275c1ae3d 100644
--- a/doc/CRD-generator.md
+++ b/doc/CRD-generator.md
@@ -152,9 +152,107 @@ The field will be skipped in the generated CRD and will not appear in the schema
type: object
```
-### javax.validation.constraints.NotNull
+### io.fabric8.generator.annotation.Min
-If a field or one of its accessors is annotated with `javax.validation.constraints.NotNull`
+If a field or one of its accessors is annotated with `io.fabric8.generator.annotation.Min`
+
+```java
+public class ExampleSpec {
+ @Min(-1)
+ int someValue;
+}
+```
+
+The field will have the `minimum` property in the generated CRD, such as:
+
+```yaml
+ spec:
+ properties:
+ someValue:
+ minimum: -1.0
+ type: integer
+ required:
+ - someValue
+ type: object
+```
+
+### io.fabric8.generator.annotation.Max
+
+If a field or one of its accessors is annotated with `io.fabric8.generator.annotation.Max`
+
+```java
+public class ExampleSpec {
+ @Min(1)
+ int someValue;
+}
+```
+
+The field will have the `maximum` property in the generated CRD, such as:
+
+```yaml
+ spec:
+ properties:
+ someValue:
+ maximum: 1.0
+ type: integer
+ required:
+ - someValue
+ type: object
+```
+
+### io.fabric8.generator.annotation.Pattern
+
+If a field or one of its accessors is annotated with `io.fabric8.generator.annotation.Pattern`
+
+```java
+public class ExampleSpec {
+ @Pattern("\\b[1-9]\\b")
+ String someValue;
+}
+```
+
+The field will have the `pattern` property in the generated CRD, such as:
+
+```yaml
+ spec:
+ properties:
+ someValue:
+ maximum: "\\b[1-9]\\b"
+ type: string
+ required:
+ - someValue
+ type: object
+```
+
+### io.fabric8.generator.annotation.Nullable
+
+If a field or one of its accessors is annotated with `io.fabric8.generator.annotation.Nullable`
+
+```java
+public class ExampleSpec {
+ @Nullable
+ String someValue;
+}
+```
+
+The field will have the `nullable` property in the generated CRD, such as:
+
+```yaml
+ spec:
+ properties:
+ someValue:
+ nullable: true
+ type: string
+ required:
+ - someValue
+ type: object
+```
+
+### io.fabric8.generator.annotation.Required
+
+__DEPRECATED:__ `javax.validation.constraints.NotNull`
+
+If a field or one of its accessors is annotated with `io.fabric8.generator.annotation.Required`
```java
public class ExampleSpec {
@@ -268,7 +366,11 @@ Corresponding `x-kubernetes-preserve-unknown-fields: true` will be generated in
| `com.fasterxml.jackson.annotation.JsonIgnore` | The field is ignored |
| `com.fasterxml.jackson.annotation.JsonAnyGetter` | The corresponding object have `x-kubernetes-preserve-unknown-fields: true` defined |
| `com.fasterxml.jackson.annotation.JsonAnySetter` | The corresponding object have `x-kubernetes-preserve-unknown-fields: true` defined |
-| `javax.validation.constraints.NotNull` | The field is marked as `required` |
+| `io.fabric8.generator.annotation.Min` | The field defines a validation `min` |
+| `io.fabric8.generator.annotation.Max` | The field defines a validation `max` |
+| `io.fabric8.generator.annotation.Pattern` | The field defines a validation `pattern` |
+| `io.fabric8.generator.annotation.Nullable` | The field is marked as `nullable` |
+| `io.fabric8.generator.annotation.Required` | The field is marked as `required` |
| `io.fabric8.crd.generator.annotation.SchemaFrom` | The field type for the generation is the one coming from the annotation |
| `io.fabric8.crd.generator.annotation.SchemaSwap` | Same as SchemaFrom, but can be applied at any point in the class hierarchy |
diff --git a/generator-annotations/pom.xml b/generator-annotations/pom.xml
new file mode 100644
index 0000000000..5ca98b912b
--- /dev/null
+++ b/generator-annotations/pom.xml
@@ -0,0 +1,33 @@
+
+
+
+ 4.0.0
+
+ io.fabric8
+ kubernetes-client-project
+ 6.1-SNAPSHOT
+
+
+ generator-annotations
+ jar
+ Fabric8 :: Generator Annotations
+
+
diff --git a/generator-annotations/src/main/java/io/fabric8/generator/annotation/Max.java b/generator-annotations/src/main/java/io/fabric8/generator/annotation/Max.java
new file mode 100644
index 0000000000..4f371db245
--- /dev/null
+++ b/generator-annotations/src/main/java/io/fabric8/generator/annotation/Max.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fabric8.generator.annotation;
+
+import java.lang.annotation.*;
+
+@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Max {
+ double value();
+}
diff --git a/generator-annotations/src/main/java/io/fabric8/generator/annotation/Min.java b/generator-annotations/src/main/java/io/fabric8/generator/annotation/Min.java
new file mode 100644
index 0000000000..3876efbab1
--- /dev/null
+++ b/generator-annotations/src/main/java/io/fabric8/generator/annotation/Min.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fabric8.generator.annotation;
+
+import java.lang.annotation.*;
+
+@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Min {
+ double value();
+}
diff --git a/generator-annotations/src/main/java/io/fabric8/generator/annotation/Nullable.java b/generator-annotations/src/main/java/io/fabric8/generator/annotation/Nullable.java
new file mode 100644
index 0000000000..88238c3cd9
--- /dev/null
+++ b/generator-annotations/src/main/java/io/fabric8/generator/annotation/Nullable.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fabric8.generator.annotation;
+
+import java.lang.annotation.*;
+
+@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Nullable {
+}
diff --git a/generator-annotations/src/main/java/io/fabric8/generator/annotation/Pattern.java b/generator-annotations/src/main/java/io/fabric8/generator/annotation/Pattern.java
new file mode 100644
index 0000000000..e5c7c73928
--- /dev/null
+++ b/generator-annotations/src/main/java/io/fabric8/generator/annotation/Pattern.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fabric8.generator.annotation;
+
+import java.lang.annotation.*;
+
+@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Pattern {
+ String value();
+}
diff --git a/generator-annotations/src/main/java/io/fabric8/generator/annotation/Required.java b/generator-annotations/src/main/java/io/fabric8/generator/annotation/Required.java
new file mode 100644
index 0000000000..5d83c708f2
--- /dev/null
+++ b/generator-annotations/src/main/java/io/fabric8/generator/annotation/Required.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fabric8.generator.annotation;
+
+import java.lang.annotation.*;
+
+@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Required {
+}
diff --git a/pom.xml b/pom.xml
index c6ab4d4b49..c9601f7e3c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -192,6 +192,7 @@
platforms
kubernetes-tests
uberjar
+ generator-annotations
crd-generator
java-generator
httpclient-okhttp
@@ -200,6 +201,11 @@
+
+ io.fabric8
+ generator-annotations
+ ${project.version}
+
io.fabric8
java-generator-core