Skip to content

Commit

Permalink
Support GA Workload API (#164)
Browse files Browse the repository at this point in the history
* Support apps/v1 versioned workload kinds

* General docs updates, including for apps/v1 changes and Scala syntax highlighting
  • Loading branch information
doriordan authored Jun 24, 2018
1 parent 20ae0a8 commit 85e6448
Show file tree
Hide file tree
Showing 19 changed files with 1,539 additions and 290 deletions.
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

0 comments on commit 85e6448

Please sign in to comment.