Rewrite for newest PMS version. Add easy deploy helm chart.
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
FROM alpine:3.6
|
||||
|
||||
ADD kube-plex_linux_amd64 /kube-plex
|
||||
@@ -0,0 +1,62 @@
|
||||
# kube-plex
|
||||
|
||||
kube-plex is a scalable Plex Media Server solution for Kubernetes. It
|
||||
distributes transcode jobs by creating pods in a Kubernetes cluster to perform
|
||||
transcodes, instead of running transcodes on the Plex Media Server instance
|
||||
itself.
|
||||
|
||||
## How it works
|
||||
|
||||
kube-plex works by replacing the Plex Transcoder program on the main PMS
|
||||
instance with our own little shim. This shim intercepts calls to Plex
|
||||
Transcoder, and creates Kubernetes pods to perform the work instead. These
|
||||
pods use shared persistent volumes to store the results of the transcode (and
|
||||
read your media!).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* A persistent volume type that supports ReadWriteMany volumes (e.g. NFS,
|
||||
Amazon EFS)
|
||||
* Your Plex Media Server *must* be configured to allow connections from
|
||||
unauthorized users for your pod network, else the transcode job is unable to
|
||||
report information back to Plex about the state of the transcode job. At some
|
||||
point in the future this may change, but it is a required step in order to make
|
||||
transcodes work right now.
|
||||
|
||||
## Setup
|
||||
|
||||
This guide will go through setting up a Plex Media Server instance on a
|
||||
Kubernetes cluster, configured to launch transcode jobs on the same cluster
|
||||
in pods created in the same 'plex' namespace.
|
||||
|
||||
1) Obtain a Plex Claim Token by visiting [plex.tv/claim](https://plex.tv/claim).
|
||||
This will be used to bind your new PMS instance to your own user account
|
||||
automatically.
|
||||
|
||||
2) Deploy the Helm chart included in this repository:
|
||||
|
||||
```bash
|
||||
➜ helm install ./charts/kube-plex --name plex \
|
||||
--namespace plex \
|
||||
--set claimToken=[insert claim token here]
|
||||
```
|
||||
|
||||
This will deploy a scalable Plex Media Server instance.
|
||||
|
||||
3) Access the Plex dashboard, either using `kubectl port-forward` or via your
|
||||
services LoadBalancer IP address. Set up your Plex server and appropriate media.
|
||||
|
||||
4) Visit Settings->Server->Network and add your pod network subnet to the
|
||||
`List of IP addresses and networks that are allowed without auth` (near the
|
||||
bottom). For example, `10.100.0.0/16` is the subnet that pods in my cluster are
|
||||
assigned IPs from.
|
||||
|
||||
You should now be able to play media from your PMS instance - pods will be
|
||||
created to handle transcodes, and data automatically mounted in appropriately:
|
||||
|
||||
```bash
|
||||
➜ kubectl get po -n plex
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
plex-kube-plex-75b96cdcb4-skrxr 1/1 Running 0 14m
|
||||
pms-elastic-transcoder-7wnqk 1/1 Running 0 8m
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
description: A Helm chart for Kubernetes
|
||||
name: kube-plex
|
||||
version: 0.1.0
|
||||
@@ -0,0 +1,19 @@
|
||||
1. Get the application URL by running these commands:
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range .Values.ingress.hosts }}
|
||||
http://{{ . }}
|
||||
{{- end }}
|
||||
{{- else if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get svc -w {{ template "fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
|
||||
echo http://$SERVICE_IP:{{ .Values.service.externalPort }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl port-forward $POD_NAME 8080:{{ .Values.service.externalPort }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,16 @@
|
||||
{{/* vim: set filetype=mustache: */}}
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
*/}}
|
||||
{{- define "fullname" -}}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride -}}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,105 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ template "fullname" . }}
|
||||
labels:
|
||||
app: {{ template "name" . }}
|
||||
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ template "name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ template "name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
spec:
|
||||
serviceAccountName: "{{ template "fullname" . }}"
|
||||
hostname: "{{ template "fullname" . }}"
|
||||
initContainers:
|
||||
- name: kube-plex-install
|
||||
image: "{{ .Values.kubePlexImage.repository }}:{{ .Values.kubePlexImage.tag }}"
|
||||
imagePullPolicy: {{ .Values.kubePlexImage.pullPolicy }}
|
||||
command:
|
||||
- cp
|
||||
- /kube-plex
|
||||
- /shared/kube-plex
|
||||
volumeMounts:
|
||||
- name: shared
|
||||
mountPath: /shared
|
||||
containers:
|
||||
- name: plex
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
# We replace the PMS binary with a postStart hook to save having to
|
||||
# modify the default image entrypoint.
|
||||
lifecycle:
|
||||
postStart:
|
||||
exec:
|
||||
command:
|
||||
- bash
|
||||
- -c
|
||||
- |
|
||||
#!/bin/bash
|
||||
set -e
|
||||
rm -f '/usr/lib/plexmediaserver/Plex Transcoder'
|
||||
cp /shared/kube-plex '/usr/lib/plexmediaserver/Plex Transcoder'
|
||||
# readinessProbe:
|
||||
# httpGet:
|
||||
# path: /
|
||||
# port: 32400
|
||||
env:
|
||||
- name: TZ
|
||||
value: Europe/London
|
||||
# TODO: move this to a secret?
|
||||
- name: PLEX_CLAIM
|
||||
value: "{{ .Values.claimToken }}"
|
||||
# kube-plex env vars
|
||||
- name: PMS_INTERNAL_ADDRESS
|
||||
value: http://{{ template "fullname" . }}:32400
|
||||
- name: PMS_IMAGE
|
||||
value: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
- name: KUBE_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: TRANSCODE_PVC
|
||||
value: "{{ template "fullname" . }}-transcode"
|
||||
- name: DATA_PVC
|
||||
value: "{{ template "fullname" . }}-data"
|
||||
- name: CONFIG_PVC
|
||||
value: "{{ template "fullname" . }}-config"
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
- name: config
|
||||
mountPath: /config
|
||||
- name: transcode
|
||||
mountPath: /transcode
|
||||
- name: shared
|
||||
mountPath: /shared
|
||||
resources:
|
||||
{{ toYaml .Values.resources | indent 10 }}
|
||||
{{- if .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{ toYaml .Values.nodeSelector | indent 8 }}
|
||||
{{- end }}
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: "{{ template "fullname" . }}-data"
|
||||
- name: config
|
||||
persistentVolumeClaim:
|
||||
claimName: "{{ template "fullname" . }}-config"
|
||||
- name: transcode
|
||||
persistentVolumeClaim:
|
||||
claimName: "{{ template "fullname" . }}-transcode"
|
||||
- name: shared
|
||||
emptyDir: {}
|
||||
@@ -0,0 +1,32 @@
|
||||
{{- if .Values.ingress.enabled -}}
|
||||
{{- $serviceName := include "fullname" . -}}
|
||||
{{- $servicePort := .Values.service.externalPort -}}
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ template "fullname" . }}
|
||||
labels:
|
||||
app: {{ template "name" . }}
|
||||
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
annotations:
|
||||
{{- range $key, $value := .Values.ingress.annotations }}
|
||||
{{ $key }}: {{ $value | quote }}
|
||||
{{- end }}
|
||||
spec:
|
||||
rules:
|
||||
{{- range $host := .Values.ingress.hosts }}
|
||||
- host: {{ $host }}
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: {{ $serviceName }}
|
||||
servicePort: {{ $servicePort }}
|
||||
{{- end -}}
|
||||
{{- if .Values.ingress.tls }}
|
||||
tls:
|
||||
{{ toYaml .Values.ingress.tls | indent 4 }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,50 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: {{ template "fullname" . }}
|
||||
labels:
|
||||
app: {{ template "name" . }}
|
||||
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
- pods/attach
|
||||
- pods/exec
|
||||
- pods/portforward
|
||||
- pods/proxy
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: {{ template "fullname" . }}
|
||||
labels:
|
||||
app: {{ template "name" . }}
|
||||
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: {{ template "fullname" . }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ template "fullname" . }}
|
||||
namespace: {{ .Release.Namespace | quote }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ template "fullname" . }}
|
||||
@@ -0,0 +1,24 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ template "fullname" . }}
|
||||
labels:
|
||||
app: {{ template "name" . }}
|
||||
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 32400
|
||||
- name: https
|
||||
port: 443
|
||||
targetPort: 32443
|
||||
- name: pms
|
||||
port: 32400
|
||||
targetPort: 32400
|
||||
selector:
|
||||
app: {{ template "name" . }}
|
||||
release: {{ .Release.Name }}
|
||||
@@ -0,0 +1,54 @@
|
||||
## TODO: make this configurable through the helm chart
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ template "fullname" . }}-transcode
|
||||
labels:
|
||||
app: {{ template "name" . }}
|
||||
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
storageClassName: "nfs"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ template "fullname" . }}-config
|
||||
labels:
|
||||
app: {{ template "name" . }}
|
||||
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
storageClassName: "nfs"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ template "fullname" . }}-data
|
||||
labels:
|
||||
app: {{ template "name" . }}
|
||||
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
selector:
|
||||
matchLabels:
|
||||
name: pms-data
|
||||
storageClassName: ""
|
||||
@@ -0,0 +1,40 @@
|
||||
# Default values for kube-plex.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
replicaCount: 1
|
||||
image:
|
||||
repository: plexinc/pms-docker
|
||||
tag: 1.10.1.4602-f54242b6b
|
||||
pullPolicy: IfNotPresent
|
||||
kubePlexImage:
|
||||
repository: quay.io/munnerz/kube-plex
|
||||
tag: latest
|
||||
pullPolicy: Always
|
||||
# Override this with the plex claim token from plex.tv/claim
|
||||
claimToken: ""
|
||||
service:
|
||||
type: ClusterIP
|
||||
ingress:
|
||||
enabled: false
|
||||
# Used to create an Ingress record.
|
||||
hosts:
|
||||
- chart-example.local
|
||||
annotations:
|
||||
# kubernetes.io/ingress.class: nginx
|
||||
# kubernetes.io/tls-acme: "true"
|
||||
tls:
|
||||
# Secrets must be manually created in the namespace.
|
||||
# - secretName: chart-example-tls
|
||||
# hosts:
|
||||
# - chart-example.local
|
||||
resources: {}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
# choice for the user. This also increases chances charts run on environments with little
|
||||
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||
# limits:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
@@ -0,0 +1,199 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/munnerz/kube-plex/pkg/signals"
|
||||
)
|
||||
|
||||
// data pvc name
|
||||
var dataPVC = os.Getenv("DATA_PVC")
|
||||
|
||||
// config pvc name
|
||||
var configPVC = os.Getenv("CONFIG_PVC")
|
||||
|
||||
// transcode pvc name
|
||||
var transcodePVC = os.Getenv("TRANSCODE_PVC")
|
||||
|
||||
// pms namespace
|
||||
var namespace = os.Getenv("KUBE_NAMESPACE")
|
||||
|
||||
// image for the plexmediaserver container containing the transcoder. This
|
||||
// should be set to the same as the 'master' pms server
|
||||
var pmsImage = os.Getenv("PMS_IMAGE")
|
||||
var pmsInternalAddress = os.Getenv("PMS_INTERNAL_ADDRESS")
|
||||
|
||||
func main() {
|
||||
env := os.Environ()
|
||||
args := os.Args
|
||||
|
||||
rewriteEnv(env)
|
||||
rewriteArgs(args)
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("Error getting working directory: %s", err)
|
||||
}
|
||||
pod := generatePod(cwd, env, args)
|
||||
|
||||
cfg, err := clientcmd.BuildConfigFromFlags("", "")
|
||||
if err != nil {
|
||||
log.Fatalf("Error building kubeconfig: %s", err)
|
||||
}
|
||||
|
||||
kubeClient, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("Error building kubernetes clientset: %s", err)
|
||||
}
|
||||
|
||||
pod, err = kubeClient.CoreV1().Pods(namespace).Create(pod)
|
||||
if err != nil {
|
||||
log.Fatalf("Error creating pod: %s", err)
|
||||
}
|
||||
|
||||
stopCh := signals.SetupSignalHandler()
|
||||
waitFn := func() <-chan error {
|
||||
stopCh := make(chan error)
|
||||
go func() {
|
||||
stopCh <- waitForPodCompletion(kubeClient, pod)
|
||||
}()
|
||||
return stopCh
|
||||
}
|
||||
|
||||
select {
|
||||
case err := <-waitFn():
|
||||
if err != nil {
|
||||
log.Printf("Error waiting for pod to complete: %s", err)
|
||||
}
|
||||
case <-stopCh:
|
||||
log.Printf("Exit requested.")
|
||||
}
|
||||
|
||||
log.Printf("Cleaning up pod...")
|
||||
err = kubeClient.CoreV1().Pods(namespace).Delete(pod.Name, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Error cleaning up pod: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// rewriteEnv rewrites environment variables to be passed to the transcoder
|
||||
func rewriteEnv(in []string) {
|
||||
// no changes needed
|
||||
}
|
||||
|
||||
func rewriteArgs(in []string) {
|
||||
for i, v := range in {
|
||||
switch v {
|
||||
case "-progressurl", "-manifest_name", "-segment_list":
|
||||
in[i+1] = strings.Replace(in[i+1], "http://127.0.0.1:32400", pmsInternalAddress, 1)
|
||||
case "-loglevel":
|
||||
in[i+1] = "debug"
|
||||
case "-loglevel_plex":
|
||||
in[i+1] = "debug"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generatePod(cwd string, env []string, args []string) *corev1.Pod {
|
||||
envVars := toCoreV1EnvVar(env)
|
||||
return &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "pms-elastic-transcoder-",
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
RestartPolicy: corev1.RestartPolicyNever,
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "plex",
|
||||
Command: args,
|
||||
Image: pmsImage,
|
||||
Env: envVars,
|
||||
WorkingDir: cwd,
|
||||
VolumeMounts: []corev1.VolumeMount{
|
||||
{
|
||||
Name: "data",
|
||||
MountPath: "/data",
|
||||
ReadOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "config",
|
||||
MountPath: "/config",
|
||||
ReadOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "transcode",
|
||||
MountPath: "/transcode",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Volumes: []corev1.Volume{
|
||||
{
|
||||
Name: "data",
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
|
||||
ClaimName: dataPVC,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "config",
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
|
||||
ClaimName: configPVC,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "transcode",
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
|
||||
ClaimName: transcodePVC,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func toCoreV1EnvVar(in []string) []corev1.EnvVar {
|
||||
out := make([]corev1.EnvVar, len(in))
|
||||
for i, v := range in {
|
||||
splitvar := strings.SplitN(v, "=", 2)
|
||||
out[i] = corev1.EnvVar{
|
||||
Name: splitvar[0],
|
||||
Value: splitvar[1],
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func waitForPodCompletion(cl kubernetes.Interface, pod *corev1.Pod) error {
|
||||
for {
|
||||
pod, err := cl.CoreV1().Pods(pod.Namespace).Get(pod.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch pod.Status.Phase {
|
||||
case corev1.PodPending:
|
||||
case corev1.PodRunning:
|
||||
case corev1.PodUnknown:
|
||||
log.Printf("Warning: pod %q is in an unknown state", pod.Name)
|
||||
case corev1.PodFailed:
|
||||
return fmt.Errorf("pod %q failed", pod.Name)
|
||||
case corev1.PodSucceeded:
|
||||
return nil
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
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 signals
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
)
|
||||
|
||||
var onlyOneSignalHandler = make(chan struct{})
|
||||
|
||||
// SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned
|
||||
// which is closed on one of these signals. If a second signal is caught, the program
|
||||
// is terminated with exit code 1.
|
||||
func SetupSignalHandler() (stopCh <-chan struct{}) {
|
||||
close(onlyOneSignalHandler) // panics when called twice
|
||||
|
||||
stop := make(chan struct{})
|
||||
c := make(chan os.Signal, 2)
|
||||
signal.Notify(c, shutdownSignals...)
|
||||
go func() {
|
||||
<-c
|
||||
close(stop)
|
||||
<-c
|
||||
os.Exit(1) // second signal. Exit directly.
|
||||
}()
|
||||
|
||||
return stop
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
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 signals
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
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 signals
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
var shutdownSignals = []os.Signal{os.Interrupt}
|
||||
Reference in New Issue
Block a user