yelp / pgctl Goto Github PK
View Code? Open in Web Editor NEWManage sets of developer services -- "playground control"
Home Page: http://pgctl.rtfd.org
License: MIT License
Manage sets of developer services -- "playground control"
Home Page: http://pgctl.rtfd.org
License: MIT License
If we added just a little color to the output, it would be much more readable.
colorama seems to be the go-to library for this.
This replaces yelp-mains $PGCONF/environ.sh.
We load the environ.d using envdir: http://skarnet.org/software/s6/s6-envdir.html
"up" is actually an intermediate state.
Any service that stays in this state long has something wrong with it.
I don't think "up" denotes this.
If I see this:
(virtualenv_run)ckuehl@dev4-uswest1cdevc:~/pg/yelp-main$ pgctl status
memcached: down
watch-templates: down
apache: down
haproxy: down
relode: down
Then I expect pgctl stop
to be a no-op. However it isn't in this case:
(virtualenv_run)ckuehl@dev4-uswest1cdevc:~/pg/yelp-main$ pgctl stop
[pgctl] Stopping: memcached, watch-templates, apache, haproxy, relode
[pgctl] Stopped: memcached
[pgctl] Stopped: watch-templates
[pgctl] Stopped: haproxy
[pgctl] Stopped: relode
[pgctl] ERROR: service 'apache' failed to stop after 30 seconds, these runaway processes did not stop:
UID PID PPID PGID SID C STIME TTY STAT TIME CMD
ckuehl 125867 1 82930 82930 0 Aug24 ? S 0:01 apache2 -DFOREGROUND -f /nail/home/ckuehl/.pgconf-ckuehl/apache2/httpd-webapp.conf -k start
ckuehl 125802 1 82930 82930 0 Aug24 ? S 0:02 apache2 -DFOREGROUND -f /nail/home/ckuehl/.pgconf-ckuehl/apache2/httpd-webapp.conf -k start
ckuehl 122718 1 82930 82930 0 Aug24 ? S 0:20 apache2 -DFOREGROUND -f /nail/home/ckuehl/.pgconf-ckuehl/apache2/httpd-webapp.conf -k start
ckuehl 121655 1 82930 82930 0 Aug24 ? S 0:21 apache2 -DFOREGROUND -f /nail/home/ckuehl/.pgconf-ckuehl/apache2/httpd-webapp.conf -k start
ckuehl 121561 1 82930 82930 0 Aug24 ? S 0:22 apache2 -DFOREGROUND -f /nail/home/ckuehl/.pgconf-ckuehl/apache2/httpd-webapp.conf -k start
There are two ways you can fix this:
* temporarily: lsof -t /nail/home/ckuehl/.pgconf-ckuehl/playground/apache | xargs kill -9
* permanently: http://pgctl.readthedocs.org/en/latest/user/quickstart.html#writing-playground-services
==> /nail/home/ckuehl/.pgconf-ckuehl/playground/apache/log <==
[...log of apache trying to start...]
[pgctl]
[pgctl] There might be useful information further up in the log; you can view it by running:
[pgctl] less +G /nail/home/ckuehl/.pgconf-ckuehl/playground/apache/log
[pgctl] ERROR: Some services failed to stop: apache
The current belief is that this happens because I ran pgctl start
successfully in the past, then later the service dies and repeatedly tries to come back up, and while it's trying to start, the status is down
and pgctl stop
fails.
I think we need two new states: starting
and stopping
. Alternatively, desired state: ready
, but I think that is less obvious.
An invariant that we should have:
pgctl stop
a down
service is a no-oppgctl start
an up
or ready
service is a no-opThis one seems useful, there's about 6 places that this is violated in pgctl (and I don't necessarily understand them all)
closes #61
It appears to always fail, and sends me emails about it failing.
when I ctrl^c the debug process I get:
^C
SIGINT/SIGQUIT received...killing workers...
gateway "uWSGI http 1" has been buried (pid: 4022375)
subprocess 4016589 exited by signal 2
(virtualenv_run)user@localhost:~/pg/project (master) $ subprocess 4016550 exited with code 130
worker 1 buried after 1 seconds
worker 2 buried after 1 seconds
goodbye to uWSGI.
(virtualenv_run)(www_pages/master) rkwills@dev24-devc:~/trees/yelp/www_pages
[Mon 08-17 05:03:17PM]$ cd assets
(virtualenv_run)(www_pages/master) rkwills@dev24-devc:~/trees/yelp/www_pages/assets
[Wed 08-19 09:19:43AM]$ pgctl-2015 start
Starting:
Traceback (most recent call last):
File "/nail/home/rkwills/trees/yelp/www_pages/virtualenv_run/bin/pgctl-2015", line 9, in <module>
load_entry_point('pgctl==0.0.2', 'console_scripts', 'pgctl-2015')()
File "/nail/home/rkwills/trees/yelp/pgctl/pgctl/cli.py", line 259, in main
return app()
File "/nail/home/rkwills/trees/yelp/pgctl/pgctl/cli.py", line 74, in __call__
return command()
File "/nail/home/rkwills/trees/yelp/pgctl/pgctl/cli.py", line 97, in start
return self.__change_state('-u', 'up', 'Starting:', 'Started:')
File "/nail/home/rkwills/trees/yelp/pgctl/pgctl/cli.py", line 79, in __change_state
with self.pgdir.as_cwd():
File "/usr/lib64/python2.7/contextlib.py", line 17, in __enter__
return self.gen.next()
File "/nail/home/rkwills/trees/yelp/www_pages/virtualenv_run/lib/python2.7/site-packages/py/_path/local.py", line 562, in as_cwd
old = self.chdir()
File "/nail/home/rkwills/trees/yelp/www_pages/virtualenv_run/lib/python2.7/site-packages/py/_path/local.py", line 553, in chdir
py.error.checked_call(os.chdir, self.strpath)
File "/nail/home/rkwills/trees/yelp/www_pages/virtualenv_run/lib/python2.7/site-packages/py/_error.py", line 84, in checked_call
raise cls("%s%r" % (func.__name__, args))
py.error.ENOENT: [No such file or directory]: chdir(u'/nail/home/rkwills/trees/yelp/www_pages/assets/playground',)
Naively you end up with output that looks like this if you try and make sub-playgrounds within a pgctl service:
$ pgctl debug scrapers
[pgctl] Stopping: scrapers
[pgctl] Stopped: scrapers
[pgctl] Stopping: scraper_pscrp1, scraper_pscrp2, scraper_pscrp3
[pgctl] Stopped: scraper_pscrp1
[pgctl] Stopped: scraper_pscrp2
[pgctl] Stopped: scraper_pscrp3
Traceback (most recent call last):
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/bin/pgctl", line 11, in <module>
sys.exit(main())
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/pgctl/cli.py", line 436, in main
return app()
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/pgctl/cli.py", line 142, in __call__
result = command()
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/pgctl/cli.py", line 265, in stop
failed = self.__change_state(stop)
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/pgctl/cli.py", line 183, in __change_state
return self.__locked_change_state(state)
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/contextlib2.py", line 414, in __exit__
_reraise_with_existing_context(exc_details)
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/contextlib2.py", line 276, in _reraise_with_existing_context
raise exc_details[1]
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/contextlib2.py", line 403, in __exit__
if cb(*exc_details):
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/contextlib2.py", line 331, in _exit_wrapper
return cm_exit(cm, *exc_details)
File "/usr/lib/python3.5/contextlib.py", line 66, in __exit__
next(self.gen)
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/pgctl/flock.py", line 84, in flock
os.close(fd)
OSError: [Errno 9] Bad file descriptor
[pgctl] Stopping: scraper_pscrp1, scraper_pscrp2, scraper_pscrp3
[pgctl] Stopped: scraper_pscrp1
[pgctl] Stopped: scraper_pscrp2
[pgctl] Stopped: scraper_pscrp3
Traceback (most recent call last):
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/bin/pgctl", line 11, in <module>
sys.exit(main())
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/pgctl/cli.py", line 436, in main
return app()
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/pgctl/cli.py", line 142, in __call__
result = command()
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/pgctl/cli.py", line 265, in stop
failed = self.__change_state(stop)
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/pgctl/cli.py", line 183, in __change_state
return self.__locked_change_state(state)
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/contextlib2.py", line 414, in __exit__
_reraise_with_existing_context(exc_details)
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/contextlib2.py", line 276, in _reraise_with_existing_context
raise exc_details[1]
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/contextlib2.py", line 403, in __exit__
if cb(*exc_details):
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/contextlib2.py", line 331, in _exit_wrapper
return cm_exit(cm, *exc_details)
File "/usr/lib/python3.5/contextlib.py", line 66, in __exit__
next(self.gen)
File "/home/asottile/workspace/PokemonGo-Map-dev/venv/lib/python3.5/site-packages/pgctl/flock.py", line 84, in flock
os.close(fd)
OSError: [Errno 9] Bad file descriptor
Traceback (most recent call last):
File "/home/asottile/workspace/PokemonGo-Map-dev/playground/scrapers/run", line 58, in main
remove_old_scrapers()
File "/home/asottile/workspace/PokemonGo-Map-dev/playground/scrapers/run", line 29, in remove_old_scrapers
subprocess.check_call(('pgctl', 'stop'))
File "/usr/lib/python3.5/subprocess.py", line 581, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '('pgctl', 'stop')' returned non-zero exit status 1
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/asottile/workspace/PokemonGo-Map-dev/playground/scrapers/run", line 68, in <module>
exit(main())
File "/home/asottile/workspace/PokemonGo-Map-dev/playground/scrapers/run", line 64, in main
remove_old_scrapers()
File "/home/asottile/workspace/PokemonGo-Map-dev/playground/scrapers/run", line 29, in remove_old_scrapers
subprocess.check_call(('pgctl', 'stop'))
File "/usr/lib/python3.5/subprocess.py", line 581, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '('pgctl', 'stop')' returned non-zero exit status 1
Here's a workaround:
def no_pgctl_environ():
# We're using pgctl-in-pgctl, clear all pgctl related variables
for k in tuple(os.environ):
if k.startswith('PGCTL_'):
del os.environ[k]
This file is nearly always the same.
We can automatically populate it if/when it doesn't exist.
For whatever reason, my default on trusty is 0002 which threw me for a loop
For example, this error only shows that the service failed to start, but does not show the stack associated with the service. For debugging this is pretty useless and for those less familiar with pgctl, they paste this trace expecting a dev to know how to fix the problem.
15:42:50.184 def check_call(*popenargs, **kwargs):
15:42:50.185 """Run command with arguments. Wait for command to complete. If
15:42:50.185 the exit code was zero then return, otherwise raise
15:42:50.186 CalledProcessError. The CalledProcessError object will have the
15:42:50.188 return code in the returncode attribute.
15:42:50.188
15:42:50.189 The arguments are the same as for the call function. Example:
15:42:50.190
15:42:50.190 check_call(["ls", "-l"])
15:42:50.191 """
15:42:50.191 retcode = call(*popenargs, **kwargs)
15:42:50.192 if retcode:
15:42:50.193 cmd = kwargs.get("args")
15:42:50.193 if cmd is None:
15:42:50.194 cmd = popenargs[0]
15:42:50.194 > raise CalledProcessError(retcode, cmd)
15:42:50.195 E subprocess.CalledProcessError: Command '('pgctl', 'start', 'service')' returned non-zero exit status 1
15:42:50.195
15:42:50.196 /usr/lib/python3.5/subprocess.py:584: CalledProcessError
pre-commit cmd_output
seems to have a nice way to give a better printout, but all of the extra code to support windows seems unnecessary for our use case.
$ pgctl-poll-ready --help
Traceback (most recent call last):
File "/nail/home/buck/pg/master/virtualenv_run/bin/pgctl-poll-ready", line 9, in <module>
load_entry_point('pgctl==1.4.0rc1', 'console_scripts', 'pgctl-poll-ready')()
File "/nail/home/buck/pg/master/virtualenv_run/lib/python2.7/site-packages/pgctl/poll_ready.py", line 76, in main
notification_fd = int(floatfile('notification-fd'))
File "/nail/home/buck/pg/master/virtualenv_run/lib/python2.7/site-packages/pgctl/poll_ready.py", line 19, in floatfile
with open(filename) as f:
IOError: [Errno 2] No such file or directory: u'notification-fd'
easiest fix: chmod -R g-w .
Suggested content
git checkout upstream/master -b {{branch}}
git commit -m "{{version}}"
git tag {{version}}
git push --tags
(push tags to upstream)git push origin HEAD
Currently if we change the design between versions, the old and new pgctl don't cooperate in any natural way. Since pgctl has some state, the playground can get into a completely weird irrecoverable state.
We need to figure out a strategy that allows pgctl to make major design changes between versions without harming users.
Possibly, we should record the version and do a deep-clean when we see the version has changed.
We shouldn't be testing the standard library; and this particular instance makes maintenance more challenging.
We can probably find a way to test that the help is being displayed without asserting the entire body of it.
For details see: b994b65
While someone should not put pgctl-poll-ready in the ready script. We should prevent pgctl from recursively spawning processes if this mistake were to happen. This could be solved through locking the poll-ready script.
Severity: High, machine becomes inoperable due to too many processes, cause for system reboot
Likelihood: Low
I am not quite sure how I got into this state, but if I follow the lsof | xargs kill
instructions, I'll kill tmux without thinking too much about it, which might be really disruptive.
I was able to fix this by just killing the two s6 processes. Maybe there's a better command we can suggest? Unsure.
(virtualenv_run)ckuehl@dev4-uswest1cdevc:~/proj/www_pages$ pgctl stop yelp-main
[pgctl] Stopping: yelp-main
[pgctl] ERROR: service 'yelp-main' failed to stop after 40.02 seconds, these runaway processes did not stop:
UID PID PPID PGID SID C STIME TTY STAT TIME CMD
ckuehl 43601 1 14852 124554 0 04:34 pts/5 S 0:00 s6-ftrig-wait event d
ckuehl 43602 43601 14852 124554 0 04:34 pts/5 S 0:00 \_ s6-ftrigrd
ckuehl 122700 1 122700 122700 0 Aug31 ? Ss 2:07 tmux
There are two ways you can fix this:
* temporarily: lsof -t playground/yelp-main | xargs kill -9
* permanently: http://pgctl.readthedocs.org/en/latest/user/quickstart.html#writing-playground-services
==> playground/yelp-main/log <==
[pgctl]
[pgctl] There might be useful information further up in the log; you can view it by running:
[pgctl] less +G playground/yelp-main/log
[pgctl] ERROR: Some services failed to stop: yelp-main
Document the "why" of #38
add to the docs/developer page
http://pgctl.readthedocs.org/en/latest/developer/design.html
We currently use pidfiles to signal services (e.g. we have a service which watches Python files and sends HUP to apache when a file is changed).
pidfiles suck; it'd be great if we could use pgctl to get the PID.
We discussed adding a "kill" or "signal" command to pgctl, but a nice start would just be adding JSON output to pgctl status --json
.
If I have a pgctl.yaml I expect it to error if yaml is not installed. Instead it silently fails.
Check for written file happens before file is actually written. This is due to daemon tools not being able to "check" if a service is running successfully.
$ pgctl-2015 log i-dont-exist
==> playground/i-dont-exist/log <==
And then afterwards:
$ pgctl-2015 start
[pgctl] Starting: i-dont-exist, scraper, server
[pgctl] Started: i-dont-exist
[pgctl] Started: scraper
[pgctl] Started: server
wompwomp
This seems incomprehensible.
$ echo 3 > notification-fd
$ pgctl-poll-ready /dev/null
Traceback (most recent call last):
File "/nail/home/buck/trees/yelp/pgctl/.tox/pgctl/bin/pgctl-poll-ready", line 9, in <module>
load_entry_point('pgctl==1.0.1', 'console_scripts', 'pgctl-poll-ready')()
File "/nail/home/buck/trees/yelp/pgctl/pgctl/poll_ready.py", line 77, in main
exec_(argv[1:]) # never returns
File "/nail/home/buck/trees/yelp/pgctl/pgctl/functions.py", line 23, in exec_
atexit._run_exitfuncs() # pylint:disable=protected-access
File "/nail/home/buck/trees/yelp/pgctl/.tox/pgctl/lib/python2.7/os.py", line 353, in execvpe
_execvpe(file, args, env)
File "/nail/home/buck/trees/yelp/pgctl/.tox/pgctl/lib/python2.7/os.py", line 368, in _execvpe
func(file, *argrest)
OSError: [Errno 13] Permission denied
For example:
$ pgctl debug relode
[pgctl] Stopping: relode
[pgctl] Stopped: relode
INFO:relode:base watch is on /nail/home/rkwills/trees/yelp id 1
It obviously started the service named 'relode' in debug mode, but the messages only say that it stopped it.
[pgctl] DEBUG: RAW : up (pid 3714560) 13 seconds, normally down, ready 7 seconds
[pgctl] DEBUG: PARSED: up (pid 3714560) 13 seconds
down file needs to be removed and/or we need to parse better (ensure dir)
Example:
aliases:
default:
- watch-templates
test:
-
Traceback:
[Mon 08-31 11:48:54AM]$ pgctl-2015 start test
Traceback (most recent call last):
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/bin/pgctl-2015", line 11, in <module>
sys.exit(main())
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 261, in main
return app()
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 66, in __call__
return command()
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 89, in start
return self.__change_state('-u', 'up', 'Starting:', 'Started:')
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 70, in __change_state
print(xing, self.service_names_string, file=stderr)
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/cached_property.py", line 26, in __get__
value = obj.__dict__[self.func.__name__] = self.func(obj)
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 221, in service_names_string
return ', '.join(self.service_names)
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/cached_property.py", line 26, in __get__
value = obj.__dict__[self.func.__name__] = self.func(obj)
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 225, in service_names
return tuple([service.name for service in self.services])
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/cached_property.py", line 26, in __get__
value = obj.__dict__[self.func.__name__] = self.func(obj)
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 176, in services
for service_name in self._expand_aliases(alias)
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 161, in service_by_name
path = self.pgdir.join(service_name)
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/py/_path/local.py", line 334, in join
arg = arg.strip(sep)
AttributeError: 'NoneType' object has no attribute 'strip'
Example:
aliases:
default:
- watch-templates
test:
Traceback:
[Mon 08-31 11:48:55AM]$ pgctl-2015 start test
Traceback (most recent call last):
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/bin/pgctl-2015", line 11, in <module>
sys.exit(main())
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 261, in main
return app()
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 66, in __call__
return command()
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 89, in start
return self.__change_state('-u', 'up', 'Starting:', 'Started:')
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 70, in __change_state
print(xing, self.service_names_string, file=stderr)
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/cached_property.py", line 26, in __get__
value = obj.__dict__[self.func.__name__] = self.func(obj)
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 221, in service_names_string
return ', '.join(self.service_names)
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/cached_property.py", line 26, in __get__
value = obj.__dict__[self.func.__name__] = self.func(obj)
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 225, in service_names
return tuple([service.name for service in self.services])
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/cached_property.py", line 26, in __get__
value = obj.__dict__[self.func.__name__] = self.func(obj)
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 176, in services
for service_name in self._expand_aliases(alias)
File "/nail/home/rkwills/pg/yelp-main/virtualenv_run/lib/python2.7/site-packages/pgctl/cli.py", line 195, in _expand_aliases
stack.extend(reversed(aliases[name]))
TypeError: argument to reversed() must be a sequence
Same reason as venv-update which @bukzor reverted:
Please don't do this, this is not expected behavior for changing a directory.
pgctl-2011 is dead!
For each (listed) service, show the various processes involved.
fuser -v playground/myservice
gets pretty close, but doesn't indicate the tree structure.
docs/developer Design of Debug
[pgctl] ERROR: service 'apache' failed to start after 1.2e+02 seconds, its status is up (pid 319066) 0 seconds
CUrrently you have linux
in your setup.py as platforms
.
PyPi shows it as:
Platform: l,i,n,u,x
It should be a list
For example, a malformed haproxy configuration
Only the first will be in the foreground.
This enables a no-argument pgctl debug
to be meaningful in real-world scenarios.
Of coures the documentation / --help will need to be updated to reflect this.
curl request from ready script causes uwsgi to spam stdout during debugging
daemontools does not support "checking" if a service is up
switch to s6
Because you need to "exec" in your bash script.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.