Compare commits

...

11 Commits

Author SHA1 Message Date
hacker
4a1323c500 Custom 2025-11-24 00:11:00 +00:00
Alexey Kolvakh
437f6e3d4f update year in landing.html (#654)
Some checks failed
Go / test (1.16.0, ubuntu-latest) (push) Has been cancelled
2025-06-02 09:29:31 -03:00
Marcos Lilljedahl
37ee1622f5 bring down default duration from 4 to 2h
Signed-off-by: Marcos Lilljedahl <marcosnils@gmail.com>
2025-05-21 23:25:18 -03:00
Michael Irwin
0f5d302aa7 Remove survey prompts, as no longer accepting new submissions (#646)
This reverts commit 3935d3ef92.
2025-01-27 16:01:30 -03:00
Marcos Lilljedahl
e1434ba283 use legacy iptables
Signed-off-by: Marcos Lilljedahl <marcosnils@gmail.com>
2024-12-20 02:04:47 -03:00
Marcos Nils
fdbb3f1e66 remove localhost CORS as it's not needed anymore (#637)
fixes #636

Signed-off-by: Marcos Lilljedahl <marcosnils@gmail.com>
2024-12-02 00:34:52 -03:00
Marcos Lilljedahl
4e69f4b776 add gcr mirro 2024-10-06 23:03:42 -03:00
Marcos Lilljedahl
122bf5cf63 add new k8s logo
Signed-off-by: Marcos Lilljedahl <marcosnils@gmail.com>
2024-05-31 17:48:19 -03:00
SvenFa
dede444497 Update kubernetes.repo (#612)
As provided by official documentation the old repositories are unavailable since januar 2024

https://kubernetes.io/blog/2023/08/15/pkgs-k8s-io-introduction/
2024-03-08 09:10:12 -03:00
Michael Irwin
3935d3ef92 Add survey feedback toast and button (#610) 2024-02-23 14:46:52 -03:00
Marcos Lilljedahl
a07bc20f06 make sure instance client is ready before provisioning stack
Signed-off-by: Marcos Lilljedahl <marcosnils@gmail.com>
2024-01-12 11:15:53 -03:00
9 changed files with 202 additions and 125 deletions

2
api.go
View File

@@ -47,7 +47,7 @@ func main() {
sch.Start() sch.Start()
d, err := time.ParseDuration("4h") d, err := time.ParseDuration("2h")
if err != nil { if err != nil {
log.Fatalf("Cannot parse duration Got: %v", err) log.Fatalf("Cannot parse duration Got: %v", err)
} }

View File

@@ -1,12 +1,11 @@
version: '3.2'
services: services:
haproxy: haproxy:
container_name: haproxy container_name: haproxy
image: haproxy image: haproxy
ports:
- "80:8080"
volumes: volumes:
- ./haproxy:/usr/local/etc/haproxy - ./haproxy:/usr/local/etc/haproxy
environment:
- SERVICE_URL_PWD_8080
pwd: pwd:
# pwd daemon container always needs to be named this way # pwd daemon container always needs to be named this way
@@ -33,10 +32,7 @@ services:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
- $PWD:/go/src - $PWD:/go/src
- networks:/pwd - networks:/pwd
ports: #ports:
- "8022:22" # - "8022:22"
- "8053:53" # - "8053:53"
- "443:443" # - "443:443"
volumes:
sessions:
networks:

View File

@@ -5,7 +5,8 @@ RUN apk add --no-cache py-pip python3-dev libffi-dev openssl-dev git tmux apache
&& ln -s /usr/local/bin/docker /usr/bin/docker && ln -s /usr/local/bin/docker /usr/bin/docker
ENV GOPATH /root/go ENV GOPATH /root/go
ENV PATH $PATH:$GOPATH ENV IPTABLES_LEGACY /usr/local/sbin/.iptables-legacy/
ENV PATH $IPTABLES_LEGACY:$GOPATH:$PATH
ENV DOCKER_TLS_CERTDIR="" ENV DOCKER_TLS_CERTDIR=""
@@ -25,14 +26,13 @@ RUN wget -O /tmp/scout.tar.gz https://github.com/docker/scout-cli/releases/downl
&& chmod +x /usr/local/bin/docker-scout \ && chmod +x /usr/local/bin/docker-scout \
&& ln -s $(which docker-scout) /usr/lib/docker/cli-plugins \ && ln -s $(which docker-scout) /usr/lib/docker/cli-plugins \
&& rm /tmp/scout.tar.gz && rm /tmp/scout.tar.gz
# Add bash completion and set bash as default shell # Add bash completion and set bash as default shell
RUN mkdir /etc/bash_completion.d \ RUN curl -sS https://raw.githubusercontent.com/docker/cli/refs/heads/master/contrib/completion/bash/docker -o /etc/bash_completion.d/docker \
&& curl -sS https://raw.githubusercontent.com/docker/cli/master/contrib/completion/bash/docker -o /etc/bash_completion.d/docker \
&& sed -i "s/ash/bash/" /etc/passwd && sed -i "s/ash/bash/" /etc/passwd
# Replace modprobe with a no-op to get rid of spurious warnings # Replace modprobe with a no-op to get rid of spurious warnings
# (note: we can't just symlink to /bin/true because it might be busybox) # (note: we can't just symlink to /bin/true because it might be busybox)
RUN rm /sbin/modprobe && echo '#!/bin/true' >/sbin/modprobe && chmod +x /sbin/modprobe RUN rm /sbin/modprobe && echo '#!/bin/true' >/sbin/modprobe && chmod +x /sbin/modprobe

View File

@@ -2,6 +2,7 @@
"experimental": true, "experimental": true,
"debug": true, "debug": true,
"log-level": "info", "log-level": "info",
"registry-mirrors": ["https://mirror.gcr.io"],
"insecure-registries": [ "insecure-registries": [
"127.0.0.1" "127.0.0.1"
], ],

View File

@@ -1,8 +1,7 @@
[kubernetes] [kubernetes]
name=Kubernetes name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64 baseurl=https://pkgs.k8s.io/core:/stable:/v1.28/rpm/
enabled=1 enabled=1
gpgcheck=1 gpgcheck=1
repo_gpgcheck=1 gpgkey=https://pkgs.k8s.io/core:/stable:/v1.28/rpm/repodata/repomd.xml.key
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg

View File

@@ -69,8 +69,7 @@ func Register(extend HandlerExtender) {
corsRouter := mux.NewRouter() corsRouter := mux.NewRouter()
corsHandler := gh.CORS(gh.AllowCredentials(), gh.AllowedHeaders([]string{"x-requested-with", "content-type"}), gh.AllowedMethods([]string{"GET", "POST", "HEAD", "DELETE"}), gh.AllowedOriginValidator(func(origin string) bool { corsHandler := gh.CORS(gh.AllowCredentials(), gh.AllowedHeaders([]string{"x-requested-with", "content-type"}), gh.AllowedMethods([]string{"GET", "POST", "HEAD", "DELETE"}), gh.AllowedOriginValidator(func(origin string) bool {
if strings.Contains(origin, "localhost") || if strings.HasSuffix(origin, ".play-with-docker.com") ||
strings.HasSuffix(origin, ".play-with-docker.com") ||
strings.HasSuffix(origin, ".play-with-kubernetes.com") || strings.HasSuffix(origin, ".play-with-kubernetes.com") ||
strings.HasSuffix(origin, ".docker.com") || strings.HasSuffix(origin, ".docker.com") ||
strings.HasSuffix(origin, ".play-with-go.dev") { strings.HasSuffix(origin, ".play-with-go.dev") {

View File

@@ -59,7 +59,7 @@
</div> </div>
<footer class="footer"> <footer class="footer">
<p>&copy; Play with Docker 2017 - 2023</p> <p>&copy; Play with Docker 2017 - 2025</p>
</footer> </footer>
</div> </div>

View File

@@ -1,152 +1,231 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en" ng-app="PWDLanding" ng-controller="LoginController"> <html lang="en" ng-app="PWDLanding" ng-controller="LoginController">
<head> <head>
<script src="https://unpkg.com/angular@1.6.6/angular.min.js"></script> <script src="https://unpkg.com/angular@1.6.6/angular.min.js"></script>
<script src="https://unpkg.com/angular-cookies@1.6.6/angular-cookies.min.js"></script> <script src="https://unpkg.com/angular-cookies@1.6.6/angular-cookies.min.js"></script>
<meta charset="utf-8"> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta
<meta name="description" content=""> name="viewport"
<meta name="author" content=""> content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="description" content="" />
<meta name="author" content="" />
<title>Play with Kubernetes</title> <title>Play with Kubernetes</title>
<!-- Bootstrap core CSS --> <!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://unpkg.com/bootstrap@4.0.0-beta/dist/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous"> <link
rel="stylesheet"
href="https://unpkg.com/bootstrap@4.0.0-beta/dist/css/bootstrap.min.css"
integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M"
crossorigin="anonymous"
/>
<!-- Custom styles for this template --> <!-- Custom styles for this template -->
<link href="/assets/landing.css" rel="stylesheet"> <link href="/assets/landing.css" rel="stylesheet" />
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<div class="header clearfix"> <div class="header clearfix">
<nav> <nav>
<ul class="nav nav-pills float-right"> <ul class="nav nav-pills float-right">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="https://github.com/play-with-docker/play-with-docker">Contribute</a> <a
class="nav-link"
href="https://github.com/play-with-docker/play-with-docker"
>Contribute</a
>
</li> </li>
</ul> </ul>
</nav> </nav>
</div> </div>
<div class="jumbotron" ng-cloak> <div class="jumbotron" ng-cloak>
<img src="https://kubernetes.io/images/favicon.png" /> <img src="https://avatars.githubusercontent.com/u/13629408?v=4" />
<h1 class="display-3">Play with Kubernetes</h1> <h1 class="display-3">Play with Kubernetes</h1>
<p class="lead">A simple, interactive and fun playground to learn Kubernetes</p> <p class="lead">
A simple, interactive and fun playground to learn Kubernetes
</p>
<div ng-hide="loggedIn" class="btn-group" role="group"> <div ng-hide="loggedIn" class="btn-group" role="group">
<button id="btnGroupDrop1" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button
id="btnGroupDrop1"
type="button"
class="btn btn-primary dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Login Login
</button> </button>
<div class="dropdown-menu" aria-labelledby="btnGroupDrop1"> <div class="dropdown-menu" aria-labelledby="btnGroupDrop1">
<a ng-repeat="provider in providers" class="dropdown-item" ng-click="login(provider)">{{provider}}</a> <a
ng-repeat="provider in providers"
class="dropdown-item"
ng-click="login(provider)"
>{{provider}}</a
>
</div> </div>
</div> </div>
<form id="landingForm" method="POST" action="/"> <form id="landingForm" method="POST" action="/">
<p ng-show="loggedIn"><a class="btn btn-lg btn-success" href="#" ng-click="start()" role="button">Start</a></p> <p ng-show="loggedIn">
<input id="stack" type="hidden" name="stack" value=""/> <a
<input id="stack_name" type="hidden" name="stack_name" value=""/> class="btn btn-lg btn-success"
<input id="image_name" type="hidden" name="image_name" value=""/> href="#"
ng-click="start()"
role="button"
>Start</a
>
</p>
<input id="stack" type="hidden" name="stack" value="" />
<input id="stack_name" type="hidden" name="stack_name" value="" />
<input id="image_name" type="hidden" name="image_name" value="" />
</form> </form>
</div> </div>
<div class="row marketing"> <div class="row marketing">
<div class="col-lg-12"> <div class="col-lg-12">
<p>Play with Kubernetes is a labs site provided by <a href="https://docker.com">Docker</a> and created by Tutorius. Play with Kubernetes is a playground which allows users to run K8s clusters in a matter of seconds. It gives the experience of having a free Alpine Linux Virtual Machine in browser. Under the hood Docker-in-Docker (DinD) is used to give the effect of multiple VMs/PCs.</p> <p>
<p>If you want to learn more about Kubernetes, consider the <a href="https://training.play-with-kubernetes.com/">Play with Kubernetes Classroom</a> which provides more directed learning using an integrated Play with Kubernetes commandline.</p> Play with Kubernetes is a labs site provided by
<a href="https://docker.com">Docker</a> and created by Tutorius.
Play with Kubernetes is a playground which allows users to run K8s
clusters in a matter of seconds. It gives the experience of having a
free Alpine Linux Virtual Machine in browser. Under the hood
Docker-in-Docker (DinD) is used to give the effect of multiple
VMs/PCs.
</p>
<p>
If you want to learn more about Kubernetes, consider the
<a href="https://training.play-with-kubernetes.com/"
>Play with Kubernetes Classroom</a
>
which provides more directed learning using an integrated Play with
Kubernetes commandline.
</p>
</div> </div>
</div> </div>
<footer class="footer"> <footer class="footer">
<p>&copy; Play with Kubernetes 2017 - 2023</p> <p>&copy; Play with Kubernetes 2017 - 2023</p>
</footer> </footer>
</div> </div>
<script crossorigin="anonymous" src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> <script
<script crossorigin="anonymous" src="https://unpkg.com/popper.js@1.11.0/dist/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script> crossorigin="anonymous"
<script crossorigin="anonymous" src="https://unpkg.com/bootstrap@4.0.0-beta/dist/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script> src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous"
></script>
<script
crossorigin="anonymous"
src="https://unpkg.com/popper.js@1.11.0/dist/umd/popper.min.js"
integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4"
crossorigin="anonymous"
></script>
<script
crossorigin="anonymous"
src="https://unpkg.com/bootstrap@4.0.0-beta/dist/js/bootstrap.min.js"
integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1"
crossorigin="anonymous"
></script>
<script> <script>
angular.module('PWDLanding', []) angular.module("PWDLanding", []).controller("LoginController", [
.controller('LoginController', ['$scope', '$http', '$window', function($scope, $http, $window) { "$scope",
$scope.providers = []; "$http",
$scope.loggedIn = false; "$window",
$scope.user = null; function ($scope, $http, $window) {
$scope.providers = [];
$scope.loggedIn = false;
$scope.user = null;
function checkLoggedIn() { function checkLoggedIn() {
$http({ $http({
method: 'GET', method: "GET",
url: '/users/me' url: "/users/me",
}).then(function(response) { }).then(
$scope.user = response.data; function (response) {
$scope.loggedIn = true; $scope.user = response.data;
}, function(response) { $scope.loggedIn = true;
$scope.user = null; },
$scope.loggedIn = false; function (response) {
}); $scope.user = null;
} $scope.loggedIn = false;
},
);
}
checkLoggedIn();
$http({
method: "GET",
url: "/oauth/providers",
}).then(
function (response) {
$scope.providers = response.data;
if ($scope.providers.length == 0) {
$scope.loggedIn = true;
}
},
function (response) {
console.log("ERROR", response);
},
);
$scope.login = function (provider) {
var width = screen.width * 0.6;
// fixed height as the login window is not responsive
var height = 620;
var x = screen.width / 2 - width / 2;
var y = screen.height / 2 - height / 2;
$window.open(
"/oauth/providers/" + provider + "/login",
"PWDLogin",
"width=" + width + ",height=" + height + ",left=" + x + ",top=" + y,
);
var eventMethod = window.addEventListener
? "addEventListener"
: "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
// Listen to message from child window
eventer(
messageEvent,
function (e) {
if (e.data === "done") {
checkLoggedIn(); checkLoggedIn();
}
},
false,
);
};
$http({ $scope.start = function () {
method: 'GET', function getParameterByName(name, url) {
url: '/oauth/providers' if (!url) url = window.location.href;
}).then(function(response) { name = name.replace(/[\[\]]/g, "\\$&");
$scope.providers = response.data; var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
if ($scope.providers.length == 0) { results = regex.exec(url);
$scope.loggedIn = true; if (!results) return null;
} if (!results[2]) return "";
}, function(response) { return decodeURIComponent(results[2].replace(/\+/g, " "));
console.log('ERROR', response); }
});
var stack = getParameterByName("stack");
$scope.login = function(provider) { if (stack) {
var width = screen.width*0.6; document.getElementById("stack").value = stack;
// fixed height as the login window is not responsive }
var height = 620; var stackName = getParameterByName("stack_name");
var x = screen.width/2 - width/2; if (stackName) {
var y = screen.height/2 - height/2; document.getElementById("stack_name").value = stackName;
$window.open('/oauth/providers/' + provider + '/login', 'PWDLogin', 'width='+width+',height='+height+',left='+x+',top='+y); }
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent"; var imageName = getParameterByName("image_name");
var eventer = window[eventMethod]; if (imageName) {
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message"; document.getElementById("image_name").value = imageName;
// Listen to message from child window }
eventer(messageEvent,function(e) { document.getElementById("landingForm").submit();
if (e.data === 'done') { };
checkLoggedIn(); },
} ]);
}, false); </script>
}
$scope.start = function() {
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
var stack = getParameterByName('stack');
if (stack) {
document.getElementById('stack').value = stack;
}
var stackName = getParameterByName('stack_name');
if (stackName) {
document.getElementById('stack_name').value = stackName;
}
var imageName = getParameterByName('image_name');
if (imageName) {
document.getElementById('image_name').value = imageName;
}
document.getElementById('landingForm').submit();
}
}]);
</script>
</body> </body>
</html> </html>

View File

@@ -143,7 +143,6 @@ func (p *pwd) SessionClose(s *types.Session) error {
p.setGauges() p.setGauges()
p.event.Emit(event.SESSION_END, s.Id) p.event.Emit(event.SESSION_END, s.Id)
return nil return nil
} }
func (p *pwd) SessionGetSmallestViewPort(sessionId string) types.ViewPort { func (p *pwd) SessionGetSmallestViewPort(sessionId string) types.ViewPort {
@@ -214,6 +213,11 @@ func (p *pwd) SessionDeployStack(s *types.Session) error {
return err return err
} }
if _, err := p.dockerFactory.GetForInstance(i); err != nil {
log.Printf("error retrieving docker client for new instance %v", err)
return err
}
code, err := dockerClient.ExecAttach(i.Name, []string{"sh", "-c", cmd}, &w) code, err := dockerClient.ExecAttach(i.Name, []string{"sh", "-c", cmd}, &w)
if err != nil { if err != nil {
log.Printf("Error executing stack [%s]: %s\n", s.Stack, err) log.Printf("Error executing stack [%s]: %s\n", s.Stack, err)
@@ -233,7 +237,6 @@ func (p *pwd) SessionGet(sessionId string) (*types.Session, error) {
defer observeAction("SessionGet", time.Now()) defer observeAction("SessionGet", time.Now())
s, err := p.storage.SessionGet(sessionId) s, err := p.storage.SessionGet(sessionId)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return nil, err return nil, err