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

Support GA Workload API #164

Merged
merged 4 commits into from
Jun 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,23 @@ See the [programming guide](docs/GUIDE.md) for more details.

## Example Usage

This example creates a nginx service (accessed via port 30001 on each Kubernetes cluster node) that is backed by five nginx replicas.
This example creates a nginx service (accessed via port 30001 on each Kubernetes cluster node) that is backed by a deployment of five nginx replicas.

```scala
import skuber._
import skuber.json.format._
import skuber.apps.v1.Deployment
import LabelSelector.dsl._

val nginxSelector = Map("app" -> "nginx")
val nginxContainer = Container("nginx",image="nginx").exposePort(80)
val nginxController= ReplicationController("nginx",nginxContainer,nginxSelector)
val nginxSelector = "app" is "nginx"
val nginxContainer = Container(name = "nginx", image = "nginx").exposePort(80)
val nginxTemplate = Pod.Template.Spec.named("nginx").addContainer(nginxContainer).addLabel("app" -> "nginx")
val nginxDeployment = Deployment(name)
.withReplicas(5)
.withTemplate(nginxTemplate)
.withLabelSelector(nginxSelector)
val nginxService = Service("nginx")
.withSelector(nginxSelector)
.withSelector("app" -> "nginx")
.exposeOnNodePort(30001 -> 80)

// Some standard Akka implicits that are required by the skuber v2 client API
Expand All @@ -41,13 +46,14 @@ implicit val dispatcher = system.dispatcher
// Initialise skuber client
val k8s = k8sInit

// Create the service and the deployment on the Kubernetes cluster
val createOnK8s = for {
svc <- k8s create nginxService
rc <- k8s create nginxController
} yield (rc,svc)
dep <- k8s create nginxDeployment
} yield (dep,svc)

createOnK8s onComplete {
case Success(_) => System.out.println("Successfully created nginx replication controller & service on Kubernetes cluster")
case Success(_) => System.out.println("Successfully created nginx deployment & service on Kubernetes cluster")
case Failure(ex) => System.err.println("Encountered exception trying to create resources on Kubernetes cluster: " + ex)
}

Expand Down Expand Up @@ -100,23 +106,24 @@ The quickest way to get started with Skuber:
- Try one or more of the examples: if you have cloned this repository run `sbt` in the top-level directory to start sbt in interactive mode and then:

```bash
> project examples

> run
[warn] Multiple main classes detected. Run 'show discoveredMainClasses' to see the list
sbt:root> project examples
sbt:skuber-examples> run

Multiple main classes detected, select one to run:

[1] skuber.examples.customresources.CreateCRD
[2] skuber.examples.deployment.DeploymentExamples
[3] skuber.examples.fluent.FluentExamples
[4] skuber.examples.guestbook.Guestbook
[5] skuber.examples.ingress.NginxIngress
[6] skuber.examples.job.PrintPiJob
[7] skuber.examples.list.ListExamples
[8] skuber.examples.scale.ScaleExamples
[8] skuber.examples.patch.PatchExamples
[9] skuber.examples.podlogs.PodLogExample
[10] skuber.examples.scale.ScaleExamples
[11] skuber.examples.watch.WatchExamples

Enter number:
Enter number:
```

For other Kubernetes setups, see the [Configuration guide](docs/Configuration.md) for details on how to tailor the configuration for your clusters security, namespace and connectivity requirements.
Expand Down
7 changes: 4 additions & 3 deletions client/src/it/scala/skuber/DeploymentSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package skuber

import org.scalatest.Matchers
import org.scalatest.concurrent.{Eventually, ScalaFutures}
import skuber.ext.Deployment
import skuber.json.ext.format._
import skuber.LabelSelector.IsEqualRequirement
import skuber.apps.v1.Deployment

import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
Expand Down Expand Up @@ -59,8 +59,9 @@ class DeploymentSpec extends K8SFixture with Eventually with Matchers {
def getNginxContainer(version: String): Container = Container(name = "nginx", image = "nginx:" + version).exposePort(80)

def getNginxDeployment(name: String, version: String): Deployment = {
import LabelSelector.dsl._
val nginxContainer = getNginxContainer(version)
val nginxTemplate = Pod.Template.Spec.named("nginx").addContainer(nginxContainer).addLabel("app" -> "nginx")
Deployment(name).withTemplate(nginxTemplate)
Deployment(name).withTemplate(nginxTemplate).withLabelSelector("app" is "nginx")
}
}
9 changes: 0 additions & 9 deletions client/src/main/scala/skuber/Skuber/package.scala

This file was deleted.

30 changes: 15 additions & 15 deletions client/src/main/scala/skuber/apps/StatefulSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -96,29 +96,29 @@ object StatefulSet {
implicit val statefulSetPodPcyMgmtFmt: Format[StatefulSet.PodManagementPolicyType.PodManagementPolicyType] = Format(enumReads(StatefulSet.PodManagementPolicyType, StatefulSet.PodManagementPolicyType.OrderedReady), enumWrites)
implicit val statefulSetRollUp: Format[StatefulSet.RollingUpdateStrategy] = Json.format[StatefulSet.RollingUpdateStrategy]
implicit val statefulSetUpdStrFmt: Format[StatefulSet.UpdateStrategy] = (
(JsPath \ "type").formatEnum(StatefulSet.UpdateStrategyType, Some(StatefulSet.UpdateStrategyType.RollingUpdate)) and
(JsPath \ "rollingUpdate").formatNullable[StatefulSet.RollingUpdateStrategy]
)(StatefulSet.UpdateStrategy.apply _,unlift(StatefulSet.UpdateStrategy.unapply))
(JsPath \ "type").formatEnum(StatefulSet.UpdateStrategyType, Some(StatefulSet.UpdateStrategyType.RollingUpdate)) and
(JsPath \ "rollingUpdate").formatNullable[StatefulSet.RollingUpdateStrategy]
)(StatefulSet.UpdateStrategy.apply _,unlift(StatefulSet.UpdateStrategy.unapply))

implicit val statefulSetSpecFmt: Format[StatefulSet.Spec] = (
(JsPath \ "replicas").formatNullable[Int] and
(JsPath \ "serviceName").formatNullable[String] and
(JsPath \ "selector").formatNullableLabelSelector and
(JsPath \ "template").format[Pod.Template.Spec] and
(JsPath \ "volumeClaimTemplates").formatMaybeEmptyList[PersistentVolumeClaim] and
(JsPath \ "podManagmentPolicy").formatNullableEnum(StatefulSet.PodManagementPolicyType) and
(JsPath \ "updateStrategy").formatNullable[StatefulSet.UpdateStrategy] and
(JsPath \ "revisionHistoryLimit").formatNullable[Int]
)(StatefulSet.Spec.apply _, unlift(StatefulSet.Spec.unapply))
(JsPath \ "replicas").formatNullable[Int] and
(JsPath \ "serviceName").formatNullable[String] and
(JsPath \ "selector").formatNullableLabelSelector and
(JsPath \ "template").format[Pod.Template.Spec] and
(JsPath \ "volumeClaimTemplates").formatMaybeEmptyList[PersistentVolumeClaim] and
(JsPath \ "podManagmentPolicy").formatNullableEnum(StatefulSet.PodManagementPolicyType) and
(JsPath \ "updateStrategy").formatNullable[StatefulSet.UpdateStrategy] and
(JsPath \ "revisionHistoryLimit").formatNullable[Int]
)(StatefulSet.Spec.apply _, unlift(StatefulSet.Spec.unapply))

implicit val statefulSetCondFmt: Format[StatefulSet.Condition] = Json.format[StatefulSet.Condition]
implicit val statefulSetStatusFmt: Format[StatefulSet.Status] = Json.format[StatefulSet.Status]

implicit lazy val statefulSetFormat: Format[StatefulSet] = (
objFormat and
(JsPath \ "spec").formatNullable[StatefulSet.Spec] and
(JsPath \ "status").formatNullable[StatefulSet.Status]
) (StatefulSet.apply _, unlift(StatefulSet.unapply))
(JsPath \ "spec").formatNullable[StatefulSet.Spec] and
(JsPath \ "status").formatNullable[StatefulSet.Status]
)(StatefulSet.apply _, unlift(StatefulSet.unapply))

implicit val statefulSetListFormat: Format[StatefulSetList] = ListResourceFormat[StatefulSet]
}
120 changes: 120 additions & 0 deletions client/src/main/scala/skuber/apps/v1/DaemonSet.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package skuber.apps.v1

/**
* @author David O'Riordan
*/

import skuber.ResourceSpecification.{Names, Scope}
import skuber.{IntOrString, LabelSelector, NonCoreResourceSpecification, ObjectMeta, ObjectResource, Pod, ResourceDefinition, Timestamp}

case class DaemonSet(val kind: String ="DaemonSet",
override val apiVersion: String = appsAPIVersion,
val metadata: ObjectMeta,
spec: Option[DaemonSet.Spec] = None,
status: Option[DaemonSet.Status] = None)
extends ObjectResource {

lazy val copySpec = this.spec.getOrElse(new DaemonSet.Spec)

def withTemplate(template: Pod.Template.Spec) = this.copy(spec=Some(copySpec.copy(template=Some(template))))
def withLabelSelector(sel: LabelSelector) = this.copy(spec=Some(copySpec.copy(selector=Some(sel))))
}

object DaemonSet {

val specification=NonCoreResourceSpecification (
group=Some("apps"),
version="v1",
scope = Scope.Namespaced,
names=Names(
plural = "daemonsets",
singular = "daemonset",
kind = "DaemonSet",
shortNames = List("ds")
)
)
implicit val dsDef = new ResourceDefinition[DaemonSet] { def spec=specification }
implicit val dsListDef = new ResourceDefinition[DaemonSetList] { def spec=specification }

def apply(name: String) = new DaemonSet(metadata=ObjectMeta(name=name))

case class Spec(
minReadySeconds: Int = 0,
selector: Option[LabelSelector] = None,
template: Option[Pod.Template.Spec] = None,
updateStrategy: Option[UpdateStrategy] = None,
revisionHistoryLimit: Option[Int] = None
)

object UpdateStrategyType extends Enumeration {
type UpdateStrategyType = Value
val OnDelete, RollingUpdate = Value
}

sealed trait UpdateStrategy {
def _type: UpdateStrategyType.UpdateStrategyType
def rollingUpdate: Option[RollingUpdate]
}

object UpdateStrategy {
private[skuber] case class StrategyImpl(_type: UpdateStrategyType.UpdateStrategyType, rollingUpdate: Option[RollingUpdate]) extends UpdateStrategy
def apply: UpdateStrategy = StrategyImpl(_type=UpdateStrategyType.RollingUpdate, rollingUpdate=Some(RollingUpdate()))
def apply(_type: UpdateStrategyType.UpdateStrategyType,rollingUpdate: Option[RollingUpdate]) : UpdateStrategy = StrategyImpl(_type, rollingUpdate)
def apply(rollingUpdate: RollingUpdate) : UpdateStrategy = StrategyImpl(_type=UpdateStrategyType.RollingUpdate, rollingUpdate=Some(rollingUpdate))
def unapply(strategy: UpdateStrategy): Option[(UpdateStrategyType.UpdateStrategyType, Option[RollingUpdate])] =
Some(strategy._type,strategy.rollingUpdate)
}

case class RollingUpdate(maxUnavailable: IntOrString = Left(1))

case class Condition(
_type: String,
status: String,
reason: Option[String]=None,
message: Option[String]=None,
lastTransitionTime: Option[Timestamp]=None)

case class Status(
currentNumberScheduled: Int,
numberMisscheduled: Int,
desiredNumberScheduled: Int,
numberReady: Int,
observedGeneration: Option[Long],
updatedNumberScheduled: Option[Int],
numberAvailable: Option[Int],
numberUnavailable:Option[Int],
collisionCount:Option[Long],
conditions: List[Condition])

// json formatters
import play.api.libs.json.{Json,Format, JsPath}
import play.api.libs.functional.syntax._
import skuber.json.format._

implicit val condFmt: Format[Condition] = Json.format[Condition]
implicit val rollingUpdFmt: Format[RollingUpdate] = (
(JsPath \ "maxUnavailable").formatMaybeEmptyIntOrString(Left(1)).inmap(mu => RollingUpdate(mu), (ru: RollingUpdate) => ru.maxUnavailable)
)

implicit val updateStrategyFmt: Format[UpdateStrategy] = (
(JsPath \ "type").formatEnum(UpdateStrategyType, Some(UpdateStrategyType.RollingUpdate)) and
(JsPath \ "rollingUpdate").formatNullable[RollingUpdate]
)(UpdateStrategy.apply _, unlift(UpdateStrategy.unapply))

implicit val daemonsetStatusFmt: Format[Status] = Json.format[Status]
implicit val daemonsetSpecFmt: Format[Spec] = (
(JsPath \ "minReadySeconds").formatMaybeEmptyInt() and
(JsPath \ "selector").formatNullableLabelSelector and
(JsPath \ "template").formatNullable[Pod.Template.Spec] and
(JsPath \ "updateStrategy").formatNullable[UpdateStrategy] and
(JsPath \ "revisionHistoryLimit").formatNullable[Int]
)(Spec.apply, unlift(Spec.unapply))

implicit lazy val daemonsetFmt: Format[DaemonSet] = (
objFormat and
(JsPath \ "spec").formatNullable[Spec] and
(JsPath \ "status").formatNullable[Status]
) (DaemonSet.apply _, unlift(DaemonSet.unapply))

}

Loading